diff --git a/src/main/java/net/floodlightcontroller/core/ControllerId.java b/src/main/java/net/floodlightcontroller/core/ControllerId.java deleted file mode 100644 index 6fd347b234219ae4bee0b36923d5cf3893018b0c..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/ControllerId.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.floodlightcontroller.core; - -import org.sdnplatform.sync.ISyncService; -import org.sdnplatform.sync.internal.config.ClusterConfig; - -import com.google.common.base.Optional; - -/** This class represents a unique id of this controller node. It is derived from - * the node id as returned by {@link ISyncService#getLocalNodeId()}. - * <p> - * Note that the unconfigured Node Id is not supported. Users are encouraged to - * represent an unconfigured Controller Node by {@link Optional#absent()}. - * - * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> - */ -public class ControllerId { - private final short nodeId; - - private ControllerId(short nodeId) { - if(nodeId == ClusterConfig.NODE_ID_UNCONFIGURED) - throw new IllegalArgumentException("nodeId is unconfigured"); - - this.nodeId = nodeId; - } - - public short getNodeId() { - return nodeId; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + nodeId; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ControllerId other = (ControllerId) obj; - if (nodeId != other.nodeId) - return false; - return true; - } - - @Override - public String toString() { - return Short.toString(nodeId); - } - - public static ControllerId of(short nodeId) { - return new ControllerId(nodeId); - } - -} diff --git a/src/main/java/net/floodlightcontroller/core/Deliverable.java b/src/main/java/net/floodlightcontroller/core/Deliverable.java deleted file mode 100644 index c82f31d85bd2ffab1567ac8fee25a926315f77b7..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/Deliverable.java +++ /dev/null @@ -1,38 +0,0 @@ -package net.floodlightcontroller.core; - -/** - * abstracts the 'back side' of a Future that is being listened on, i.e., an - * object that receives a result or an error of the computaton once it is ready. - * A deliverable can accept multiple computation results, indicated by the - * return value of deliver. - * - * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> - * @param <T> - * type of the result of the computation - */ -public interface Deliverable<T> { - public static enum Status { - DONE, - CONTINUE - } - - /** - * deliver the result after a successful computation has completed - * - * @param msg - * result - * @return whether the delivery is complete with this result. - **/ - public void deliver(T msg); - - /** deliver an error result for the computation - * @param cause throwable that describes the error - */ - void deliverError(Throwable cause); - - /** whether or not the deliverable has been completed before. - */ - boolean isDone(); - - boolean cancel(boolean mayInterruptIfRunning); -} diff --git a/src/main/java/net/floodlightcontroller/core/DeliverableListenableFuture.java b/src/main/java/net/floodlightcontroller/core/DeliverableListenableFuture.java deleted file mode 100644 index 1e724ac51819fc6a52d26a649ded028f0bd917cf..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/DeliverableListenableFuture.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.floodlightcontroller.core; - -import com.google.common.util.concurrent.AbstractFuture; - -/** Implementation of a ListenableFuture that provides a Deliverable interface to - * the provider. - * - * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> - * @see Deliverable - * @param <T> - */ -public class DeliverableListenableFuture<T> extends AbstractFuture<T> implements Deliverable<T> { - @Override - public void deliver(final T result) { - set(result); - } - - @Override - public void deliverError(final Throwable cause) { - setException(cause); - } -} diff --git a/src/main/java/net/floodlightcontroller/core/GenTableMap.java b/src/main/java/net/floodlightcontroller/core/GenTableMap.java deleted file mode 100644 index ef32a19796dfc977183fe39cf8f55fdf5049a6cb..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/GenTableMap.java +++ /dev/null @@ -1,136 +0,0 @@ -package net.floodlightcontroller.core; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; - -import org.projectfloodlight.openflow.protocol.OFBsnGentableDescStatsEntry; -import org.projectfloodlight.openflow.protocol.OFBsnGentableDescStatsReply; -import org.projectfloodlight.openflow.protocol.OFBsnGentableDescStatsRequest; -import org.projectfloodlight.openflow.types.GenTableId; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.ImmutableMap; - -/** A registry for GenTables. Initialized during switch handshake, based on a - * {@link OFBsnGentableDescStatsRequest}. The table for a particular switch - * can be retrieved via {@link IOFSwitch#getGenTableMap()}. - * - * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> - */ -@Immutable -public class GenTableMap { - private static final Logger logger = LoggerFactory.getLogger(GenTableMap.class); - - private final Map<GenTableId, OFBsnGentableDescStatsEntry> idMap; - private final Map<String, OFBsnGentableDescStatsEntry> nameMap; - - // an empty gentable map - private GenTableMap() { - idMap = ImmutableMap.of(); - nameMap = ImmutableMap.of(); - } - - public GenTableMap(Iterable<OFBsnGentableDescStatsEntry> entries) { - // note: not using an ImmutableMap.Builder here, because we need to - // check for duplicates - Map<GenTableId, OFBsnGentableDescStatsEntry> idBuildMap = new LinkedHashMap<>(); - Map<String, OFBsnGentableDescStatsEntry> nameBuildMap = new LinkedHashMap<>(); - - for (OFBsnGentableDescStatsEntry e : entries) { - GenTableId id = e.getTableId(); - String name = e.getName(); - if (idBuildMap.containsKey(id)) { - logger.warn("Duplicate table id " + id + " - entry {} present. Ignoring new entry {}", - idBuildMap.get(id), e); - continue; - } - if (nameBuildMap.containsKey(name)) { - logger.warn( - "Duplicate table name " + name + " - entry named {} present. Ignoring new entry {}", - nameBuildMap.get(name), e); - continue; - } - idBuildMap.put(id, e); - nameBuildMap.put(name, e); - } - this.idMap = ImmutableMap.copyOf(idBuildMap); - this.nameMap = ImmutableMap.copyOf(nameBuildMap); - } - - public Set<GenTableId> getIds() { - return idMap.keySet(); - } - - public Set<String> getNames() { - return nameMap.keySet(); - } - - public Collection<OFBsnGentableDescStatsEntry> getEntries() { - return idMap.values(); - } - - public boolean hasEntry(GenTableId id) { - return idMap.containsKey(id); - } - - /** retrieve a GenTable Description from the map by id. - * - * @param id - * @return the retrieved gen table description. - * @throws GenTableNotFoundException if no gentable with the given id was found in the map - */ - @Nonnull - public OFBsnGentableDescStatsEntry getEntry(GenTableId id) throws GenTableNotFoundException { - OFBsnGentableDescStatsEntry entry = idMap.get(id); - if(entry == null) - throw new GenTableNotFoundException(id, idMap.values()); - return entry; - } - - public boolean hasEntry(String name) { - return nameMap.containsKey(name); - } - - /** retrieve a GenTable Description from the map by name. - * - * @param name - * @return the retrieved gen table description. - * @throws GenTableNotFoundException if no gentable with the given name was found in the map - */ - @Nonnull - public OFBsnGentableDescStatsEntry getEntry(String name) throws GenTableNotFoundException { - OFBsnGentableDescStatsEntry entry = nameMap.get(name); - if(entry == null) - throw new GenTableNotFoundException(name, nameMap.values()); - return entry; - } - - public final static GenTableMap empty() { - return new GenTableMap(); - } - - public static GenTableMap of(Iterable<OFBsnGentableDescStatsReply> replies) { - List<OFBsnGentableDescStatsEntry> allEntries = new ArrayList<>(); - for(OFBsnGentableDescStatsReply reply: replies) { - allEntries.addAll(reply.getEntries()); - } - return new GenTableMap(allEntries); - } - - @Override - public String toString() { - return idMap.toString(); - } - - public int size() { - return idMap.size(); - } -} diff --git a/src/main/java/net/floodlightcontroller/core/GenTableNotFoundException.java b/src/main/java/net/floodlightcontroller/core/GenTableNotFoundException.java deleted file mode 100644 index 544fc1e8488a0fa33ee934be31d3d6e03ce64ca2..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/GenTableNotFoundException.java +++ /dev/null @@ -1,44 +0,0 @@ -package net.floodlightcontroller.core; - -import org.projectfloodlight.openflow.protocol.OFBsnGentableDescStatsEntry; -import org.projectfloodlight.openflow.types.GenTableId; - -/** A GenTable was not found in the {@link GenTableMap}. - * - * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> - */ -public class GenTableNotFoundException extends RuntimeException { - private static final long serialVersionUID = 1L; - - public GenTableNotFoundException(String name, Iterable<OFBsnGentableDescStatsEntry> available) { - super(getMessageForName(name, available)); - } - - private static String getMessageForName(String name, - Iterable<OFBsnGentableDescStatsEntry> available) { - return String.format("Table not found: %s (available tables: %s)", name, availableDescr(available)); - } - - public GenTableNotFoundException(GenTableId id, Iterable<OFBsnGentableDescStatsEntry> available) { - super(getMessageForId(id, available)); - } - - private static String getMessageForId(GenTableId id, - Iterable<OFBsnGentableDescStatsEntry> available) { - return String.format("Table not found: %s (available tables: %s)", id, availableDescr(available)); - } - - private static String availableDescr(Iterable<OFBsnGentableDescStatsEntry> available) { - StringBuilder b = new StringBuilder(); - boolean first = true; - for(OFBsnGentableDescStatsEntry e: available) { - if(!first) - b.append(", "); - first = false; - b.append(e.getName()).append("=").append(e.getTableId()); - } - return b.toString(); - } - - -} diff --git a/src/main/java/net/floodlightcontroller/core/HARole.java b/src/main/java/net/floodlightcontroller/core/HARole.java deleted file mode 100644 index 0c2886f1e179579fc0b011317cdf8362a95cab9f..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/HARole.java +++ /dev/null @@ -1,88 +0,0 @@ -package net.floodlightcontroller.core; - -import org.projectfloodlight.openflow.protocol.OFControllerRole; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * HARole describes the role that a given controller node currently plays in the - * management of the SDN network. Controller nodes can be either - * <ul> - * <li>ACTIVE - currently controlling the network - * <li>STANDBY - standing by in case of a fail-over from the ACTIVE node - * </ul> - * At any given time there SHOULD be at most one ACTIVE node in the network - * (this invariant cannot be strictly guranteed for certain split-brain - * situtations). There can be multiple STANDBY controllers. There are other - * HA-related roles in the system. Try to not confuse them. - * <ul> - * <li>On the cluster management/bigsync layer {@link ISyncService} determines a - * DOMAIN LEADER / DOMAIN FOLLOWER (which are exposed via - * <li>On the OF layer, switch connections can be in either MASTER, SLAVE or - * EQUAL {@link Role} (exposed by {@link IOFSwitchListener}). - * </ul> - * Most applications and modules trying to decide something on the ACTIVE node - * should base that decision on the HARole. - * - * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> - */ -public enum HARole { - /** This controller node is currently actively managing the SDN network. At any given - * time, there should be at most one ACTIVE node. When the ACTIVE node fails, a STANDBY - * node is determined to become ACTIVE. - */ - ACTIVE(OFControllerRole.ROLE_MASTER), - - /** This controller node is currently standing by and not managing the networking. There - * may be more than one STANDBY nodes in the network. - */ - STANDBY(OFControllerRole.ROLE_SLAVE); - - private static final Logger logger = LoggerFactory.getLogger(HARole.class); - private final OFControllerRole ofRole; - - HARole(OFControllerRole ofRole) { - this.ofRole = ofRole; - } - - /** a backwards-compatible {@link #valueOf} that accepts the old terms "MASTER" and "SLAVE" - * and normalizes them to ACTIVE and STANDBY. - * - * @param roleString - * @return an HARole - * @throws IllegalArgumentException - if no such role can be found. - */ - public static HARole valueOfBackwardsCompatible(String roleString) throws IllegalArgumentException { - roleString = roleString.trim().toUpperCase(); - if("MASTER".equals(roleString)) { - logger.warn("got legacy role name MASTER - normalized to ACTIVE", roleString); - if(logger.isDebugEnabled()) { - logger.debug("Legacy role call stack", new IllegalArgumentException()); - } - roleString = "ACTIVE"; - } else if ("SLAVE".equals(roleString)) { - logger.warn("got legacy role name SLAVE - normalized to STANDBY", roleString); - if(logger.isDebugEnabled()) { - logger.debug("Legacy role call stack", new IllegalArgumentException()); - } - roleString = "STANDBY"; - } - return valueOf(roleString); - } - - public OFControllerRole getOFRole() { - return ofRole; - } - - public static HARole ofOFRole(OFControllerRole role) { - switch(role) { - case ROLE_MASTER: - case ROLE_EQUAL: - return ACTIVE; - case ROLE_SLAVE: - return STANDBY; - default: - throw new IllegalArgumentException("Unmappable controller role: " + role); - } - } -} diff --git a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java index ce3e1786f90660064f7cc0e8e0ed6b634670ce5d..c0aa1fd6d5faa9426c7580d7e02fc685c0a96021 100644 --- a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java +++ b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java @@ -17,6 +17,8 @@ package net.floodlightcontroller.core; +import java.util.HashMap; + import java.util.List; import java.util.Set; import java.util.Map; @@ -24,22 +26,11 @@ import java.util.Map; import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.packet.Ethernet; -import org.jboss.netty.util.Timer; - -import net.floodlightcontroller.core.FloodlightContext; -import net.floodlightcontroller.core.HARole; -import net.floodlightcontroller.core.IHAListener; -import net.floodlightcontroller.core.IInfoProvider; -import net.floodlightcontroller.core.IOFMessageListener; -import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.RoleInfo; -import net.floodlightcontroller.core.internal.RoleManager; -import net.floodlightcontroller.core.internal.Controller.IUpdate; -import net.floodlightcontroller.core.internal.Controller.ModuleLoaderState; - -import net.floodlightcontroller.core.FloodlightContextStore; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFType; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFType; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.vendor.nicira.OFRoleVendorData; + /** * The interface exposed by the core bundle that allows you to interact * with connected switches. @@ -54,7 +45,38 @@ public interface IFloodlightProviderService extends * representation of the payload of a packet-in message. */ public static final String CONTEXT_PI_PAYLOAD = - "org.projectfloodlight.core.IFloodlightProvider.piPayload"; + "net.floodlightcontroller.core.IFloodlightProvider.piPayload"; + + /** + * The role of the controller as used by the OF 1.2 and OVS failover and + * load-balancing mechanism. + */ + public static enum Role { + EQUAL(OFRoleVendorData.NX_ROLE_OTHER), + MASTER(OFRoleVendorData.NX_ROLE_MASTER), + SLAVE(OFRoleVendorData.NX_ROLE_SLAVE); + + private final int nxRole; + + private Role(int nxRole) { + this.nxRole = nxRole; + } + + private static Map<Integer,Role> nxRoleToEnum + = new HashMap<Integer,Role>(); + static { + for(Role r: Role.values()) + nxRoleToEnum.put(r.toNxRole(), r); + } + public int toNxRole() { + return nxRole; + } + // Return the enum representing the given nxRole or null if no + // such role exists + public static Role fromNxRole(int nxRole) { + return nxRoleToEnum.get(nxRole); + } + }; /** * A FloodlightContextStore object that can be used to retrieve the @@ -63,15 +85,6 @@ public interface IFloodlightProviderService extends public static final FloodlightContextStore<Ethernet> bcStore = new FloodlightContextStore<Ethernet>(); - /** - * Service name used in the service directory representing - * the OpenFlow controller-switch channel - * - * @see ILocalServiceAddressTracker - * @see IClusterServiceAddressDirectory - */ - public static final String SERVICE_DIRECTORY_SERVICE_NAME = "openflow"; - /** * Adds an OpenFlow message listener * @param type The OFType the component wants to listen for @@ -93,52 +106,77 @@ public interface IFloodlightProviderService extends public Map<OFType, List<IOFMessageListener>> getListeners(); /** - * Get the current role of the controller + * If the switch with the given DPID is known to any controller in the + * cluster, this method returns the associated IOFSwitch instance. As such + * the returned switches not necessarily connected or in master role for + * the local controller. + * + * Multiple calls to this method with the same DPID may return different + * IOFSwitch references. A caller must not store or otherwise rely on + * IOFSwitch references to be constant over the lifecycle of a switch. + * + * @param dpid the dpid of the switch to query + * @return the IOFSwitch instance associated with the dpid, null if no + * switch with the dpid is known to the cluster */ - public HARole getRole(); + public IOFSwitch getSwitch(long dpid); /** - * Get the current role of the controller + * Returns a snapshot of the set DPIDs for all known switches. + * + * The returned set is owned by the caller: the caller can modify it at + * will and changes to the known switches are not reflected in the returned + * set. The caller needs to call getAllSwitchDpids() if an updated + * version is needed. + * + * See {@link #getSwitch(long)} for what "known" switch is. + * @return the set of DPIDs of all known switches */ - public RoleInfo getRoleInfo(); + public Set<Long> getAllSwitchDpids(); /** - * Get the current mapping of controller IDs to their IP addresses - * Returns a copy of the current mapping. - * @see IHAListener + * Return a snapshot + * FIXME: asdf + * @return */ - public Map<String,String> getControllerNodeIPs(); + public Map<Long,IOFSwitch> getAllSwitchMap(); /** - * Gets the ID of the controller + * Get the current role of the controller */ - public String getControllerId(); + public Role getRole(); /** - * Gets the controller hostname - * @return the controller hostname + * Get the current role of the controller */ - public String getOFHostname(); + public RoleInfo getRoleInfo(); /** - * Gets the controller's openflow port - * @return the controller's openflow port + * Get the current mapping of controller IDs to their IP addresses + * Returns a copy of the current mapping. + * @see IHAListener */ - public int getOFPort(); + public Map<String,String> getControllerNodeIPs(); + /** * Set the role of the controller * @param role The new role for the controller node * @param changeDescription The reason or other information for this role change */ - public void setRole(HARole role, String changeDescription); + public void setRole(Role role, String changeDescription); /** - * Add an update task for asynchronous, serialized execution - * - * @param update + * Add a switch listener + * @param listener The module that wants to listen for events + */ + public void addOFSwitchListener(IOFSwitchListener listener); + + /** + * Remove a switch listener + * @param listener The The module that no longer wants to listen for events */ - public void addUpdateToQueue(IUpdate update); + public void removeOFSwitchListener(IOFSwitchListener listener); /** * Adds a listener for HA role events @@ -152,13 +190,53 @@ public interface IFloodlightProviderService extends */ public void removeHAListener(IHAListener listener); + /** + * Add a listener for ready-for-flow-reconcile events + * @param l + */ + public void addReadyForReconcileListener(IReadyForReconcileListener l); + + /** + * Terminate the process + */ + public void terminate(); + + /** + * Re-injects an OFMessage back into the packet processing chain + * @param sw The switch to use for the message + * @param msg the message to inject + * @return True if successfully re-injected, false otherwise + * @throws NullPointerException if switch or msg is null + */ + public boolean injectOfMessage(IOFSwitch sw, OFMessage msg); + + /** + * Re-injects an OFMessage back into the packet processing chain + * @param sw The switch to use for the message + * @param msg the message to inject + * @param bContext a floodlight context to use if required. Can be null + * @return True if successfully re-injected, false otherwise + * @throws NullPointerException if switch or msg is null + */ + public boolean injectOfMessage(IOFSwitch sw, OFMessage msg, + FloodlightContext bContext); + /** * Process written messages through the message listeners for the controller * @param sw The switch being written to * @param m the message + * @param bc any accompanying context object. Can be null in which case a + * new context will be allocated and passed to listeners * @throws NullPointerException if switch or msg is null */ - public void handleOutgoingMessage(IOFSwitch sw, OFMessage m); + public void handleOutgoingMessage(IOFSwitch sw, OFMessage m, + FloodlightContext bc); + + /** + * Gets the BasicFactory + * @return an OpenFlow message factory + */ + public BasicFactory getOFMessageFactory(); /** * Run the main I/O loop of the Controller. @@ -187,12 +265,21 @@ public interface IFloodlightProviderService extends */ public Map<String, Object> getControllerInfo(String type); + /** * Return the controller start time in milliseconds * @return */ public long getSystemStartTime(); + /** + * Configure controller to always clear the flow table on the switch, + * when it connects to controller. This will be true for first time switch + * reconnect, as well as a switch re-attaching to Controller after HA + * switch over to ACTIVE role + */ + public void setAlwaysClearFlowsOnSwActivate(boolean value); + /** * Get controller memory information */ @@ -205,38 +292,30 @@ public interface IFloodlightProviderService extends public Long getUptime(); /** - * Get the set of port prefixes that will define an UPLINK port. - * @return The set of prefixes + * Adds an OFSwitch driver + * @param manufacturerDescriptionPrefix Register the given prefix + * with the driver. + * @param driver A IOFSwitchDriver instance to handle IOFSwitch instaniation + * for the given manufacturer description prefix + * @throws IllegalStateException If the the manufacturer description is + * already registered + * @throws NullPointerExeption if manufacturerDescriptionPrefix is null + * @throws NullPointerExeption if driver is null */ - public Set<String> getUplinkPortPrefixSet(); - - - public void handleMessage(IOFSwitch sw, OFMessage m, - FloodlightContext bContext); + public void addOFSwitchDriver(String desc, IOFSwitchDriver driver); /** - * Gets a hash wheeled timer to be used for for timeout scheduling - * @return a hash wheeled timer + * Record a switch event in in-memory debug-event + * @param switchDPID + * @param reason Reason for this event + * @param flushNow see debug-event flushing in IDebugEventService */ - public Timer getTimer(); + public void addSwitchEvent(long switchDPID, String reason, boolean flushNow); /** - * Gets the role manager - * @return the role manager - */ - public RoleManager getRoleManager(); - - /** - * Gets the current module loading state. - * @return the current module loading state. - */ - ModuleLoaderState getModuleLoaderState(); - - /** - * Gets the current number of worker threads - * @return Used for netty setup + * Get the set of port prefixes that will define an UPLINK port. + * @return The set of prefixes */ - public int getWorkerThreads(); + public Set<String> getUplinkPortPrefixSet(); } - diff --git a/src/main/java/net/floodlightcontroller/core/IHAListener.java b/src/main/java/net/floodlightcontroller/core/IHAListener.java new file mode 100644 index 0000000000000000000000000000000000000000..2ffe82fd586b1012f29c24d397eb6c212a5aa02e --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/IHAListener.java @@ -0,0 +1,45 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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 net.floodlightcontroller.core; + +import java.util.Map; + +public interface IHAListener extends IListener<HAListenerTypeMarker> { + + /** + * This notification is fired if the controller's initial role was SLAVE + * and the controller is now transitioning to MASTER. + * Modules need to read their initial role in startUp from floodlight + * provider. + */ + public void transitionToMaster(); + + /** + * Gets called when the IP addresses of the controller nodes in the + * controller cluster change. All parameters map controller ID to + * the controller's IP. + * + * @param curControllerNodeIPs The current mapping of controller IDs to IP + * @param addedControllerNodeIPs These IPs were added since the last update + * @param removedControllerNodeIPs These IPs were removed since the last update + */ + public void controllerNodeIPsChanged( + Map<String, String> curControllerNodeIPs, + Map<String, String> addedControllerNodeIPs, + Map<String, String> removedControllerNodeIPs + ); +} diff --git a/src/main/java/net/floodlightcontroller/core/IOFConnection.java b/src/main/java/net/floodlightcontroller/core/IOFConnection.java deleted file mode 100644 index a14021fbd47b087495b57bdd3b1736966df0a798..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/IOFConnection.java +++ /dev/null @@ -1,62 +0,0 @@ -package net.floodlightcontroller.core; - -import java.net.SocketAddress; - -import java.util.Date; -import org.projectfloodlight.openflow.protocol.OFFactory; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFAuxId; - - -/** Contract for an openflow connection to a switch. - * Provides message write and request/response handling capabilities. - * - * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> - */ -public interface IOFConnection extends IOFMessageWriter { - - /** - * Retrieves the date the connection connected to this controller - * @return the date - */ - Date getConnectedSince(); - - /** - * Flush all flows queued for this switch in the current thread. - * NOTE: The contract is limited to the current thread - */ - void flush(); - - /** @return the DatapathId of the switch associated with the connection */ - DatapathId getDatapathId(); - - /** @return the OFAuxId of the this connection */ - OFAuxId getAuxId(); - - /** - * Get the IP address of the remote (switch) end of the connection - * @return the inet address - */ - SocketAddress getRemoteInetAddress(); - - /** - * Get the IP address of the local end of the connection - * - * @return the inet address - */ - SocketAddress getLocalInetAddress(); - - /** - * Get's the OFFactory that this connection was constructed with. - * This is the factory that was found in the features reply during - * the channel handshake - * @return The connection's OFFactory - */ - OFFactory getOFFactory(); - - /** @return whether this connection is currently (still) connected to the controller. - */ - boolean isConnected(); - - -} diff --git a/src/main/java/net/floodlightcontroller/core/IOFConnectionBackend.java b/src/main/java/net/floodlightcontroller/core/IOFConnectionBackend.java deleted file mode 100644 index 0ec7a9e1b2b081b34c813d5d9dd959028e1e49cc..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/IOFConnectionBackend.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.floodlightcontroller.core; - -import net.floodlightcontroller.core.internal.IOFConnectionListener; - -public interface IOFConnectionBackend extends IOFConnection { - /** - * Disconnect the channel - */ - void disconnect(); - - /** - * Cancel all pending request - */ - void cancelAllPendingRequests(); - - /** @return whether the output stream associated with this connection - * is currently writeable (for throttling) - */ - boolean isWritable(); - - /** set the message/closing listener for this connection */ - void setListener(IOFConnectionListener listener); -} diff --git a/src/main/java/net/floodlightcontroller/core/IOFMessageListener.java b/src/main/java/net/floodlightcontroller/core/IOFMessageListener.java index 1fe71529c9e64d364c92a0d2255083169a737dc8..00fdac1f99379ee8aa1e5dfee00de6bd4da9a4f1 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFMessageListener.java +++ b/src/main/java/net/floodlightcontroller/core/IOFMessageListener.java @@ -17,8 +17,8 @@ package net.floodlightcontroller.core; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFType; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFType; /** * diff --git a/src/main/java/net/floodlightcontroller/core/IOFMessageWriter.java b/src/main/java/net/floodlightcontroller/core/IOFMessageWriter.java deleted file mode 100644 index bd27181f19e9170fac71db1557942dcc8cef0510..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/IOFMessageWriter.java +++ /dev/null @@ -1,79 +0,0 @@ -/** -* Copyright 2011, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* 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 net.floodlightcontroller.core; - -import java.util.List; - -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFRequest; -import org.projectfloodlight.openflow.protocol.OFStatsReply; -import org.projectfloodlight.openflow.protocol.OFStatsRequest; - -import com.google.common.util.concurrent.ListenableFuture; - -/** - * An interface to describe classes that write OF messages. - * E.g. IOFSwitch, IOFConnection - */ - -public interface IOFMessageWriter{ - - /** - * Writes to the OFMessage to the output stream. - * - * <p><b>Note:</b> this method has fire-and-forget semantics. When the connection is - * not currently connected, it will silently discard the messages. - * - * @param m - */ - void write(OFMessage m); - - /** - * Writes the list of messages to the output stream. - * - * <p><b>Note:</b> this method has fire-and-forget semantics. When the connection is - * not currently connected, it will silently discard the messages. - * - * @param msglist - */ - void write(Iterable<OFMessage> msglist); - - /** write an OpenFlow Request message, register for a single corresponding reply message - * or error message. - * - * @param request - * @return a Future object that can be used to retrieve the asynchrounous - * response when available. - * - * If the connection is not currently connected, will - * return a Future that immediately fails with a @link{SwitchDisconnectedException}. - */ - <R extends OFMessage> ListenableFuture<R> writeRequest(OFRequest<R> request); - - /** write a Stats (Multipart-) request, register for all corresponding reply messages. - * Returns a Future object that can be used to retrieve the List of asynchronous - * OFStatsReply messages when it is available. - * - * @param request stats request - * @return Future object wrapping OFStatsReply - * If the connection is not currently connected, will - * return a Future that immediately fails with a @link{SwitchDisconnectedException}. - */ - <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> writeStatsRequest( - OFStatsRequest<REPLY> request); -} diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java new file mode 100644 index 0000000000000000000000000000000000000000..fb27460401007cdc639d4c8ee38cc73101ce0c40 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java @@ -0,0 +1,669 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* 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 net.floodlightcontroller.core; + +import java.io.IOException; +import java.net.SocketAddress; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Future; +import net.floodlightcontroller.core.IFloodlightProviderService.Role; +import net.floodlightcontroller.core.internal.Controller; +import net.floodlightcontroller.debugcounter.IDebugCounterService; +import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException; +import net.floodlightcontroller.threadpool.IThreadPoolService; +import net.floodlightcontroller.util.OrderedCollection; + +import org.jboss.netty.channel.Channel; +import org.openflow.protocol.OFFeaturesReply; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPortStatus; +import org.openflow.protocol.OFStatisticsReply; +import org.openflow.protocol.OFStatisticsRequest; +import org.openflow.protocol.statistics.OFDescriptionStatistics; +import org.openflow.protocol.statistics.OFStatistics; + +/** + * + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public interface IOFSwitch { + // Attribute keys + public static final String SWITCH_DESCRIPTION_FUTURE = "DescriptionFuture"; + public static final String SWITCH_SUPPORTS_NX_ROLE = "supportsNxRole"; + public static final String SWITCH_IS_CORE_SWITCH = "isCoreSwitch"; + public static final String PROP_FASTWILDCARDS = "FastWildcards"; + public static final String PROP_REQUIRES_L3_MATCH = "requiresL3Match"; + public static final String PROP_SUPPORTS_OFPP_TABLE = "supportsOfppTable"; + public static final String PROP_SUPPORTS_OFPP_FLOOD = "supportsOfppFlood"; + public static final String PROP_SUPPORTS_NETMASK_TBL = "supportsNetmaskTbl"; + + public enum OFPortType { + NORMAL("normal"), // normal port (default) + TUNNEL("tunnel"), // tunnel port + UPLINK("uplink"), // uplink port (on a virtual switch) + MANAGEMENT("management"), // for in-band management + TUNNEL_LOOPBACK("tunnel-loopback"); + + private final String value; + OFPortType(String v) { + value = v; + } + + @Override + public String toString() { + return value; + } + + public static OFPortType fromString(String str) { + for (OFPortType m : OFPortType.values()) { + if (m.value.equals(str)) { + return m; + } + } + return OFPortType.NORMAL; + } + } + + /** + * Describes a change of an open flow port + */ + public static class PortChangeEvent { + public final ImmutablePort port; + public final PortChangeType type; + /** + * @param port + * @param type + */ + public PortChangeEvent(ImmutablePort port, + PortChangeType type) { + this.port = port; + this.type = type; + } + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((port == null) ? 0 : port.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + PortChangeEvent other = (PortChangeEvent) obj; + if (port == null) { + if (other.port != null) return false; + } else if (!port.equals(other.port)) return false; + if (type != other.type) return false; + return true; + } + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "[" + type + " " + port.toBriefString() + "]"; + } + } + + /** + * the type of change that happened to an open flow port + */ + public enum PortChangeType { + ADD, OTHER_UPDATE, DELETE, UP, DOWN, + } + + /** + * Set IFloodlightProviderService for this switch instance + * Called immediately after instantiation + * + * @param controller + */ + public void setFloodlightProvider(Controller controller); + + /** + * Set IThreadPoolService for this switch instance + * Called immediately after instantiation + * + * @param threadPool + */ + public void setThreadPoolService(IThreadPoolService threadPool); + + /** + * Set debug counter service for per-switch counters + * Called immediately after instantiation + * @param debugCounters + * @throws CounterException + */ + public void setDebugCounterService(IDebugCounterService debugCounters) + throws CounterException; + + /** + * Set the netty Channel this switch instance is associated with + * Called immediately after instantiation + * + * @param channel + */ + public void setChannel(Channel channel); + + /** + * Called when OFMessage enters pipeline. Returning true cause the message + * to be dropped. + * @param ofm + * @return + */ + public boolean inputThrottled(OFMessage ofm); + + /** + * Return if the switch is currently overloaded. The definition of + * overload refers to excessive traffic in the control path, namely + * a high packet in rate. + * @return + */ + boolean isOverloaded(); + + /** + * Write OFMessage to the output stream, subject to switch rate limiting. + * The message will be handed to the floodlightProvider for possible filtering + * and processing by message listeners + * @param msg + * @param cntx + * @throws IOException + */ + public void writeThrottled(OFMessage msg, FloodlightContext cntx) throws IOException; + + /** + * Writes the list of messages to the output stream, subject to rate limiting. + * The message will be handed to the floodlightProvider for possible filtering + * and processing by message listeners. + * @param msglist + * @param bc + * @throws IOException + */ + void writeThrottled(List<OFMessage> msglist, FloodlightContext bc) + throws IOException; + + /** + * Writes to the OFMessage to the output stream, bypassing rate limiting. + * The message will be handed to the floodlightProvider for possible filtering + * and processing by message listeners + * @param m + * @param bc + * @throws IOException + */ + public void write(OFMessage m, FloodlightContext bc) throws IOException; + + /** + * Writes the list of messages to the output stream, bypassing rate limiting. + * The message will be handed to the floodlightProvider for possible filtering + * and processing by message listeners. + * @param msglist + * @param bc + * @throws IOException + */ + public void write(List<OFMessage> msglist, FloodlightContext bc) throws IOException; + + /** + * + * @throws IOException + */ + public void disconnectOutputStream(); + + /** + * Returns switch features from features Reply + * @return + */ + public int getBuffers(); + + public int getActions(); + + public int getCapabilities(); + + public byte getTables(); + + /** + * @return a copy of the description statistics for this switch + */ + public OFDescriptionStatistics getDescriptionStatistics(); + + /** + * Set the OFFeaturesReply message returned by the switch during initial + * handshake. + * @param featuresReply + */ + public void setFeaturesReply(OFFeaturesReply featuresReply); + + /** + * Get list of all enabled ports. This will typically be different from + * the list of ports in the OFFeaturesReply, since that one is a static + * snapshot of the ports at the time the switch connected to the controller + * whereas this port list also reflects the port status messages that have + * been received. + * @return Unmodifiable list of ports not backed by the underlying collection + */ + public Collection<ImmutablePort> getEnabledPorts(); + + /** + * Get list of the port numbers of all enabled ports. This will typically + * be different from the list of ports in the OFFeaturesReply, since that + * one is a static snapshot of the ports at the time the switch connected + * to the controller whereas this port list also reflects the port status + * messages that have been received. + * @return Unmodifiable list of ports not backed by the underlying collection + */ + public Collection<Short> getEnabledPortNumbers(); + + /** + * Retrieve the port object by the port number. The port object + * is the one that reflects the port status updates that have been + * received, not the one from the features reply. + * @param portNumber + * @return port object + */ + public ImmutablePort getPort(short portNumber); + + /** + * Retrieve the port object by the port name. The port object + * is the one that reflects the port status updates that have been + * received, not the one from the features reply. + * Port names are case insentive + * @param portName + * @return port object + */ + public ImmutablePort getPort(String portName); + + /** + * Add or modify a switch port. + * This is called by the core controller + * code in response to a OFPortStatus message. It should not typically be + * called by other floodlight applications. + * + * OFPPR_MODIFY and OFPPR_ADD will be treated as equivalent. The OpenFlow + * spec is not clear on whether portNames are portNumbers are considered + * authoritative identifiers. We treat portNames <-> portNumber mappings + * as fixed. If they change, we delete all previous conflicting ports and + * add all new ports. + * + * @param ps the port status message + * @return the ordered Collection of changes "applied" to the old ports + * of the switch according to the PortStatus message. A single PortStatus + * message can result in multiple changes. + * If portName <-> portNumber mappings have + * changed, the iteration order ensures that delete events for old + * conflicting appear before before events adding new ports + */ + public OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps); + + /** + * Get list of all ports. This will typically be different from + * the list of ports in the OFFeaturesReply, since that one is a static + * snapshot of the ports at the time the switch connected to the controller + * whereas this port list also reflects the port status messages that have + * been received. + * @return Unmodifiable list of ports + */ + public Collection<ImmutablePort> getPorts(); + + /** + * @param portNumber + * @return Whether a port is enabled per latest port status message + * (not configured down nor link down nor in spanning tree blocking state) + */ + public boolean portEnabled(short portNumber); + + /** + * @param portNumber + * @return Whether a port is enabled per latest port status message + * (not configured down nor link down nor in spanning tree blocking state) + */ + public boolean portEnabled(String portName); + + /** + * Compute the changes that would be required to replace the old ports + * of this switch with the new ports + * @param ports new ports to set + * @return the ordered collection of changes "applied" to the old ports + * of the switch in order to set them to the new set. + * If portName <-> portNumber mappings have + * changed, the iteration order ensures that delete events for old + * conflicting appear before before events adding new ports + */ + public OrderedCollection<PortChangeEvent> + comparePorts(Collection<ImmutablePort> ports); + + /** + * Replace the ports of this switch with the given ports. + * @param ports new ports to set + * @return the ordered collection of changes "applied" to the old ports + * of the switch in order to set them to the new set. + * If portName <-> portNumber mappings have + * changed, the iteration order ensures that delete events for old + * conflicting appear before before events adding new ports + */ + public OrderedCollection<PortChangeEvent> + setPorts(Collection<ImmutablePort> ports); + + + /** + * Get the datapathId of the switch + * @return + */ + public long getId(); + + /** + * Get a string version of the ID for this switch + * @return + */ + public String getStringId(); + + /** + * Get the IP Address for the switch + * @return the inet address + */ + public SocketAddress getInetAddress(); + + /** + * Retrieves attributes of this switch + * @return + */ + public Map<Object, Object> getAttributes(); + + /** + * Retrieves the date the switch connected to this controller + * @return the date + */ + public Date getConnectedSince(); + + /** + * Returns the next available transaction id + * @return + */ + public int getNextTransactionId(); + + /** + * Returns a Future object that can be used to retrieve the asynchronous + * OFStatisticsReply when it is available. + * + * @param request statistics request + * @return Future object wrapping OFStatisticsReply + * @throws IOException + */ + public Future<List<OFStatistics>> queryStatistics(OFStatisticsRequest request) + + throws IOException; + + /** + * Returns a Future object that can be used to retrieve the asynchronous + * OFStatisticsReply when it is available. + * + * @param request statistics request + * @return Future object wrapping OFStatisticsReply + * @throws IOException + */ + public Future<OFFeaturesReply> querySwitchFeaturesReply() + throws IOException; + + /** + * Deliver the featuresReply future reply + * @param reply the reply to deliver + */ + void deliverOFFeaturesReply(OFMessage reply); + + /* + * Cancel features reply with a specific transction ID + * @param transactionId the transaction ID + */ + public void cancelFeaturesReply(int transactionId); + + /** + * Check if the switch is connected to this controller. Whether a switch + * is connected is independent of whether the switch is active + * @return whether the switch is still disconnected + */ + public boolean isConnected(); + + /** + * Check if the switch is active. I.e., the switch is connected to this + * controller and is in master role + * @return + */ + public boolean isActive(); + + /** + * Set whether the switch is connected + * @param connected whether the switch is connected + */ + public void setConnected(boolean connected); + + /** + * Get the current role of the controller for the switch + * @return the role of the controller + */ + public Role getHARole(); + + /** + * Set switch's HA role to role. The haRoleReplyReceived indicates + * if a reply was received from the switch (error replies excluded). + * + * If role is null, the switch should close the channel connection. + * + * @param role + * @param haRoleReplyReceived + */ + public void setHARole(Role role); + + /** + * Deliver the statistics future reply + * @param reply the reply to deliver + */ + public void deliverStatisticsReply(OFStatisticsReply reply); + + /** + * Cancel the statistics reply with the given transaction ID + * @param transactionId the transaction ID + */ + public void cancelStatisticsReply(int transactionId); + + /** + * Cancel all statistics replies + */ + public void cancelAllStatisticsReplies(); + + /** + * Checks if a specific switch property exists for this switch + * @param name name of property + * @return value for name + */ + boolean hasAttribute(String name); + + /** + * Set properties for switch specific behavior + * @param name name of property + * @return value for name + */ + Object getAttribute(String name); + + /** + * Check if the given attribute is present and if so whether it is equal + * to "other" + * @param name the name of the attribute to check + * @param other the object to compare the attribute against. + * @return true iff the specified attribute is set and equals() the given + * other object. + */ + boolean attributeEquals(String name, Object other); + + /** + * Set properties for switch specific behavior + * @param name name of property + * @param value value for name + */ + void setAttribute(String name, Object value); + + /** + * Set properties for switch specific behavior + * @param name name of property + * @return current value for name or null (if not present) + */ + Object removeAttribute(String name); + + /** + * Clear all flowmods on this switch + */ + public void clearAllFlowMods(); + + /** + * Update broadcast cache + * @param data + * @return true if there is a cache hit + * false if there is no cache hit. + */ + public boolean updateBroadcastCache(Long entry, Short port); + + /** + * Get the portBroadcastCacheHits + * @return + */ + public Map<Short, Long> getPortBroadcastHits(); + + /** + * Send a flow statistics request to the switch. This call returns after + * sending the stats. request to the switch. + * @param request flow statistics request message + * @param xid transaction id, must be obtained by using the getXid() API. + * @param caller the caller of the API. receive() callback of this + * caller would be called when the reply from the switch is received. + * @return the transaction id for the message sent to the switch. The + * transaction id can be used to match the response with the request. Note + * that the transaction id is unique only within the scope of this switch. + * @throws IOException + */ + public void sendStatsQuery(OFStatisticsRequest request, int xid, + IOFMessageListener caller) throws IOException; + + /** + * Flush all flows queued for this switch in the current thread. + * NOTE: The contract is limited to the current thread + */ + public void flush(); + + /*********************************************** + * The following method can be overridden by + * specific types of switches + *********************************************** + */ + + /** + * Set the SwitchProperties based on it's description + * @param description + */ + public void setSwitchProperties(OFDescriptionStatistics description); + + /** + * Return the type of OFPort + * @param port_num + * @return + */ + public OFPortType getPortType(short port_num); + + /** + * Can the port be turned on without forming a new loop? + * @param port_num + * @return + */ + public boolean isFastPort(short port_num); + + /** + * Return whether write throttling is enabled on the switch + */ + public boolean isWriteThrottleEnabled(); + + /** + * 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(); + + /** + * Start this switch driver's sub handshake. This might be a no-op but + * this method must be called at least once for the switch to be become + * ready. + * This method must only be called from the I/O thread + * @throws SwitchDriverSubHandshakeAlreadyStarted if the sub-handshake has + * already been started + */ + public void startDriverHandshake(); + + /** + * Check if the sub-handshake for this switch driver has been completed. + * This method can only be called after startDriverHandshake() + * + * This methods must only be called from the I/O thread + * @return true if the sub-handshake has been completed. False otherwise + * @throws SwitchDriverSubHandshakeNotStarted if startDriverHandshake() has + * not been called yet. + */ + public boolean isDriverHandshakeComplete(); + + /** + * Pass the given OFMessage to the driver as part of this driver's + * sub-handshake. Must not be called after the handshake has been completed + * This methods must only be called from the I/O thread + * @param m The message that the driver should process + * @throws SwitchDriverSubHandshakeCompleted if isDriverHandshake() returns + * false before this method call + * @throws SwitchDriverSubHandshakeNotStarted if startDriverHandshake() has + * not been called yet. + */ + public void processDriverHandshakeMessage(OFMessage m); +} diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java b/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java deleted file mode 100644 index 58cc627860d679bb80975637604ca59314ec1daa..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java +++ /dev/null @@ -1,189 +0,0 @@ -/** -* Copyright 2011, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* 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 net.floodlightcontroller.core; - -import java.util.Collection; - -import org.projectfloodlight.openflow.protocol.OFBsnControllerConnectionsReply; -import org.projectfloodlight.openflow.protocol.OFControllerRole; -import org.projectfloodlight.openflow.protocol.OFFeaturesReply; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; -import org.projectfloodlight.openflow.protocol.OFPortStatus; - -import net.floodlightcontroller.util.OrderedCollection; - -/** - * An openflow switch connecting to the controller. This interface offers - * methods for interacting with switches using OpenFlow, and retrieving - * information about the switches. - */ -public interface IOFSwitchBackend extends IOFSwitch { - /** - * Set the netty Channel this switch instance is associated with - * Called immediately after instantiation - * @param channel - */ - void registerConnection(IOFConnectionBackend connection); - - /** - * Remove the netty Channels associated with this switch - * @param channel - */ - void removeConnections(); - - /** - * Remove the netty Channel belonging to the specified connection - * @param connection - */ - void removeConnection(IOFConnectionBackend connection); - - /** - * Set the OFFeaturesReply message returned by the switch during initial - * handshake. - * @param featuresReply - */ - void setFeaturesReply(OFFeaturesReply featuresReply); - - /** set the gentable mapping for this switch */ - void setGenTableMap(GenTableMap map); - - /** - * Add or modify a switch port. - * This is called by the core controller - * code in response to a OFPortStatus message. It should not typically be - * called by other floodlight applications. - * - * OFPPR_MODIFY and OFPPR_ADD will be treated as equivalent. The OpenFlow - * spec is not clear on whether portNames are portNumbers are considered - * authoritative identifiers. We treat portNames <-> portNumber mappings - * as fixed. If they change, we delete all previous conflicting ports and - * add all new ports. - * - * @param ps the port status message - * @return the ordered Collection of changes "applied" to the old ports - * of the switch according to the PortStatus message. A single PortStatus - * message can result in multiple changes. - * If portName <-> portNumber mappings have - * changed, the iteration order ensures that delete events for old - * conflicting appear before before events adding new ports - */ - OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps); - - /** - * Compute the changes that would be required to replace the old ports - * of this switch with the new ports - * @param ports new ports to set - * @return the ordered collection of changes "applied" to the old ports - * of the switch in order to set them to the new set. - * If portName <-> portNumber mappings have - * changed, the iteration order ensures that delete events for old - * conflicting appear before before events adding new ports - */ - OrderedCollection<PortChangeEvent> - comparePorts(Collection<OFPortDesc> ports); - - /** - * Replace the ports of this switch with the given ports. - * @param ports new ports to set - * @return the ordered collection of changes "applied" to the old ports - * of the switch in order to set them to the new set. - * If portName <-> portNumber mappings have - * changed, the iteration order ensures that delete events for old - * conflicting appear before before events adding new ports - */ - OrderedCollection<PortChangeEvent> - setPorts(Collection<OFPortDesc> ports); - - /*********************************************** - * The following method can be overridden by - * specific types of switches - *********************************************** - */ - - /** - * Set the SwitchProperties based on it's description - * @param description - */ - void setSwitchProperties(SwitchDescription description); - - /** - * Set the flow table full flag in the switch - */ - void setTableFull(boolean isFull); - - /** - * Start this switch driver's sub handshake. This might be a no-op but - * this method must be called at least once for the switch to be become - * ready. - * This method must only be called from the I/O thread - * @throws SwitchDriverSubHandshakeAlreadyStarted if the sub-handshake has - * already been started - */ - void startDriverHandshake(); - - /** - * Check if the sub-handshake for this switch driver has been completed. - * This method can only be called after startDriverHandshake() - * - * This methods must only be called from the I/O thread - * @return true if the sub-handshake has been completed. False otherwise - * @throws SwitchDriverSubHandshakeNotStarted if startDriverHandshake() has - * not been called yet. - */ - boolean isDriverHandshakeComplete(); - - /** - * Pass the given OFMessage to the driver as part of this driver's - * sub-handshake. Must not be called after the handshake has been completed - * This methods must only be called from the I/O thread - * @param m The message that the driver should process - * @throws SwitchDriverSubHandshakeCompleted if isDriverHandshake() returns - * false before this method call - * @throws SwitchDriverSubHandshakeNotStarted if startDriverHandshake() has - * not been called yet. - */ - void processDriverHandshakeMessage(OFMessage m); - - void setPortDescStats(OFPortDescStatsReply portDescStats); - - /** - * Cancel all pending request - */ - void cancelAllPendingRequests(); - - /** the the current HA role of this switch */ - void setControllerRole(OFControllerRole role); - - void setStatus(SwitchStatus switchStatus); - - /** - * Updates the switch's mapping of controller connections - * @param controllerCxnsReply the controller connections message sent from the switch - */ - void updateControllerConnections(OFBsnControllerConnectionsReply controllerCxnsReply); - - /** - * Determines whether there is another master controller that the switches are - * connected to by looking at the controller connections. - * @return true if another viable master exists - */ - boolean hasAnotherMaster(); - -} diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitchDriver.java b/src/main/java/net/floodlightcontroller/core/IOFSwitchDriver.java index b56655535373e57b59e017791dce25834abe5649..f7f720f914d34f6e812dc9032ccb135a8a3f100a 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitchDriver.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitchDriver.java @@ -16,10 +16,7 @@ package net.floodlightcontroller.core; -import net.floodlightcontroller.core.IOFSwitchBackend; -import org.projectfloodlight.openflow.protocol.OFFactory; - -import net.floodlightcontroller.core.SwitchDescription; +import org.openflow.protocol.statistics.OFDescriptionStatistics; public interface IOFSwitchDriver { /** @@ -29,5 +26,5 @@ public interface IOFSwitchDriver { * @return A IOFSwitch instance if the driver found an implementation * for the given description. Null otherwise */ - public IOFSwitchBackend getOFSwitchImpl(SwitchDescription description, OFFactory factory); -} \ No newline at end of file + public IOFSwitch getOFSwitchImpl(OFDescriptionStatistics description); +} diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java b/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java index 57eb4d179d27ad38d7202991ac26799bd822e6e7..e0c7b53f8b29eb8349f28d9f42cc97b43b309c8f 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java @@ -17,9 +17,6 @@ package net.floodlightcontroller.core; -import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.types.DatapathId; - /** * Switch lifecycle notifications. * @@ -48,20 +45,20 @@ public interface IOFSwitchListener { * the switch is connected at some controller in the cluster * @param switchId the datapath Id of the new switch */ - public void switchAdded(DatapathId switchId); + public void switchAdded(long switchId); /** * Fired when a switch disconnects from the cluster , * @param switchId the datapath Id of the switch */ - public void switchRemoved(DatapathId switchId); + public void switchRemoved(long switchId); /** * Fired when a switch becomes active *on the local controller*, I.e., * the switch is connected to the local controller and is in MASTER mode * @param switchId the datapath Id of the switch */ - public void switchActivated(DatapathId switchId); + public void switchActivated(long switchId); /** * Fired when a port on a known switch changes. @@ -77,9 +74,9 @@ public interface IOFSwitchListener { * @param port * @param type */ - public void switchPortChanged(DatapathId switchId, - OFPortDesc port, - PortChangeType type); + public void switchPortChanged(long switchId, + ImmutablePort port, + IOFSwitch.PortChangeType type); /** * Fired when any non-port related information (e.g., attributes, @@ -87,5 +84,5 @@ public interface IOFSwitchListener { * TODO: currently unused * @param switchId */ - public void switchChanged(DatapathId switchId); + public void switchChanged(long switchId); } diff --git a/src/main/java/net/floodlightcontroller/core/ImmutablePort.java b/src/main/java/net/floodlightcontroller/core/ImmutablePort.java new file mode 100644 index 0000000000000000000000000000000000000000..c015454cad15d3d24d85a0d0ddc89531062237d4 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/ImmutablePort.java @@ -0,0 +1,561 @@ +package net.floodlightcontroller.core; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +import net.floodlightcontroller.util.EnumBitmaps; +import org.openflow.protocol.OFPhysicalPort; +import org.openflow.protocol.OFPhysicalPort.OFPortConfig; +import org.openflow.protocol.OFPhysicalPort.OFPortFeatures; +import org.openflow.protocol.OFPhysicalPort.OFPortState; +import org.openflow.protocol.OFPhysicalPort.PortSpeed; +import org.openflow.util.HexString; + + +/** + * An immutable version of an OFPhysical port. In addition, it uses EnumSets + * instead of integer bitmaps to represent + * OFPortConfig, OFPortState, and OFPortFeature bitmaps. + * + * Port names are stored with the original case but equals() and XXXX use + * case-insentivie comparisions for port names!! + * + * TODO: create a Builder so we can easily construct OFPhysicalPorts + * TODO: should we verify / ensure that the features make sense, i.e., that + * currentFeatures IsSubsetOf advertisedFeatures IsSubsetOf + * supportedFeatures + * + * @author gregor + * + */ +public class ImmutablePort { + private final short portNumber; + private final byte[] hardwareAddress; + private final String name; + private final EnumSet<OFPortConfig> config; + private final boolean portStateLinkDown; + private final OFPortState stpState; + private final EnumSet<OFPortFeatures> currentFeatures; + private final EnumSet<OFPortFeatures> advertisedFeatures; + private final EnumSet<OFPortFeatures> supportedFeatures; + private final EnumSet<OFPortFeatures> peerFeatures; + + /** + * A builder class to create ImmutablePort instances + * + * TODO: add methods to remove elements from the EnumSets + */ + public static class Builder { + private short portNumber; + private byte[] hardwareAddress; + private String name; + private EnumSet<OFPortConfig> config; + private boolean portStateLinkDown; + private OFPortState stpState; + private EnumSet<OFPortFeatures> currentFeatures; + private EnumSet<OFPortFeatures> advertisedFeatures; + private EnumSet<OFPortFeatures> supportedFeatures; + private EnumSet<OFPortFeatures> peerFeatures; + + public Builder() { + this.portNumber = (short)1; + this.hardwareAddress = new byte[] { 0, 0, 0, 0, 0, 0 }; + this.name = ""; + this.config = EnumSet.noneOf(OFPortConfig.class); + this.portStateLinkDown = false; + this.stpState = OFPortState.OFPPS_STP_LISTEN; + this.currentFeatures = EnumSet.noneOf(OFPortFeatures.class); + this.advertisedFeatures = EnumSet.noneOf(OFPortFeatures.class); + this.supportedFeatures = EnumSet.noneOf(OFPortFeatures.class); + this.peerFeatures = EnumSet.noneOf(OFPortFeatures.class); + } + + public Builder(ImmutablePort p) { + this.portNumber = p.getPortNumber(); + this.hardwareAddress = p.getHardwareAddress(); + this.name = p.getName(); + this.config = EnumSet.copyOf(p.getConfig()); + this.portStateLinkDown = p.isLinkDown(); + this.stpState = p.getStpState(); + this.currentFeatures = EnumSet.copyOf(p.getCurrentFeatures()); + this.advertisedFeatures = EnumSet.copyOf(p.getAdvertisedFeatures()); + this.supportedFeatures = EnumSet.copyOf(p.getSupportedFeatures()); + this.peerFeatures = EnumSet.copyOf(p.getPeerFeatures()); + } + + /** + * @param portNumber the portNumber to set + */ + public Builder setPortNumber(short portNumber) { + this.portNumber = portNumber; + return this; + } + /** + * @param hardwareAddress the hardwareAddress to set + */ + public Builder setHardwareAddress(byte[] hardwareAddress) { + if (hardwareAddress== null) { + throw new NullPointerException("Hardware address must not be null"); + } + if (hardwareAddress.length != 6) { + throw new IllegalArgumentException("Harware address must be 6 " + + "bytes long but hardware address is " + + Arrays.toString(hardwareAddress)); + } + this.hardwareAddress = Arrays.copyOf(hardwareAddress, 6); + return this; + } + /** + * @param name the name to set + */ + public Builder setName(String name) { + if (name == null) + throw new NullPointerException("Port name must not be null"); + this.name = name; + return this; + } + /** + * @param config the config to set + */ + public Builder addConfig(OFPortConfig config) { + if (config == null) + throw new NullPointerException("PortConfig must not be null"); + this.config.add(config); + return this; + } + /** + * @param portStateLinkDown the portStateLinkDown to set + */ + public Builder setPortStateLinkDown(boolean portStateLinkDown) { + this.portStateLinkDown = portStateLinkDown; + return this; + } + /** + * @param stpState the stpState to set + */ + public Builder setStpState(OFPortState stpState) { + if (stpState == null) + throw new NullPointerException("stpState must not be null"); + if (!stpState.isStpState()) { + String msg = String.format("OFPortState enum constant %s " + + "is not an STP state", stpState); + throw new IllegalArgumentException(msg); + } + this.stpState = stpState; + return this; + } + /** + * @param currentFeatures the currentFeatures to set + */ + public Builder addCurrentFeature(OFPortFeatures currentFeature) { + if (currentFeature == null) + throw new NullPointerException("CurrentFeature must not be null"); + this.currentFeatures.add(currentFeature); + return this; + } + /** + * @param advertisedFeatures the advertisedFeatures to set + */ + public Builder + addAdvertisedFeature(OFPortFeatures advertisedFeature) { + if (advertisedFeature == null) { + throw new + NullPointerException("AdvertisedFeature must not be null"); + } + this.advertisedFeatures.add(advertisedFeature); + return this; + } + /** + * @param supportedFeatures the supportedFeatures to set + */ + public Builder addSupportedFeature(OFPortFeatures supportedFeature) { + if (supportedFeature == null) { + throw new NullPointerException("SupportedFeature must not be null"); + } + this.supportedFeatures.add(supportedFeature); + return this; + } + /** + * @param peerFeatures the peerFeatures to set + */ + public Builder addPeerFeature(OFPortFeatures peerFeature) { + if (peerFeature == null) + throw new NullPointerException("PortFeature must not be null"); + this.peerFeatures.add(peerFeature); + return this; + } + + /** + * @return + */ + public ImmutablePort build() { + return new ImmutablePort(portNumber, + hardwareAddress, + name, + EnumSet.copyOf(config), + portStateLinkDown, + stpState, + EnumSet.copyOf(currentFeatures), + EnumSet.copyOf(advertisedFeatures), + EnumSet.copyOf(supportedFeatures), + EnumSet.copyOf(peerFeatures)); + } + } + + + public static ImmutablePort fromOFPhysicalPort(OFPhysicalPort p) { + if (p == null) { + throw new NullPointerException("OFPhysicalPort must not be null"); + } + if (p.getHardwareAddress() == null) { + throw new NullPointerException("Hardware address must not be null"); + } + if (p.getName() == null) { + throw new NullPointerException("Port name must not be null"); + } + + return new ImmutablePort( + + p.getPortNumber(), + Arrays.copyOf(p.getHardwareAddress(), 6), + p.getName(), + EnumBitmaps.toEnumSet(OFPortConfig.class, p.getConfig()), + OFPortState.isPortDown(p.getState()), + OFPortState.getStpState(p.getState()), + EnumBitmaps.toEnumSet(OFPortFeatures.class, + p.getCurrentFeatures()), + EnumBitmaps.toEnumSet(OFPortFeatures.class, + p.getAdvertisedFeatures()), + EnumBitmaps.toEnumSet(OFPortFeatures.class, + p.getSupportedFeatures()), + EnumBitmaps.toEnumSet(OFPortFeatures.class, + p.getPeerFeatures()) + ); + } + + public static ImmutablePort create(String name, Short portNumber) { + return new ImmutablePort(portNumber, + new byte[] { 0, 0, 0, 0, 0, 0 }, + name, + EnumSet.noneOf(OFPortConfig.class), + false, + OFPortState.OFPPS_STP_LISTEN, + EnumSet.noneOf(OFPortFeatures.class), + EnumSet.noneOf(OFPortFeatures.class), + EnumSet.noneOf(OFPortFeatures.class), + EnumSet.noneOf(OFPortFeatures.class)); + } + + /** + * Private constructor. Use factory methods. + * + * Verifies pre-conditions of arguments + * Does NOT make defensive copies. Calling factory methods are required + * to copy defensively if required. + * + * @param portNumber + * @param hardwareAddress + * @param name + * @param config + * @param portStateLinkDown + * @param portStateStp + * @param currentFeatures + * @param advertisedFeatures + * @param supportedFeatures + * @param peerFeatures + */ + private ImmutablePort(short portNumber, byte[] hardwareAddress, + String name, EnumSet<OFPortConfig> config, + boolean portStateLinkDown, + OFPortState portStateStp, + EnumSet<OFPortFeatures> currentFeatures, + EnumSet<OFPortFeatures> advertisedFeatures, + EnumSet<OFPortFeatures> supportedFeatures, + EnumSet<OFPortFeatures> peerFeatures) { + if (name == null) { + throw new NullPointerException("Port name must not be null"); + } + if (hardwareAddress== null) { + throw new NullPointerException("Hardware address must not be null"); + } + if (hardwareAddress.length != 6) { + throw new IllegalArgumentException("Harware address must be 6 " + + "bytes long but hardware address is " + + Arrays.toString(hardwareAddress)); + } + if (config == null) + throw new NullPointerException("portConfig must not be null"); + if (portStateStp == null) + throw new NullPointerException("portStateStp must not be null"); + if (currentFeatures == null) + throw new NullPointerException("currentFeatures must not be null"); + if (advertisedFeatures == null) + throw new NullPointerException("advertisedFeatures must not be null"); + if (supportedFeatures == null) + throw new NullPointerException("supportedFeatures must not be null"); + if (peerFeatures == null) + throw new NullPointerException("peerFeatures must not be null"); + + this.portNumber = portNumber; + this.hardwareAddress = hardwareAddress; + this.name = name; + this.config = config; + this.portStateLinkDown = portStateLinkDown; + this.stpState = portStateStp; + this.currentFeatures = currentFeatures; + this.advertisedFeatures = advertisedFeatures; + this.supportedFeatures = supportedFeatures; + this.peerFeatures = peerFeatures; + } + + public short getPortNumber() { + return portNumber; + } + + public byte[] getHardwareAddress() { + // FIXME: don't use arrays. + return Arrays.copyOf(hardwareAddress, 6); + } + + public String getName() { + return name; + } + + public Set<OFPortConfig> getConfig() { + return Collections.unmodifiableSet(config); + } + + /** + * Returns true if the OFPortState indicates the port is down + * @return + */ + public boolean isLinkDown() { + return portStateLinkDown; + } + + /** + * Returns the STP state portion of the OFPortState. The returned + * enum constant will be one of the four STP states and will have + * isStpState() return true + * @return + */ + public OFPortState getStpState() { + return this.stpState; + } + + public Set<OFPortFeatures> getCurrentFeatures() { + return Collections.unmodifiableSet(currentFeatures); + } + + public Set<OFPortFeatures> getAdvertisedFeatures() { + return Collections.unmodifiableSet(advertisedFeatures); + } + + public Set<OFPortFeatures> getSupportedFeatures() { + return Collections.unmodifiableSet(supportedFeatures); + } + + public Set<OFPortFeatures> getPeerFeatures() { + return Collections.unmodifiableSet(peerFeatures); + } + + + /** + * Returns true if the port is up, i.e., it's neither administratively + * down nor link down. It currently does NOT take STP state into + * consideration + * @return + */ + public boolean isEnabled() { + return (!portStateLinkDown && + !config.contains(OFPortConfig.OFPPC_PORT_DOWN)); + } + + /** + * @return the speed of the port (from currentFeatures) if the port is + * enabled, otherwise return SPEED_NONE + */ + public PortSpeed getCurrentPortSpeed() { + if (!isEnabled()) + return PortSpeed.SPEED_NONE; + PortSpeed maxSpeed = PortSpeed.SPEED_NONE; + for (OFPortFeatures f: currentFeatures) + PortSpeed.max(maxSpeed, f.getSpeed()); + return maxSpeed; + } + + public OFPhysicalPort toOFPhysicalPort() { + OFPhysicalPort ofpp = new OFPhysicalPort(); + ofpp.setPortNumber(this.getPortNumber()); + ofpp.setHardwareAddress(this.getHardwareAddress()); + ofpp.setName(this.getName()); + ofpp.setConfig(EnumBitmaps.toBitmap(this.getConfig())); + int state = this.getStpState().getValue(); + if (this.isLinkDown()) + state |= OFPortState.OFPPS_LINK_DOWN.getValue(); + ofpp.setState(state); + ofpp.setCurrentFeatures(EnumBitmaps.toBitmap(this.getCurrentFeatures())); + ofpp.setAdvertisedFeatures( + EnumBitmaps.toBitmap(this.getAdvertisedFeatures())); + ofpp.setSupportedFeatures( + EnumBitmaps.toBitmap(this.getSupportedFeatures())); + ofpp.setPeerFeatures(EnumBitmaps.toBitmap(this.getPeerFeatures())); + return ofpp; + } + + /** + * Return a brief String describing this port containing the port number + * and port name + * @return + */ + public String toBriefString() { + return String.format("%s (%d)", name, portNumber); + } + + + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime + * result + + ((advertisedFeatures == null) ? 0 + : advertisedFeatures.hashCode()); + result = prime * result + ((config == null) ? 0 : config.hashCode()); + result = prime + * result + + ((currentFeatures == null) ? 0 + : currentFeatures.hashCode()); + result = prime * result + Arrays.hashCode(hardwareAddress); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + + ((peerFeatures == null) ? 0 : peerFeatures.hashCode()); + result = prime * result + portNumber; + result = prime * result + (portStateLinkDown ? 1231 : 1237); + result = prime * result + + ((stpState == null) ? 0 : stpState.hashCode()); + result = prime + * result + + ((supportedFeatures == null) ? 0 + : supportedFeatures.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + ImmutablePort other = (ImmutablePort) obj; + if (portNumber != other.portNumber) return false; + if (name == null) { + if (other.name != null) return false; + } else if (!name.equalsIgnoreCase(other.name)) return false; + if (advertisedFeatures == null) { + if (other.advertisedFeatures != null) return false; + } else if (!advertisedFeatures.equals(other.advertisedFeatures)) + return false; + if (config == null) { + if (other.config != null) return false; + } else if (!config.equals(other.config)) return false; + if (currentFeatures == null) { + if (other.currentFeatures != null) return false; + } else if (!currentFeatures.equals(other.currentFeatures)) + return false; + if (!Arrays.equals(hardwareAddress, other.hardwareAddress)) + return false; + if (peerFeatures == null) { + if (other.peerFeatures != null) return false; + } else if (!peerFeatures.equals(other.peerFeatures)) return false; + if (portStateLinkDown != other.portStateLinkDown) return false; + if (stpState != other.stpState) return false; + if (supportedFeatures == null) { + if (other.supportedFeatures != null) return false; + } else if (!supportedFeatures.equals(other.supportedFeatures)) + return false; + return true; + } + + /** + * Convert a Collection of OFPhysicalPorts to a list of ImmutablePorts. + * All OFPhysicalPorts in the Collection must be non-null and valid. + * No other checks (name / number uniqueness) are performed + * @param ports + * @return a list of {@link ImmutablePort}s. This is list is owned by + * the caller. The returned list is not thread-safe + * @throws NullPointerException if any OFPhysicalPort or important fields + * of any OFPhysicalPort are null + * @throws IllegalArgumentException + */ + public static List<ImmutablePort> + immutablePortListOf(Collection<OFPhysicalPort> ports) { + if (ports == null) { + throw new NullPointerException("Port list must not be null"); + } + ArrayList<ImmutablePort> immutablePorts = + new ArrayList<ImmutablePort>(ports.size()); + for (OFPhysicalPort p: ports) + immutablePorts.add(fromOFPhysicalPort(p)); + return immutablePorts; + } + + /** + * Convert a Collection of ImmutablePort to a list of OFPhyscialPorts. + * All ImmutablePorts in the Collection must be non-null. + * No other checks (name / number uniqueness) are performed + * @param ports + * @return a list of {@link OFPhysicalPort}s. This is list is owned by + * the caller. The returned list is not thread-safe + * @throws NullPointerException if any {@link ImmutablePort} or the port + * list is null + * @throws IllegalArgumentException + */ + public static List<OFPhysicalPort> + ofPhysicalPortListOf(Collection<ImmutablePort> ports) { + if (ports == null) { + throw new NullPointerException("Port list must not be null"); + } + ArrayList<OFPhysicalPort> ofppList= + new ArrayList<OFPhysicalPort>(ports.size()); + for (ImmutablePort p: ports) { + if (p == null) + throw new NullPointerException("Port must not be null"); + ofppList.add(p.toOFPhysicalPort()); + } + return ofppList; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder builder2 = new StringBuilder(); + String linkState = (portStateLinkDown) ? "DOWN" : "UP"; + builder2.append("Port [") + .append(name) + .append("(").append(portNumber).append(")") + .append(", hardwareAddress=") + .append(HexString.toHexString(hardwareAddress)) + .append(", config=").append(config) + .append(", link=").append(linkState) + .append(", stpState=").append(stpState) + .append(", currentFeatures=").append(currentFeatures) + .append(", advertisedFeatures=").append(advertisedFeatures) + .append(", supportedFeatures=").append(supportedFeatures) + .append(", peerFeatures=").append(peerFeatures).append("]"); + return builder2.toString(); + } +} diff --git a/src/main/java/net/floodlightcontroller/core/LogicalOFMessageCategory.java b/src/main/java/net/floodlightcontroller/core/LogicalOFMessageCategory.java deleted file mode 100644 index 514d3541cbfc06820205ad40e9f3499bf441ceba..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/LogicalOFMessageCategory.java +++ /dev/null @@ -1,68 +0,0 @@ -package net.floodlightcontroller.core; - -import javax.annotation.Nonnull; - -import org.projectfloodlight.openflow.types.OFAuxId; - -/** - * Immutable class for logical OF message category. - * Applications should use these to define the OF Aux connections - * that they desire. - * @author Jason Parraga <Jason.Parraga@bigswitch.com> - */ -public class LogicalOFMessageCategory { - - public static final LogicalOFMessageCategory MAIN = new LogicalOFMessageCategory("MAIN", OFAuxId.MAIN); - - final private String name; - final private OFAuxId auxId; - - - public LogicalOFMessageCategory(@Nonnull String name, int auxId) { - this(name, OFAuxId.of(auxId)); - } - - public LogicalOFMessageCategory(@Nonnull String name, OFAuxId auxId) { - if (name == null) - throw new NullPointerException("name must not be null"); - this.name = name; - this.auxId = auxId; - } - - public OFAuxId getAuxId(){ - return this.auxId; - } - - public String getName(){ - return this.name; - } - - @Override - public String toString(){ - return "LogicalOFMessageCategory [name=" + getName() + " OFAuxId=" + getAuxId() + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((auxId == null) ? 0 : auxId.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - LogicalOFMessageCategory other = (LogicalOFMessageCategory) obj; - if (auxId == null) { - if (other.auxId != null) return false; - } else if (!auxId.equals(other.auxId)) return false; - if (name == null) { - if (other.name != null) return false; - } else if (!name.equals(other.name)) return false; - return true; - } -} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/Main.java b/src/main/java/net/floodlightcontroller/core/Main.java index f57ed991a385aebd94dcb4a9d2e68f2e95dfa9f7..627a56c1477f2191abc11f2f8dbc5304128c0bd6 100644 --- a/src/main/java/net/floodlightcontroller/core/Main.java +++ b/src/main/java/net/floodlightcontroller/core/Main.java @@ -18,68 +18,48 @@ package net.floodlightcontroller.core; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import net.floodlightcontroller.core.internal.CmdLineSettings; -import net.floodlightcontroller.core.module.FloodlightModuleConfigFileNotFoundException; import net.floodlightcontroller.core.module.FloodlightModuleException; import net.floodlightcontroller.core.module.FloodlightModuleLoader; import net.floodlightcontroller.core.module.IFloodlightModuleContext; +import net.floodlightcontroller.restserver.IRestApiService; /** * Host for the Floodlight main method * @author alexreimers */ public class Main { - private static final Logger logger = LoggerFactory.getLogger(Main.class); - /** - * Main method to load configuration and modules - * @param args - * @throws FloodlightModuleException - */ - public static void main(String[] args) throws FloodlightModuleException { - try { - // Setup logger - System.setProperty("org.restlet.engine.loggerFacadeClass", - "org.restlet.ext.slf4j.Slf4jLoggerFacade"); - - CmdLineSettings settings = new CmdLineSettings(); - CmdLineParser parser = new CmdLineParser(settings); - try { - parser.parseArgument(args); - } catch (CmdLineException e) { - parser.printUsage(System.out); - System.exit(1); - } - - // Load modules - FloodlightModuleLoader fml = new FloodlightModuleLoader(); - try { - IFloodlightModuleContext moduleContext = fml.loadModulesFromConfig(settings.getModuleFile()); - //TODO @Ryan I don't think we need this anymore... Run REST server - //IRestApiService restApi = moduleContext.getServiceImpl(IRestApiService.class); - //restApi.run(); - // Run the main floodlight module - //IFloodlightProviderService controller = - // moduleContext.getServiceImpl(IFloodlightProviderService.class); - // This call blocks, it has to be the last line in the main - //controller.run(); - } catch (FloodlightModuleConfigFileNotFoundException e) { - // we really want to log the message, not the stack trace - logger.error("Could not read config file: {}", e.getMessage()); - System.exit(1); - } - try { - fml.runModules(); // this should run the controller module and all modules - } catch (FloodlightModuleException e) { - logger.error("Failed to run controller modules", e); - System.exit(1); - } - } catch (Exception e) { - logger.error("Exception in main", e); - System.exit(1); - } - } + /** + * Main method to load configuration and modules + * @param args + * @throws FloodlightModuleException + */ + public static void main(String[] args) throws FloodlightModuleException { + // Setup logger + System.setProperty("org.restlet.engine.loggerFacadeClass", + "org.restlet.ext.slf4j.Slf4jLoggerFacade"); + + CmdLineSettings settings = new CmdLineSettings(); + CmdLineParser parser = new CmdLineParser(settings); + try { + parser.parseArgument(args); + } catch (CmdLineException e) { + parser.printUsage(System.out); + System.exit(1); + } + + // Load modules + FloodlightModuleLoader fml = new FloodlightModuleLoader(); + IFloodlightModuleContext moduleContext = fml.loadModulesFromConfig(settings.getModuleFile()); + // Run REST server + IRestApiService restApi = moduleContext.getServiceImpl(IRestApiService.class); + restApi.run(); + // Run the main floodlight module + IFloodlightProviderService controller = + moduleContext.getServiceImpl(IFloodlightProviderService.class); + // This call blocks, it has to be the last line in the main + controller.run(); + } } diff --git a/src/main/java/net/floodlightcontroller/core/OFConnection.java b/src/main/java/net/floodlightcontroller/core/OFConnection.java deleted file mode 100644 index ff4f529bf92c0e372967cdc5dd66fd507f34af53..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/OFConnection.java +++ /dev/null @@ -1,409 +0,0 @@ -/** - * Copyright 2012, Big Switch Networks, Inc. - * Originally created by David Erickson, Stanford University - * - * 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 net.floodlightcontroller.core; - -import java.net.SocketAddress; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import javax.annotation.Nonnull; - -import org.jboss.netty.channel.Channel; -import org.jboss.netty.util.Timeout; -import org.jboss.netty.util.Timer; -import org.jboss.netty.util.TimerTask; - -import java.util.Date; - -import net.floodlightcontroller.core.annotations.LogMessageDoc; -import net.floodlightcontroller.core.internal.Controller; -import net.floodlightcontroller.core.internal.IOFConnectionListener; -import net.floodlightcontroller.debugcounter.IDebugCounterService; - -import org.projectfloodlight.openflow.protocol.OFErrorMsg; -import org.projectfloodlight.openflow.protocol.OFFactory; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFRequest; -import org.projectfloodlight.openflow.protocol.OFStatsReply; -import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags; -import org.projectfloodlight.openflow.protocol.OFStatsRequest; -import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFAuxId; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; - -/** - * Implementation of an openflow connection to switch. Encapsulates a - * {@link Channel}, and provides message write and request/response handling - * capabilities. - * - * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> - */ -public class OFConnection implements IOFConnection, IOFConnectionBackend{ - private static final Logger logger = LoggerFactory.getLogger(OFConnection.class); - private final DatapathId dpid; - private final OFFactory factory; - private final Channel channel; - private final OFAuxId auxId; - private final Timer timer; - - private final Date connectedSince; - - private final Map<Long, Deliverable<?>> xidDeliverableMap; - - protected final static ThreadLocal<List<OFMessage>> localMsgBuffer = - new ThreadLocal<List<OFMessage>>(); - - private static final long DELIVERABLE_TIME_OUT = 60; - private static final TimeUnit DELIVERABLE_TIME_OUT_UNIT = TimeUnit.SECONDS; - - - private final OFConnectionCounters counters; - private IOFConnectionListener listener; - - public OFConnection(@Nonnull DatapathId dpid, - @Nonnull OFFactory factory, - @Nonnull Channel channel, - @Nonnull OFAuxId auxId, - @Nonnull IDebugCounterService debugCounters, - @Nonnull Timer timer) { - Preconditions.checkNotNull(dpid, "dpid"); - Preconditions.checkNotNull(factory, "factory"); - Preconditions.checkNotNull(channel, "channel"); - Preconditions.checkNotNull(timer, "timer"); - Preconditions.checkNotNull(debugCounters); - - this.listener = NullConnectionListener.INSTANCE; - this.dpid = dpid; - this.factory = factory; - this.channel = channel; - this.auxId = auxId; - this.connectedSince = new Date(); - this.xidDeliverableMap = new ConcurrentHashMap<>(); - this.counters = new OFConnectionCounters(debugCounters, dpid, this.auxId); - this.timer = timer; - } - - @Override - public void write(OFMessage m) { - if (!isConnected()) { - if (logger.isDebugEnabled()) - logger.debug("{}: not connected - dropping message {}", this, m); - return; - } - if (logger.isTraceEnabled()) - logger.trace("{}: send {}", this, m); - List<OFMessage> msgBuffer = localMsgBuffer.get(); - if (msgBuffer == null) { - msgBuffer = new ArrayList<OFMessage>(); - localMsgBuffer.set(msgBuffer); - } - - counters.updateWriteStats(m); - msgBuffer.add(m); - - if ((msgBuffer.size() >= Controller.BATCH_MAX_SIZE) - || ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) { - this.write(msgBuffer); - localMsgBuffer.set(null); - } - } - - @Override - public <R extends OFMessage> ListenableFuture<R> writeRequest(OFRequest<R> request) { - if (!isConnected()) - return Futures.immediateFailedFuture(new SwitchDisconnectedException(getDatapathId())); - - DeliverableListenableFuture<R> future = new DeliverableListenableFuture<R>(); - xidDeliverableMap.put(request.getXid(), future); - write(request); - return future; - } - - @Override - @LogMessageDoc(level = "WARN", - message = "Sending OF message that modifies switch " - + "state while in the slave role: {switch}", - explanation = "An application has sent a message to a switch " - + "that is not valid when the switch is in a slave role", - recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG) - public void write(Iterable<OFMessage> msglist) { - if (!isConnected()) { - if (logger.isDebugEnabled()) - logger.debug(this.toString() + " : not connected - dropping {} element msglist {} ", - Iterables.size(msglist), - String.valueOf(msglist).substring(0, 80)); - return; - } - for (OFMessage m : msglist) { - if (logger.isTraceEnabled()) - logger.trace("{}: send {}", this, m); - counters.updateWriteStats(m); - } - this.channel.write(msglist); - } - - // Notifies the connection object that the channel has been disconnected - public void disconnected() { - SwitchDisconnectedException exception = new SwitchDisconnectedException(getDatapathId()); - for (Long xid : xidDeliverableMap.keySet()) { - // protect against other mechanisms running at the same time - // (timeout) - Deliverable<?> removed = xidDeliverableMap.remove(xid); - if (removed != null) { - removed.deliverError(exception); - } - } - } - - @Override - public void disconnect() { - this.channel.disconnect(); - } - - @Override - public String toString() { - String channelString = (channel != null) ? String.valueOf(channel.getRemoteAddress()): "?"; - return "OFConnection [" + getDatapathId() + "(" + getAuxId() + ")" + "@" + channelString + "]"; - } - - @Override - public Date getConnectedSince() { - return connectedSince; - } - - @Override - public <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> writeStatsRequest( - OFStatsRequest<REPLY> request) { - if (!isConnected()) - return Futures.immediateFailedFuture(new SwitchDisconnectedException(getDatapathId())); - - final DeliverableListenableFuture<List<REPLY>> future = - new DeliverableListenableFuture<List<REPLY>>(); - - Deliverable<REPLY> deliverable = new Deliverable<REPLY>() { - private final List<REPLY> results = Collections - .synchronizedList(new ArrayList<REPLY>()); - - @Override - public void deliver(REPLY reply) { - results.add(reply); - if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) { - // done - future.deliver(results); - } - } - - @Override - public void deliverError(Throwable cause) { - future.deliverError(cause); - } - - @Override - public boolean isDone() { - return future.isDone(); - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - return future.cancel(mayInterruptIfRunning); - } - }; - - registerDeliverable(request.getXid(), deliverable); - this.write(request); - return future; - } - - private void registerDeliverable(long xid, Deliverable<?> deliverable) { - this.xidDeliverableMap.put(xid, deliverable); - timer.newTimeout(new TimeOutDeliverable(xid), DELIVERABLE_TIME_OUT, DELIVERABLE_TIME_OUT_UNIT); - } - - public boolean handleGenericDeliverable(OFMessage reply) { - counters.updateReadStats(reply); - @SuppressWarnings("unchecked") - Deliverable<OFMessage> deliverable = - (Deliverable<OFMessage>) this.xidDeliverableMap.get(reply.getXid()); - if (deliverable != null) { - if(reply instanceof OFErrorMsg) { - deliverable.deliverError(new OFErrorMsgException((OFErrorMsg) reply)); - } else { - deliverable.deliver(reply); - } - if (deliverable.isDone()) - this.xidDeliverableMap.remove(reply.getXid()); - return true; - } else { - return false; - } - } - - @Override - public void cancelAllPendingRequests() { - /* - * we don't need to be synchronized here. Even if another thread - * modifies the map while we're cleaning up the future will eventually - * timeout - */ - for (Deliverable<?> d : xidDeliverableMap.values()) { - d.cancel(true); - } - xidDeliverableMap.clear(); - } - - @Override - public boolean isConnected() { - return channel.isConnected(); - } - - @Override - public void flush() { - List<OFMessage> msglist = localMsgBuffer.get(); - if ((msglist != null) && (msglist.size() > 0)) { - this.write(msglist); - localMsgBuffer.set(null); - } - } - - @Override - public SocketAddress getRemoteInetAddress() { - return channel.getRemoteAddress(); - } - - @Override - public SocketAddress getLocalInetAddress() { - return channel.getLocalAddress(); - } - - public boolean deliverResponse(OFMessage m) { - if (handleGenericDeliverable(m)) - return true; - else - return false; - } - - @Override - public boolean isWritable() { - return channel.isWritable(); - } - - @Override - public DatapathId getDatapathId() { - return dpid; - } - - @Override - public OFAuxId getAuxId() { - return auxId; - } - - Set<Long> getPendingRequestIds() { - return ImmutableSet.copyOf(xidDeliverableMap.keySet()); - } - - @Override - public OFFactory getOFFactory() { - return this.factory; - } - - /** - * Timeout class instantiated for deliverables. Will throw a timeout exception - * if proper responses are not received in time. - * - */ - private class TimeOutDeliverable implements TimerTask { - private final long xid; - - public TimeOutDeliverable(long xid) { - this.xid = xid; - } - - @Override - public void run(Timeout timeout) throws Exception { - Deliverable<?> removed = xidDeliverableMap.remove(xid); - if (removed != null && !removed.isDone()) { - removed.deliverError(new TimeoutException( - "timeout - did not receive answer for xid " + xid)); - } - - } - } - - public IOFConnectionListener getListener() { - return listener; - } - - /** set the connection listener - * <p> - * Note: this is assumed to be called from the Connection's IO Thread. - * - * @param listener - */ - @Override - public void setListener(IOFConnectionListener listener) { - this.listener = listener; - } - - public void messageReceived(OFMessage m) { - // Check if message was a response for a xid waiting at the switch - if(!deliverResponse(m)){ - listener.messageReceived(this, m); - } - } - - /** A dummy connection listener that just logs warn messages. Saves us a few null checks - * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> - */ - private static class NullConnectionListener implements IOFConnectionListener { - public final static NullConnectionListener INSTANCE = new NullConnectionListener(); - - private NullConnectionListener() { } - - @Override - public void connectionClosed(IOFConnectionBackend connection) { - logger.warn("NullConnectionListener for {} - received connectionClosed", connection); - } - - @Override - public void messageReceived(IOFConnectionBackend connection, OFMessage m) { - logger.warn("NullConnectionListener for {} - received messageReceived: {}", connection, m); - } - - @Override - public boolean isSwitchHandshakeComplete(IOFConnectionBackend connection) { - return false; - } - - } - - -} diff --git a/src/main/java/net/floodlightcontroller/core/OFConnectionCounters.java b/src/main/java/net/floodlightcontroller/core/OFConnectionCounters.java deleted file mode 100644 index 403fb4bbda1c3bd0ec87c8423df624e45b69ab96..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/OFConnectionCounters.java +++ /dev/null @@ -1,710 +0,0 @@ -package net.floodlightcontroller.core; - -import net.floodlightcontroller.debugcounter.IDebugCounter; -import net.floodlightcontroller.debugcounter.IDebugCounterService; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFAuxId; -import org.python.google.common.base.Preconditions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Implementation of Counters for per-connection statistics for OpenFlow - * messages. - * @author Alok Shankar <alok@bigswitch.com> - */ -public class OFConnectionCounters { - public static final String COUNTER_MODULE = OFConnectionCounters.class.getPackage().getName(); - - /** - * Counters for open flow message types - */ - // Write Counters - // - private final IDebugCounter ctrWriteHello; - private final IDebugCounter ctrWriteError; - private final IDebugCounter ctrWriteEchoRequest; - private final IDebugCounter ctrWriteEchoReply; - private final IDebugCounter ctrWriteExperimenter; - private final IDebugCounter ctrWriteFeaturesRequest; - private final IDebugCounter ctrWriteFeaturesReply; - private final IDebugCounter ctrWriteGetConfigRequest; - private final IDebugCounter ctrWriteGetConfigReply; - private final IDebugCounter ctrWriteSetConfig; - private final IDebugCounter ctrWritePacketIn; - private final IDebugCounter ctrWritePacketOut; - private final IDebugCounter ctrWriteFlowRemoved; - private final IDebugCounter ctrWritePortStatus; - private final IDebugCounter ctrWriteFlowMod; - private final IDebugCounter ctrWritePortMod; - private final IDebugCounter ctrWriteStatsRequest; - private final IDebugCounter ctrWriteStatsReply; - private final IDebugCounter ctrWriteBarrierRequest; - private final IDebugCounter ctrWriteBarrierReply; - private final IDebugCounter ctrWriteGetAsyncReply; - private final IDebugCounter ctrWriteGetAsyncRequest; - private final IDebugCounter ctrWriteGroupMod; - private final IDebugCounter ctrWriteMeterMod; - private final IDebugCounter ctrWriteQueueGetConfigReply; - private final IDebugCounter ctrWriteQueueGetConfigRequest; - private final IDebugCounter ctrWriteRoleRequest; - private final IDebugCounter ctrWriteRoleReply; - private final IDebugCounter ctrWriteSetAsync; - private final IDebugCounter ctrWriteTableMod; - - // Read Counters - // - private final IDebugCounter ctrReadHello; - private final IDebugCounter ctrReadError; - private final IDebugCounter ctrReadEchoRequest; - private final IDebugCounter ctrReadEchoReply; - private final IDebugCounter ctrReadExperimenter; - private final IDebugCounter ctrReadFeaturesRequest; - private final IDebugCounter ctrReadFeaturesReply; - private final IDebugCounter ctrReadGetConfigRequest; - private final IDebugCounter ctrReadGetConfigReply; - private final IDebugCounter ctrReadSetConfig; - private final IDebugCounter ctrReadPacketIn; - private final IDebugCounter ctrReadPacketOut; - private final IDebugCounter ctrReadFlowRemoved; - private final IDebugCounter ctrReadPortStatus; - private final IDebugCounter ctrReadFlowMod; - private final IDebugCounter ctrReadPortMod; - private final IDebugCounter ctrReadStatsRequest; - private final IDebugCounter ctrReadStatsReply; - private final IDebugCounter ctrReadBarrierRequest; - private final IDebugCounter ctrReadBarrierReply; - private final IDebugCounter ctrReadGetAsyncReply; - private final IDebugCounter ctrReadGetAsyncRequest; - private final IDebugCounter ctrReadGroupMod; - private final IDebugCounter ctrReadMeterMod; - private final IDebugCounter ctrReadQueueGetConfigReply; - private final IDebugCounter ctrReadQueueGetConfigRequest; - private final IDebugCounter ctrReadRoleRequest; - private final IDebugCounter ctrReadRoleReply; - private final IDebugCounter ctrReadSetAsync; - private final IDebugCounter ctrReadTableMod; - - private static final Logger logger = - LoggerFactory.getLogger(OFConnectionCounters.class); - - /** - * Utility function to create description string and do counter registration - * @param countersService - * @param stringId The string ID - * @param messageType Type of open flow message - * @return the registered DebugCounter - */ - IDebugCounter registerCounterLocal(IDebugCounterService countersService, - String hierarchy, - String stringId, - String messageType){ - String counterHierarchy = stringId + hierarchy + "/" + messageType; - String counterDescription = "Number of " + messageType + - " messages in this connection"; - - return countersService.registerCounter(COUNTER_MODULE, counterHierarchy, - counterDescription); - } - - public OFConnectionCounters(IDebugCounterService counters, - DatapathId dpid, - OFAuxId auxId) { - - Preconditions.checkNotNull(counters, "Counters must not be null"); - Preconditions.checkNotNull(dpid, "dpid must not be null"); - Preconditions.checkNotNull(auxId, "auxid must not be null"); - - String stringId = dpid.toString() +":" + auxId.toString(); - String hierarchy = "/write"; - - // every level of the hierarchical counter has to be registered - // even if they are not used - - counters.registerCounter(COUNTER_MODULE, stringId , - "Counter for this connection"); - - registerCounterLocal(counters, - hierarchy, - stringId, - ""); - - ctrWriteHello = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.HELLO.toString()); - ctrWriteError = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.ERROR.toString()); - ctrWriteEchoRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.ECHO_REQUEST.toString()); - ctrWriteEchoReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.ECHO_REPLY.toString()); - ctrWriteExperimenter = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.EXPERIMENTER.toString()); - ctrWriteFeaturesRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.FEATURES_REQUEST.toString()); - ctrWriteFeaturesReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.FEATURES_REPLY.toString()); - ctrWriteGetConfigRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.GET_CONFIG_REQUEST.toString()); - ctrWriteGetConfigReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.GET_CONFIG_REPLY.toString()); - ctrWriteSetConfig = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.SET_CONFIG.toString()); - ctrWritePacketIn = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.PACKET_IN.toString()); - ctrWritePacketOut = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.PACKET_OUT.toString()); - ctrWriteFlowRemoved = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.FLOW_REMOVED.toString()); - ctrWritePortStatus = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.PORT_STATUS.toString()); - ctrWriteFlowMod = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.FLOW_MOD.toString()); - ctrWritePortMod = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.PORT_MOD.toString()); - ctrWriteStatsRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.STATS_REQUEST.toString()); - ctrWriteStatsReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.STATS_REPLY.toString()); - ctrWriteBarrierRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.BARRIER_REQUEST.toString()); - ctrWriteBarrierReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.BARRIER_REPLY.toString()); - ctrWriteGetAsyncReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.GET_ASYNC_REPLY.toString()); - ctrWriteGetAsyncRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.GET_ASYNC_REQUEST.toString()); - ctrWriteGroupMod = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.GROUP_MOD.toString()); - ctrWriteMeterMod = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.METER_MOD.toString()); - ctrWriteQueueGetConfigReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.QUEUE_GET_CONFIG_REPLY.toString()); - ctrWriteQueueGetConfigRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.QUEUE_GET_CONFIG_REQUEST.toString()); - ctrWriteRoleReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.ROLE_REPLY.toString()); - ctrWriteRoleRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.ROLE_REQUEST.toString()); - ctrWriteSetAsync = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.SET_ASYNC.toString()); - ctrWriteTableMod = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.TABLE_MOD.toString()); - - // Register Read Counters - // - hierarchy = "/read"; - - registerCounterLocal(counters, - hierarchy, - stringId, - ""); - ctrReadHello = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.HELLO.toString()); - ctrReadError = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.ERROR.toString()); - ctrReadEchoRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.ECHO_REQUEST.toString()); - ctrReadEchoReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.ECHO_REPLY.toString()); - ctrReadExperimenter = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.EXPERIMENTER.toString()); - ctrReadFeaturesRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.FEATURES_REQUEST.toString()); - ctrReadFeaturesReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.FEATURES_REPLY.toString()); - ctrReadGetConfigRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.GET_CONFIG_REQUEST.toString()); - ctrReadGetConfigReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.GET_CONFIG_REPLY.toString()); - ctrReadSetConfig = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.SET_CONFIG.toString()); - ctrReadPacketIn = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.PACKET_IN.toString()); - ctrReadPacketOut = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.PACKET_OUT.toString()); - ctrReadFlowRemoved = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.FLOW_REMOVED.toString()); - ctrReadPortStatus = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.PORT_STATUS.toString()); - ctrReadFlowMod = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.FLOW_MOD.toString()); - ctrReadPortMod = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.PORT_MOD.toString()); - ctrReadStatsRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.STATS_REQUEST.toString()); - ctrReadStatsReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.STATS_REPLY.toString()); - ctrReadBarrierRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.BARRIER_REQUEST.toString()); - ctrReadBarrierReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.BARRIER_REPLY.toString()); - ctrReadGetAsyncReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.GET_ASYNC_REPLY.toString()); - ctrReadGetAsyncRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.GET_ASYNC_REQUEST.toString()); - ctrReadGroupMod = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.GROUP_MOD.toString()); - ctrReadMeterMod = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.METER_MOD.toString()); - ctrReadQueueGetConfigReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.QUEUE_GET_CONFIG_REPLY.toString()); - ctrReadQueueGetConfigRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.QUEUE_GET_CONFIG_REQUEST.toString()); - ctrReadRoleReply = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.ROLE_REPLY.toString()); - ctrReadRoleRequest = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.ROLE_REQUEST.toString()); - ctrReadSetAsync = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.SET_ASYNC.toString()); - ctrReadTableMod = - registerCounterLocal(counters, - hierarchy, - stringId, - OFType.TABLE_MOD.toString()); - } - - /** - * Update Write Counters for Open flow messages - * @param ofm openflow message - */ - public void updateWriteStats(OFMessage ofm) { - switch(ofm.getType()){ - case BARRIER_REPLY: - ctrWriteBarrierReply.increment(); - break; - - case BARRIER_REQUEST: - ctrWriteBarrierRequest.increment(); - break; - - case ECHO_REPLY: - ctrWriteEchoReply.increment(); - break; - - case ECHO_REQUEST: - ctrWriteEchoRequest.increment(); - break; - - case ERROR: - ctrWriteError.increment(); - break; - - case EXPERIMENTER: - ctrWriteExperimenter.increment(); - break; - - case FEATURES_REPLY: - ctrWriteFeaturesReply.increment(); - break; - - case FEATURES_REQUEST: - ctrWriteFeaturesRequest.increment(); - break; - - case FLOW_MOD: - ctrWriteFlowMod.increment(); - break; - - case FLOW_REMOVED: - ctrWriteFlowRemoved.increment(); - break; - - case GET_ASYNC_REPLY: - ctrWriteGetAsyncReply.increment(); - break; - - case GET_ASYNC_REQUEST: - ctrWriteGetAsyncRequest.increment(); - break; - - case GET_CONFIG_REPLY: - ctrWriteGetConfigReply.increment(); - break; - - case GET_CONFIG_REQUEST: - ctrWriteGetConfigRequest.increment(); - break; - - case GROUP_MOD: - ctrWriteGroupMod.increment(); - break; - - case HELLO: - ctrWriteHello.increment(); - break; - - case METER_MOD: - ctrWriteMeterMod.increment(); - break; - - case PACKET_IN: - ctrWritePacketIn.increment(); - break; - - case PACKET_OUT: - ctrWritePacketOut.increment(); - break; - - case PORT_MOD: - ctrWritePortMod.increment(); - break; - - case PORT_STATUS: - ctrWritePortStatus.increment(); - break; - - case QUEUE_GET_CONFIG_REPLY: - ctrWriteQueueGetConfigReply.increment(); - break; - - case QUEUE_GET_CONFIG_REQUEST: - ctrWriteQueueGetConfigRequest.increment(); - break; - - case ROLE_REPLY: - ctrWriteRoleReply.increment(); - break; - - case ROLE_REQUEST: - ctrWriteRoleRequest.increment(); - break; - - case SET_ASYNC: - ctrWriteSetAsync.increment(); - break; - - case SET_CONFIG: - ctrWriteSetConfig.increment(); - break; - - case STATS_REPLY: - ctrWriteStatsReply.increment(); - break; - - case STATS_REQUEST: - ctrWriteStatsRequest.increment(); - break; - - case TABLE_MOD: - ctrWriteTableMod.increment(); - break; - - default: - logger.warn(ofm.getType().toString() + - ": Invalid OpenFlow Messaqe!"); - break; - } - } - - /** - * Update Read openflow counters for this connection - * @param ofm Open Flow Message - */ - public void updateReadStats(OFMessage ofm){ - switch(ofm.getType()){ - case BARRIER_REPLY: - ctrReadBarrierReply.increment(); - break; - - case BARRIER_REQUEST: - ctrReadBarrierRequest.increment(); - break; - - case ECHO_REPLY: - ctrReadEchoReply.increment(); - break; - - case ECHO_REQUEST: - ctrReadEchoRequest.increment(); - break; - - case ERROR: - ctrReadError.increment(); - break; - - case EXPERIMENTER: - ctrReadExperimenter.increment(); - break; - - case FEATURES_REPLY: - ctrReadFeaturesReply.increment(); - break; - - case FEATURES_REQUEST: - ctrReadFeaturesRequest.increment(); - break; - - case FLOW_MOD: - ctrReadFlowMod.increment(); - break; - - case FLOW_REMOVED: - ctrReadFlowRemoved.increment(); - break; - - case GET_ASYNC_REPLY: - ctrReadGetAsyncReply.increment(); - break; - - case GET_ASYNC_REQUEST: - ctrReadGetAsyncRequest.increment(); - break; - - case GET_CONFIG_REPLY: - ctrReadGetConfigReply.increment(); - break; - - case GET_CONFIG_REQUEST: - ctrReadGetConfigRequest.increment(); - break; - - case GROUP_MOD: - ctrReadGroupMod.increment(); - break; - - case HELLO: - ctrReadHello.increment(); - break; - - case METER_MOD: - ctrReadMeterMod.increment(); - break; - - case PACKET_IN: - ctrReadPacketIn.increment(); - break; - - case PACKET_OUT: - ctrReadPacketOut.increment(); - break; - - case PORT_MOD: - ctrReadPortMod.increment(); - break; - - case PORT_STATUS: - ctrReadPortStatus.increment(); - break; - - case QUEUE_GET_CONFIG_REPLY: - ctrReadQueueGetConfigReply.increment(); - break; - - case QUEUE_GET_CONFIG_REQUEST: - ctrReadQueueGetConfigRequest.increment(); - break; - - case ROLE_REPLY: - ctrReadRoleReply.increment(); - break; - - case ROLE_REQUEST: - ctrReadRoleRequest.increment(); - break; - - case SET_ASYNC: - ctrReadSetAsync.increment(); - break; - - case SET_CONFIG: - ctrReadSetConfig.increment(); - break; - - case STATS_REPLY: - ctrReadStatsReply.increment(); - break; - - case STATS_REQUEST: - ctrReadStatsRequest.increment(); - break; - - case TABLE_MOD: - ctrReadTableMod.increment(); - break; - - default: - logger.warn(ofm.getType().toString() + - ": Invalid OpenFlow Messaqe!"); - break; - } - } -} diff --git a/src/main/java/net/floodlightcontroller/core/OFErrorMsgException.java b/src/main/java/net/floodlightcontroller/core/OFErrorMsgException.java deleted file mode 100644 index f990f0a451052dfac060673e5bca7b20d6e802ad..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/OFErrorMsgException.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.floodlightcontroller.core; - -import org.projectfloodlight.openflow.protocol.OFErrorMsg; -import org.projectfloodlight.openflow.protocol.OFRequest; - -/** raised/reported by Futures in @IOFConnection when a an - * {@link OFErrorMsg} is received in response to a {@link OFRequest} - * sent via {@link OFConnection#writeRequest(OFRequest)} or - * {@link OFConnection#writeStatsRequest(org.projectfloodlight.openflow.protocol.OFStatsRequest)}. - * - * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> - */ -public class OFErrorMsgException extends Exception { - private static final long serialVersionUID = 1L; - - private final OFErrorMsg errorMessage; - - public OFErrorMsgException(final OFErrorMsg errorMessage) { - super("OF error received: " + errorMessage.toString()); - this.errorMessage = errorMessage; - } - - /** @return the received OFErrorMsg that caused the error to be raised. - */ - public OFErrorMsg getErrorMessage() { - return errorMessage; - } -} diff --git a/src/main/java/net/floodlightcontroller/core/OFMessageFilterManager.java b/src/main/java/net/floodlightcontroller/core/OFMessageFilterManager.java index 9a0348767c6d7b952cb9496169d3477b180d6865..c5adf4470355476526cfe896c92195760d5e8d0f 100644 --- a/src/main/java/net/floodlightcontroller/core/OFMessageFilterManager.java +++ b/src/main/java/net/floodlightcontroller/core/OFMessageFilterManager.java @@ -29,12 +29,12 @@ import java.util.concurrent.ScheduledExecutorService; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; -import org.projectfloodlight.openflow.protocol.OFFlowMod; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPacketIn; -import org.projectfloodlight.openflow.protocol.OFPacketOut; -import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.util.HexString; +import org.openflow.protocol.OFFlowMod; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFPacketOut; +import org.openflow.protocol.OFType; +import org.openflow.util.HexString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -226,9 +226,9 @@ public class OFMessageFilterManager OFPacketOut p = (OFPacketOut) m; // No MAC match if packetOut doesn't have the packet. - if (p.getData() == null) return null; + if (p.getPacketData() == null) return null; - eth.deserialize(p.getData(), 0, p.getData().length); + eth.deserialize(p.getPacketData(), 0, p.getPacketData().length); } else if (m.getType() == OFType.FLOW_MOD) { // flow-mod can't be matched by mac. return null; diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitch.java b/src/main/java/net/floodlightcontroller/core/OFSwitch.java deleted file mode 100644 index 38b5ddaa6b2bb7a49a3a31deadb1b21cd2719a94..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/OFSwitch.java +++ /dev/null @@ -1,1131 +0,0 @@ -/** -* Copyright 2012, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* 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 net.floodlightcontroller.core; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.URI; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import javax.annotation.Nonnull; - -import net.floodlightcontroller.core.annotations.LogMessageDoc; -import net.floodlightcontroller.core.internal.IOFSwitchManager; -import net.floodlightcontroller.core.util.AppCookie; -import net.floodlightcontroller.core.util.URIUtil; - -import org.projectfloodlight.openflow.protocol.OFActionType; -import org.projectfloodlight.openflow.protocol.OFBsnControllerConnection; -import org.projectfloodlight.openflow.protocol.OFBsnControllerConnectionState; -import org.projectfloodlight.openflow.protocol.OFBsnControllerConnectionsReply; -import org.projectfloodlight.openflow.protocol.OFCapabilities; -import org.projectfloodlight.openflow.protocol.OFControllerRole; -import org.projectfloodlight.openflow.protocol.OFFactory; -import org.projectfloodlight.openflow.protocol.OFFeaturesReply; -import org.projectfloodlight.openflow.protocol.OFFlowWildcards; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; -import org.projectfloodlight.openflow.protocol.OFPortReason; -import org.projectfloodlight.openflow.protocol.OFPortState; -import org.projectfloodlight.openflow.protocol.OFPortStatus; -import org.projectfloodlight.openflow.protocol.OFRequest; -import org.projectfloodlight.openflow.protocol.OFStatsReply; -import org.projectfloodlight.openflow.protocol.OFStatsRequest; -import org.projectfloodlight.openflow.protocol.OFVersion; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFAuxId; -import org.projectfloodlight.openflow.types.OFPort; - -import net.floodlightcontroller.util.LinkedHashSetWrapper; -import net.floodlightcontroller.util.OrderedCollection; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.ListenableFuture; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - -/** - * This is the internal representation of an openflow switch. - */ -public class OFSwitch implements IOFSwitchBackend { - protected static final Logger log = - LoggerFactory.getLogger(OFSwitch.class); - - protected final ConcurrentMap<Object, Object> attributes; - protected final IOFSwitchManager switchManager; - - /* Switch features from initial featuresReply */ - protected Set<OFCapabilities> capabilities; - protected long buffers; - protected Set<OFActionType> actions; - protected short tables; - protected final DatapathId datapathId; - - private boolean startDriverHandshakeCalled = false; - private final Map<OFAuxId, IOFConnectionBackend> connections; - private volatile Map<URI, Map<OFAuxId, OFBsnControllerConnection>> controllerConnections; - protected OFFactory factory; - - /** - * Members hidden from subclasses - */ - private final PortManager portManager; - - private volatile boolean connected; - - private volatile OFControllerRole role; - - private boolean flowTableFull = false; - - protected SwitchDescription description; - - private GenTableMap genTableMap; - - private SwitchStatus status; - - public static final int OFSWITCH_APP_ID = ident(5); - - static { - AppCookie.registerApp(OFSwitch.OFSWITCH_APP_ID, "switch"); - } - - public OFSwitch(IOFConnectionBackend connection, @Nonnull OFFactory factory, @Nonnull IOFSwitchManager switchManager, - @Nonnull DatapathId datapathId) { - if(connection == null) - throw new NullPointerException("connection must not be null"); - if(!connection.getAuxId().equals(OFAuxId.MAIN)) - throw new IllegalArgumentException("connection must be the main connection"); - if(factory == null) - throw new NullPointerException("factory must not be null"); - if(switchManager == null) - throw new NullPointerException("switchManager must not be null"); - - this.connected = true; - this.factory = factory; - this.switchManager = switchManager; - this.datapathId = datapathId; - this.attributes = new ConcurrentHashMap<Object, Object>(); - this.role = null; - this.description = new SwitchDescription(); - this.portManager = new PortManager(); - this.genTableMap = GenTableMap.empty(); - this.status = SwitchStatus.HANDSHAKE; - - // Connections - this.connections = new ConcurrentHashMap<OFAuxId, IOFConnectionBackend>(); - this.connections.put(connection.getAuxId(), connection); - - // Switch's controller connection - this.controllerConnections = ImmutableMap.of(); - - // Defaults properties for an ideal switch - this.setAttribute(PROP_FASTWILDCARDS, EnumSet.allOf(OFFlowWildcards.class)); - this.setAttribute(PROP_SUPPORTS_OFPP_FLOOD, Boolean.TRUE); - this.setAttribute(PROP_SUPPORTS_OFPP_TABLE, Boolean.TRUE); - } - - private static int ident(int i) { - return i; - } - - @Override - public OFFactory getOFFactory() { - return factory; - } - - /** - * Manages the ports of this switch. - * - * Provides methods to query and update the stored ports. The class ensures - * that every port name and port number is unique. When updating ports - * the class checks if port number <-> port name mappings have change due - * to the update. If a new port P has number and port that are inconsistent - * with the previous mapping(s) the class will delete all previous ports - * with name or number of the new port and then add the new port. - * - * Port names are stored as-is but they are compared case-insensitive - * - * The methods that change the stored ports return a list of - * PortChangeEvents that represent the changes that have been applied - * to the port list so that IOFSwitchListeners can be notified about the - * changes. - * - * Implementation notes: - * - We keep several different representations of the ports to allow for - * fast lookups - * - Ports are stored in unchangeable lists. When a port is modified new - * data structures are allocated. - * - We use a read-write-lock for synchronization, so multiple readers are - * allowed. - */ - protected static class PortManager { - private final ReentrantReadWriteLock lock; - private List<OFPortDesc> portList; - private List<OFPortDesc> enabledPortList; - private List<OFPort> enabledPortNumbers; - private Map<OFPort,OFPortDesc> portsByNumber; - private Map<String,OFPortDesc> portsByName; - - public PortManager() { - this.lock = new ReentrantReadWriteLock(); - this.portList = Collections.emptyList(); - this.enabledPortList = Collections.emptyList(); - this.enabledPortNumbers = Collections.emptyList(); - this.portsByName = Collections.emptyMap(); - this.portsByNumber = Collections.emptyMap(); - } - - /** - * Set the internal data structure storing this switch's port - * to the ports specified by newPortsByNumber - * - * CALLER MUST HOLD WRITELOCK - * - * @param newPortsByNumber - * @throws IllegaalStateException if called without holding the - * writelock - */ - private void updatePortsWithNewPortsByNumber( - Map<OFPort,OFPortDesc> newPortsByNumber) { - if (!lock.writeLock().isHeldByCurrentThread()) { - throw new IllegalStateException("Method called without " + - "holding writeLock"); - } - Map<String,OFPortDesc> newPortsByName = - new HashMap<String, OFPortDesc>(); - List<OFPortDesc> newPortList = - new ArrayList<OFPortDesc>(); - List<OFPortDesc> newEnabledPortList = - new ArrayList<OFPortDesc>(); - List<OFPort> newEnabledPortNumbers = new ArrayList<OFPort>(); - - for(OFPortDesc p: newPortsByNumber.values()) { - newPortList.add(p); - newPortsByName.put(p.getName().toLowerCase(), p); - if (p.getState().contains(OFPortState.LIVE)) { - newEnabledPortList.add(p); - newEnabledPortNumbers.add(p.getPortNo()); - } - } - portsByName = Collections.unmodifiableMap(newPortsByName); - portsByNumber = - Collections.unmodifiableMap(newPortsByNumber); - enabledPortList = - Collections.unmodifiableList(newEnabledPortList); - enabledPortNumbers = - Collections.unmodifiableList(newEnabledPortNumbers); - portList = Collections.unmodifiableList(newPortList); - } - - /** - * Handle a OFPortStatus delete message for the given port. - * Updates the internal port maps/lists of this switch and returns - * the PortChangeEvents caused by the delete. If the given port - * exists as it, it will be deleted. If the name<->number for the - * given port is inconsistent with the ports stored by this switch - * the method will delete all ports with the number or name of the - * given port. - * - * This method will increment error/warn counters and log - * - * @param delPort the port from the port status message that should - * be deleted. - * @return ordered collection of port changes applied to this switch - */ - private OrderedCollection<PortChangeEvent> - handlePortStatusDelete(OFPortDesc delPort) { - OrderedCollection<PortChangeEvent> events = - new LinkedHashSetWrapper<PortChangeEvent>(); - lock.writeLock().lock(); - try { - Map<OFPort,OFPortDesc> newPortByNumber = - new HashMap<OFPort, OFPortDesc>(portsByNumber); - OFPortDesc prevPort = - portsByNumber.get(delPort.getPortNo()); - if (prevPort == null) { - // so such port. Do we have a port with the name? - prevPort = portsByName.get(delPort.getName()); - if (prevPort != null) { - newPortByNumber.remove(prevPort.getPortNo()); - events.add(new PortChangeEvent(prevPort, - PortChangeType.DELETE)); - } - } else if (prevPort.getName().equals(delPort.getName())) { - // port exists with consistent name-number mapping - newPortByNumber.remove(delPort.getPortNo()); - events.add(new PortChangeEvent(delPort, - PortChangeType.DELETE)); - } else { - // port with same number exists but its name differs. This - // is weird. The best we can do is to delete the existing - // port(s) that have delPort's name and number. - newPortByNumber.remove(delPort.getPortNo()); - events.add(new PortChangeEvent(prevPort, - PortChangeType.DELETE)); - // is there another port that has delPort's name? - prevPort = portsByName.get(delPort.getName().toLowerCase()); - if (prevPort != null) { - newPortByNumber.remove(prevPort.getPortNo()); - events.add(new PortChangeEvent(prevPort, - PortChangeType.DELETE)); - } - } - updatePortsWithNewPortsByNumber(newPortByNumber); - return events; - } finally { - lock.writeLock().unlock(); - } - } - - /** - * Handle a OFPortStatus message, update the internal data structures - * that store ports and return the list of OFChangeEvents. - * - * This method will increment error/warn counters and log - * - * @param ps - * @return - */ - @SuppressFBWarnings(value="SF_SWITCH_FALLTHROUGH") - public OrderedCollection<PortChangeEvent> handlePortStatusMessage(OFPortStatus ps) { - if (ps == null) { - throw new NullPointerException("OFPortStatus message must " + - "not be null"); - } - lock.writeLock().lock(); - try { - OFPortDesc port = ps.getDesc(); - OFPortReason reason = ps.getReason(); - if (reason == null) { - throw new IllegalArgumentException("Unknown PortStatus " + - "reason code " + ps.getReason()); - } - - if (log.isDebugEnabled()) { - log.debug("Handling OFPortStatus: {} for {}", - reason, String.format("%s (%d)", port.getName(), port.getPortNo().getPortNumber())); - } - - if (reason == OFPortReason.DELETE) - return handlePortStatusDelete(port); - - // We handle ADD and MODIFY the same way. Since OpenFlow - // doesn't specify what uniquely identifies a port the - // notion of ADD vs. MODIFY can also be hazy. So we just - // compare the new port to the existing ones. - Map<OFPort,OFPortDesc> newPortByNumber = - new HashMap<OFPort, OFPortDesc>(portsByNumber); - OrderedCollection<PortChangeEvent> events = getSinglePortChanges(port); - for (PortChangeEvent e: events) { - switch(e.type) { - case DELETE: - newPortByNumber.remove(e.port.getPortNo()); - break; - case ADD: - if (reason != OFPortReason.ADD) { - // weird case - } - // fall through - case DOWN: - case OTHER_UPDATE: - case UP: - // update or add the port in the map - newPortByNumber.put(e.port.getPortNo(), e.port); - break; - } - } - updatePortsWithNewPortsByNumber(newPortByNumber); - return events; - } finally { - lock.writeLock().unlock(); - } - - } - - /** - * Given a new or modified port newPort, returns the list of - * PortChangeEvents to "transform" the current ports stored by - * this switch to include / represent the new port. The ports stored - * by this switch are <b>NOT</b> updated. - * - * This method acquires the readlock and is thread-safe by itself. - * Most callers will need to acquire the write lock before calling - * this method though (if the caller wants to update the ports stored - * by this switch) - * - * @param newPort the new or modified port. - * @return the list of changes - */ - public OrderedCollection<PortChangeEvent> - getSinglePortChanges(OFPortDesc newPort) { - lock.readLock().lock(); - try { - OrderedCollection<PortChangeEvent> events = - new LinkedHashSetWrapper<PortChangeEvent>(); - // Check if we have a port by the same number in our - // old map. - OFPortDesc prevPort = - portsByNumber.get(newPort.getPortNo()); - if (newPort.equals(prevPort)) { - // nothing has changed - return events; - } - - if (prevPort != null && - prevPort.getName().equals(newPort.getName())) { - // A simple modify of a exiting port - // A previous port with this number exists and it's name - // also matches the new port. Find the differences - if (prevPort.getState().contains(OFPortState.LIVE) && !newPort.getState().contains(OFPortState.LIVE)) { - events.add(new PortChangeEvent(newPort, - PortChangeType.DOWN)); - } else if (!prevPort.getState().contains(OFPortState.LIVE) && newPort.getState().contains(OFPortState.LIVE)) { - events.add(new PortChangeEvent(newPort, - PortChangeType.UP)); - } else { - events.add(new PortChangeEvent(newPort, - PortChangeType.OTHER_UPDATE)); - } - return events; - } - - if (prevPort != null) { - // There exists a previous port with the same port - // number but the port name is different (otherwise we would - // never have gotten here) - // Remove the port. Name-number mapping(s) have changed - events.add(new PortChangeEvent(prevPort, - PortChangeType.DELETE)); - } - - // We now need to check if there exists a previous port sharing - // the same name as the new/updated port. - prevPort = portsByName.get(newPort.getName().toLowerCase()); - if (prevPort != null) { - // There exists a previous port with the same port - // name but the port number is different (otherwise we - // never have gotten here). - // Remove the port. Name-number mapping(s) have changed - events.add(new PortChangeEvent(prevPort, - PortChangeType.DELETE)); - } - - // We always need to add the new port. Either no previous port - // existed or we just deleted previous ports with inconsistent - // name-number mappings - events.add(new PortChangeEvent(newPort, PortChangeType.ADD)); - return events; - } finally { - lock.readLock().unlock(); - } - } - - /** - * Compare the current ports of this switch to the newPorts list and - * return the changes that would be applied to transfort the current - * ports to the new ports. No internal data structures are updated - * see {@link #compareAndUpdatePorts(List, boolean)} - * - * @param newPorts the list of new ports - * @return The list of differences between the current ports and - * newPortList - */ - public OrderedCollection<PortChangeEvent> - comparePorts(Collection<OFPortDesc> newPorts) { - return compareAndUpdatePorts(newPorts, false); - } - - /** - * Compare the current ports of this switch to the newPorts list and - * return the changes that would be applied to transform the current - * ports to the new ports. No internal data structures are updated - * see {@link #compareAndUpdatePorts(List, boolean)} - * - * @param newPorts the list of new ports - * @return The list of differences between the current ports and - * newPortList - */ - public OrderedCollection<PortChangeEvent> - updatePorts(Collection<OFPortDesc> newPorts) { - return compareAndUpdatePorts(newPorts, true); - } - - /** - * Compare the current ports stored in this switch instance with the - * new port list given and return the differences in the form of - * PortChangeEvents. If the doUpdate flag is true, newPortList will - * replace the current list of this switch (and update the port maps) - * - * Implementation note: - * Since this method can optionally modify the current ports and - * since it's not possible to upgrade a read-lock to a write-lock - * we need to hold the write-lock for the entire operation. If this - * becomes a problem and if compares() are common we can consider - * splitting in two methods but this requires lots of code duplication - * - * @param newPorts the list of new ports. - * @param doUpdate If true the newPortList will replace the current - * port list for this switch. If false this switch will not be changed. - * @return The list of differences between the current ports and - * newPorts - * @throws NullPointerException if newPortsList is null - * @throws IllegalArgumentException if either port names or port numbers - * are duplicated in the newPortsList. - */ - private OrderedCollection<PortChangeEvent> compareAndUpdatePorts( - Collection<OFPortDesc> newPorts, - boolean doUpdate) { - if (newPorts == null) { - throw new NullPointerException("newPortsList must not be null"); - } - lock.writeLock().lock(); - try { - OrderedCollection<PortChangeEvent> events = - new LinkedHashSetWrapper<PortChangeEvent>(); - - Map<OFPort,OFPortDesc> newPortsByNumber = - new HashMap<OFPort, OFPortDesc>(); - Map<String,OFPortDesc> newPortsByName = - new HashMap<String, OFPortDesc>(); - List<OFPortDesc> newEnabledPortList = - new ArrayList<OFPortDesc>(); - List<OFPort> newEnabledPortNumbers = - new ArrayList<OFPort>(); - List<OFPortDesc> newPortsList = - new ArrayList<OFPortDesc>(newPorts); - - for (OFPortDesc p: newPortsList) { - if (p == null) { - throw new NullPointerException("portList must not " + - "contain null values"); - } - - // Add the port to the new maps and lists and check - // that every port is unique - OFPortDesc duplicatePort; - duplicatePort = newPortsByNumber.put(p.getPortNo(), p); - if (duplicatePort != null) { - String msg = String.format("Cannot have two ports " + - "with the same number: %s <-> %s", - String.format("%s (%d)", p.getName(), p.getPortNo().getPortNumber()), - String.format("%s (%d)", duplicatePort.getName(), duplicatePort.getPortNo().getPortNumber())); - throw new IllegalArgumentException(msg); - } - duplicatePort = - newPortsByName.put(p.getName().toLowerCase(), p); - if (duplicatePort != null) { - String msg = String.format("Cannot have two ports " + - "with the same name: %s <-> %s", - String.format("%s (%d)", p.getName(), p.getPortNo().getPortNumber()), - String.format("%s (%d)", duplicatePort.getName(), duplicatePort.getPortNo().getPortNumber())); - throw new IllegalArgumentException(msg); - } - if (p.getState().contains(OFPortState.LIVE)) { - newEnabledPortList.add(p); - newEnabledPortNumbers.add(p.getPortNo()); - } - - // get changes - events.addAll(getSinglePortChanges(p)); - } - // find deleted ports - // We need to do this after looping through all the new ports - // to we can handle changed name<->number mappings correctly - // We could pull it into the loop of we address this but - // it's probably not worth it - for (OFPortDesc oldPort: this.portList) { - if (!newPortsByNumber.containsKey(oldPort.getPortNo())) { - PortChangeEvent ev = - new PortChangeEvent(oldPort, - PortChangeType.DELETE); - events.add(ev); - } - } - - - if (doUpdate) { - portsByName = Collections.unmodifiableMap(newPortsByName); - portsByNumber = - Collections.unmodifiableMap(newPortsByNumber); - enabledPortList = - Collections.unmodifiableList(newEnabledPortList); - enabledPortNumbers = - Collections.unmodifiableList(newEnabledPortNumbers); - portList = Collections.unmodifiableList(newPortsList); - } - return events; - } finally { - lock.writeLock().unlock(); - } - } - - public OFPortDesc getPort(String name) { - if (name == null) { - throw new NullPointerException("Port name must not be null"); - } - lock.readLock().lock(); - try { - return portsByName.get(name.toLowerCase()); - } finally { - lock.readLock().unlock(); - } - } - - public OFPortDesc getPort(OFPort portNumber) { - lock.readLock().lock(); - try { - return portsByNumber.get(portNumber); - } finally { - lock.readLock().unlock(); - } - } - - public List<OFPortDesc> getPorts() { - lock.readLock().lock(); - try { - return portList; - } finally { - lock.readLock().unlock(); - } - } - - public List<OFPortDesc> getEnabledPorts() { - lock.readLock().lock(); - try { - return enabledPortList; - } finally { - lock.readLock().unlock(); - } - } - - public List<OFPort> getEnabledPortNumbers() { - lock.readLock().lock(); - try { - return enabledPortNumbers; - } finally { - lock.readLock().unlock(); - } - } - } - - @Override - public boolean attributeEquals(String name, Object other) { - Object attr = this.attributes.get(name); - if (attr == null) - return false; - return attr.equals(other); - } - - @Override - public Object getAttribute(String name) { - // returns null if key doesn't exist - return this.attributes.get(name); - } - - @Override - public void setAttribute(String name, Object value) { - this.attributes.put(name, value); - return; - } - - @Override - public Object removeAttribute(String name) { - return this.attributes.remove(name); - } - - @Override - public boolean hasAttribute(String name) { - return this.attributes.containsKey(name); - } - - @Override - public void registerConnection(IOFConnectionBackend connection) { - this.connections.put(connection.getAuxId(), connection); - } - - - @Override - public ImmutableList<IOFConnection> getConnections() { - return ImmutableList.<IOFConnection> copyOf(this.connections.values()); - } - - @Override - public void removeConnections() { - this.connections.clear(); - } - - @Override - public void removeConnection(IOFConnectionBackend connection) { - this.connections.remove(connection.getAuxId()); - } - - @Override - public void write(OFMessage m) { - connections.get(OFAuxId.MAIN).write(m); - } - - /** - * Gets a connection specified by aux Id. - * @param auxId the specified aux id for the connection desired. - * @return the aux connection specified by the auxId - */ - public IOFConnection getConnection(OFAuxId auxId) { - IOFConnection connection = this.connections.get(auxId); - if(connection == null){ - throw new IllegalArgumentException("OF Connection for " + this + " with " + auxId + " does not exist."); - } - return connection; - } - - public IOFConnection getConnection(LogicalOFMessageCategory category) { - if(switchManager.isCategoryRegistered(category)){ - return getConnection(category.getAuxId()); - } - else{ - throw new IllegalArgumentException(category + " is not registered with the floodlight provider service."); - } - } - - @Override - public void write(OFMessage m, LogicalOFMessageCategory category) { - this.getConnection(category).write(m); - } - - @Override - public void write(Iterable<OFMessage> msglist, LogicalOFMessageCategory category) { - this.getConnection(category).write(msglist); - } - - @Override - public OFConnection getConnectionByCategory(LogicalOFMessageCategory category){ - return (OFConnection) this.getConnection(category); - } - - @Override - public <R extends OFMessage> ListenableFuture<R> writeRequest(OFRequest<R> request, LogicalOFMessageCategory category) { - return getConnection(category).writeRequest(request); - } - - @Override - public <R extends OFMessage> ListenableFuture<R> writeRequest(OFRequest<R> request) { - return connections.get(OFAuxId.MAIN).writeRequest(request); - } - - @Override - @LogMessageDoc(level="WARN", - message="Sending OF message that modifies switch " + - "state while in the slave role: {switch}", - explanation="An application has sent a message to a switch " + - "that is not valid when the switch is in a slave role", - recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) - public void write(Iterable<OFMessage> msglist) { - connections.get(OFAuxId.MAIN).write(msglist); - } - - @Override - public void disconnect() { - - // Iterate through connections and perform cleanup - for(Entry<OFAuxId, IOFConnectionBackend> entry : this.connections.entrySet()){ - entry.getValue().disconnect(); - this.connections.remove(entry.getKey()); - } - - connected = false; - } - - @Override - public void setFeaturesReply(OFFeaturesReply featuresReply) { - if (portManager.getPorts().isEmpty() && featuresReply.getVersion().compareTo(OFVersion.OF_13) < 0) { - /* ports are updated via port status message, so we - * only fill in ports on initial connection. - */ - List<OFPortDesc> OFPortDescs = featuresReply.getPorts(); - portManager.updatePorts(OFPortDescs); - } - this.capabilities = featuresReply.getCapabilities(); - this.buffers = featuresReply.getNBuffers(); - - if (featuresReply.getVersion().compareTo(OFVersion.OF_13) < 0 ) { - // FIXME:LOJI: OF1.3 has per table actions. This needs to be modeled / handled here - this.actions = featuresReply.getActions(); - } - this.tables = featuresReply.getNTables(); - } - - @Override - public void setPortDescStats(OFPortDescStatsReply reply) { - /* ports are updated via port status message, so we - * only fill in ports on initial connection. - */ - List<OFPortDesc> OFPortDescs = reply.getEntries(); - portManager.updatePorts(OFPortDescs); - } - - @Override - public Collection<OFPortDesc> getEnabledPorts() { - return portManager.getEnabledPorts(); - } - - @Override - public Collection<OFPort> getEnabledPortNumbers() { - return portManager.getEnabledPortNumbers(); - } - - @Override - public OFPortDesc getPort(OFPort portNumber) { - return portManager.getPort(portNumber); - } - - @Override - public OFPortDesc getPort(String portName) { - return portManager.getPort(portName); - } - - @Override - public OrderedCollection<PortChangeEvent> - processOFPortStatus(OFPortStatus ps) { - return portManager.handlePortStatusMessage(ps); - } - - @Override - public Collection<OFPortDesc> getSortedPorts() { - // FIXME: Hopefully BigDB will handle this automatically soon (or not - // have the sorting requirement), in which case we could get rid of this - List<OFPortDesc> sortedPorts = - new ArrayList<OFPortDesc>(portManager.getPorts()); - Collections.sort(sortedPorts, new Comparator<OFPortDesc>() { - @Override - public int compare(OFPortDesc o1, OFPortDesc o2) { - String name1 = o1.getName(); - String name2 = o2.getName(); - return name1.compareToIgnoreCase(name2); - } - }); - return sortedPorts; - } - - @Override - public Collection<OFPortDesc> getPorts() { - return portManager.getPorts(); - } - - @Override - public OrderedCollection<PortChangeEvent> - comparePorts(Collection<OFPortDesc> ports) { - return portManager.comparePorts(ports); - } - - @Override - public OrderedCollection<PortChangeEvent> - setPorts(Collection<OFPortDesc> ports) { - return portManager.updatePorts(ports); - } - - @Override - public boolean portEnabled(OFPort portNumber) { - OFPortDesc p = portManager.getPort(portNumber); - if (p == null) return false; - return p.getState().contains(OFPortState.LIVE); - } - - @Override - public boolean portEnabled(String portName) { - OFPortDesc p = portManager.getPort(portName); - if (p == null) return false; - return p.getState().contains(OFPortState.LIVE); - } - - @Override - public DatapathId getId() { - if (datapathId == null) - throw new RuntimeException("Features reply has not yet been set"); - return datapathId; - } - - @Override - public String getStringId() { - // FIXME: Should get rid of this - return getId().toString(); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "OFSwitchBase DPID[" + ((datapathId != null) ? datapathId.toString() : "?") + "]"; - } - - @Override - public ConcurrentMap<Object, Object> getAttributes() { - return this.attributes; - } - - @Override - public Date getConnectedSince() { - return this.connections.get(OFAuxId.MAIN).getConnectedSince(); - } - - @Override - public <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> writeStatsRequest(OFStatsRequest<REPLY> request) { - return connections.get(OFAuxId.MAIN).writeStatsRequest(request); - } - - @Override - public <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> writeStatsRequest(OFStatsRequest<REPLY> request, LogicalOFMessageCategory category) { - return getConnection(category).writeStatsRequest(request); - } - - @Override - public void cancelAllPendingRequests() { - for(Entry<OFAuxId, IOFConnectionBackend> entry : this.connections.entrySet()){ - entry.getValue().cancelAllPendingRequests(); - } - } - - // If any connections are down consider a switch disconnected - @Override - public boolean isConnected() { - return connected; - } - - @Override - public boolean isActive() { - // no lock needed since we use volatile - return isConnected() && this.role == OFControllerRole.ROLE_MASTER; - } - - @Override - public OFControllerRole getControllerRole() { - return role; - } - - @Override - public void setControllerRole(OFControllerRole role) { - this.role = role; - } - - @Override - public void flush() { - for(Entry<OFAuxId, IOFConnectionBackend> entry : this.connections.entrySet()){ - entry.getValue().flush(); - } - } - - /** - * Get the IP Address for the switch - * @return the inet address - */ - @Override - public SocketAddress getInetAddress() { - return connections.get(OFAuxId.MAIN).getRemoteInetAddress(); - } - - @Override - public long getBuffers() { - return buffers; - } - - - @Override - public Set<OFActionType> getActions() { - return actions; - } - - - @Override - public Set<OFCapabilities> getCapabilities() { - return capabilities; - } - - - @Override - public short getTables() { - return tables; - } - - @Override - public SwitchDescription getSwitchDescription() { - return description; - } - - @Override - @LogMessageDoc(level="WARN", - message="Switch {switch} flow table is full", - explanation="The controller received flow table full " + - "message from the switch, could be caused by increased " + - "traffic pattern", - recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) - public void setTableFull(boolean isFull) { - if (isFull && !flowTableFull) { - switchManager.addSwitchEvent(this.datapathId, - "SWITCH_FLOW_TABLE_FULL " + - "Table full error from switch", false); - log.warn("Switch {} flow table is full", datapathId.toString()); - } - flowTableFull = isFull; - } - - @Override - public void startDriverHandshake() { - if (startDriverHandshakeCalled) - throw new SwitchDriverSubHandshakeAlreadyStarted(); - startDriverHandshakeCalled = true; - } - - @Override - public boolean isDriverHandshakeComplete() { - if (!startDriverHandshakeCalled) - throw new SwitchDriverSubHandshakeNotStarted(); - return true; - } - - @Override - public void processDriverHandshakeMessage(OFMessage m) { - if (startDriverHandshakeCalled) - throw new SwitchDriverSubHandshakeCompleted(m); - else - throw new SwitchDriverSubHandshakeNotStarted(); - } - - @Override - public GenTableMap getGenTableMap() { - return genTableMap; - } - - @Override - public void setGenTableMap(GenTableMap map) { - this.genTableMap = map; - } - - @Override - public void setSwitchProperties(SwitchDescription description) { - this.description = description; - } - - - @Override - public SwitchStatus getStatus() { - return status; - } - - @Override - public void setStatus(SwitchStatus switchStatus) { - this.status = switchStatus; - } - - /** - * Can the port be turned on without forming a new loop? - * <p> - * <b>FIXME</b> - This API is deprecated and will be removed soon. The - * property is currently not implemented by the platform, so this method - * always returns false. Whether or not a port is safe to use is dependent - * on the application strategy for forwarding traffic. - * - * @param port_num the port number to be tested - * @return always false for now. - */ - @Deprecated - @Override - public boolean isFastPort(OFPort port_num) { - return false; - } - - @Override - public void updateControllerConnections(OFBsnControllerConnectionsReply controllerCxnsReply) { - - // Instantiate clean map, can't use a builder here since we need to call temp.get() - Map<URI,Map<OFAuxId, OFBsnControllerConnection>> temp = new ConcurrentHashMap<URI,Map<OFAuxId, OFBsnControllerConnection>>(); - - List<OFBsnControllerConnection> controllerCxnUpdates = controllerCxnsReply.getConnections(); - for(OFBsnControllerConnection update : controllerCxnUpdates) { - URI uri = URI.create(update.getUri()); - - Map<OFAuxId, OFBsnControllerConnection> cxns = temp.get(uri); - - // Add to nested map - if(cxns != null){ - cxns.put(update.getAuxiliaryId(), update); - } else{ - cxns = new ConcurrentHashMap<OFAuxId, OFBsnControllerConnection>(); - cxns.put(update.getAuxiliaryId(), update); - temp.put(uri, cxns); - } - } - - this.controllerConnections = ImmutableMap.<URI,Map<OFAuxId, OFBsnControllerConnection>>copyOf(temp); - } - - @Override - public boolean hasAnotherMaster() { - - //TODO: refactor get connection to not throw illegal arg exceptions - IOFConnection mainCxn = this.getConnection(OFAuxId.MAIN); - - if(mainCxn != null) { - - // Determine the local URI - InetSocketAddress address = (InetSocketAddress) mainCxn.getLocalInetAddress(); - URI localURI = URIUtil.createURI(address.getHostName(), address.getPort()); - - for(Entry<URI,Map<OFAuxId, OFBsnControllerConnection>> entry : this.controllerConnections.entrySet()) { - - // Don't check our own controller connections - URI uri = entry.getKey(); - if(!localURI.equals(uri)){ - - // We only care for the MAIN connection - Map<OFAuxId, OFBsnControllerConnection> cxns = this.controllerConnections.get(uri); - OFBsnControllerConnection controllerCxn = cxns.get(OFAuxId.MAIN); - - if(controllerCxn != null) { - // If the controller id disconnected or not master we know it is not connected - if(controllerCxn.getState() == OFBsnControllerConnectionState.BSN_CONTROLLER_CONNECTION_STATE_CONNECTED - && controllerCxn.getRole() == OFControllerRole.ROLE_MASTER){ - return true; - } - } else { - log.warn("Unable to find controller connection with aux id " - + "MAIN for switch {} on controller with URI {}.", - this, uri); - } - } - } - } - return false; - } -} diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java new file mode 100644 index 0000000000000000000000000000000000000000..1fb321baddd352b137c78e68b99fab388766fd27 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java @@ -0,0 +1,1579 @@ +/** +* Copyright 2012, Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* 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 net.floodlightcontroller.core; + +import java.io.IOException; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import net.floodlightcontroller.core.IFloodlightProviderService.Role; +import net.floodlightcontroller.core.annotations.LogMessageDoc; +import net.floodlightcontroller.core.annotations.LogMessageDocs; +import net.floodlightcontroller.core.internal.Controller; +import net.floodlightcontroller.core.internal.OFFeaturesReplyFuture; +import net.floodlightcontroller.core.internal.OFStatisticsFuture; +import net.floodlightcontroller.core.util.AppCookie; +import net.floodlightcontroller.core.web.serializers.DPIDSerializer; +import net.floodlightcontroller.debugcounter.IDebugCounter; +import net.floodlightcontroller.debugcounter.IDebugCounterService; +import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException; +import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType; +import net.floodlightcontroller.debugcounter.NullDebugCounter; +import net.floodlightcontroller.devicemanager.SwitchPort; +import net.floodlightcontroller.packet.Ethernet; +import net.floodlightcontroller.routing.ForwardingBase; +import net.floodlightcontroller.threadpool.IThreadPoolService; +import net.floodlightcontroller.util.LinkedHashSetWrapper; +import net.floodlightcontroller.util.MACAddress; +import net.floodlightcontroller.util.OrderedCollection; +import net.floodlightcontroller.util.TimedCache; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; + +import org.jboss.netty.channel.Channel; +import org.openflow.protocol.OFFeaturesReply; +import org.openflow.protocol.OFFlowMod; +import org.openflow.protocol.OFMatch; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFPortStatus; +import org.openflow.protocol.OFPortStatus.OFPortReason; +import org.openflow.protocol.OFPort; +import org.openflow.protocol.OFStatisticsReply; +import org.openflow.protocol.OFStatisticsRequest; +import org.openflow.protocol.OFType; +import org.openflow.protocol.statistics.OFDescriptionStatistics; +import org.openflow.protocol.statistics.OFStatistics; +import org.openflow.protocol.statistics.OFStatisticsType; +import org.openflow.protocol.statistics.OFTableStatistics; +import org.openflow.util.HexString; +import org.openflow.util.U16; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is the internal representation of an openflow switch. + */ +public abstract class OFSwitchBase implements IOFSwitch { + // TODO: should we really do logging in the class or should we throw + // exception that can then be handled by callers? + protected static final Logger log = LoggerFactory.getLogger(OFSwitchBase.class); + + protected ConcurrentMap<Object, Object> attributes; + protected IFloodlightProviderService floodlightProvider; + protected IThreadPoolService threadPool; + protected IDebugCounterService debugCounters; + // FIXME: Don't use java.util.Date + protected volatile Date connectedSince; + + /* Switch features from initial featuresReply */ + protected int capabilities; + protected int buffers; + protected int actions; + protected byte tables; + protected long datapathId; + protected String stringId; + + protected short accessFlowPriority; + protected short coreFlowPriority; + + private boolean startDriverHandshakeCalled = false; + protected Channel channel; + + /** + * Members hidden from subclasses + */ + private final AtomicInteger transactionIdSource; + private final Map<Integer,OFStatisticsFuture> statsFutureMap; + private final Map<Integer, IOFMessageListener> iofMsgListenersMap; + private final Map<Integer,OFFeaturesReplyFuture> featuresFutureMap; + private volatile boolean connected; + private volatile Role role; + private final TimedCache<Long> timedCache; + private final ConcurrentMap<Short, AtomicLong> portBroadcastCacheHitMap; + + private final PortManager portManager; + + // Private members for throttling + private boolean writeThrottleEnabled = false; + protected boolean packetInThrottleEnabled = false; // used by test + private int packetInRateThresholdHigh = + Integer.parseInt(System.getProperty("input_threshold", "1000")); + private int packetInRateThresholdLow = 1; + private int packetInRatePerMacThreshold = 50; + private int packetInRatePerPortThreshold = 100; + private long messageCount = 0; + private long messageCountUniqueOFMatch = 0; + private long lastMessageTime; + private int currentRate = 0; + private TimedCache<OFMatch> ofMatchCache; + private TimedCache<Long> macCache; + private TimedCache<Long> macBlockedCache; + private TimedCache<Short> portCache; + private TimedCache<Short> portBlockedCache; + private boolean flowTableFull = false; + + protected OFDescriptionStatistics description; + + private boolean debugCountersRegistered; + @SuppressWarnings("unused") + private IDebugCounter ctrSwitch, ctrSwitchPktin, ctrSwitchWrite; + private IDebugCounter ctrSwitchPktinDrops, ctrSwitchWriteDrops; + + private static final String PACKAGE = OFSwitchBase.class.getPackage().getName(); + + + protected final static ThreadLocal<Map<IOFSwitch,List<OFMessage>>> local_msg_buffer = + new ThreadLocal<Map<IOFSwitch,List<OFMessage>>>() { + @Override + protected Map<IOFSwitch,List<OFMessage>> initialValue() { + return new HashMap<IOFSwitch,List<OFMessage>>(); + } + }; + + public static final int OFSWITCH_APP_ID = 5; + static { + AppCookie.registerApp(OFSwitchBase.OFSWITCH_APP_ID, "switch"); + } + + public OFSwitchBase() { + this.stringId = null; + this.attributes = new ConcurrentHashMap<Object, Object>(); + this.connectedSince = null; + this.transactionIdSource = new AtomicInteger(); + this.connected = false; + this.statsFutureMap = new ConcurrentHashMap<Integer,OFStatisticsFuture>(); + this.featuresFutureMap = new ConcurrentHashMap<Integer,OFFeaturesReplyFuture>(); + this.iofMsgListenersMap = new ConcurrentHashMap<Integer,IOFMessageListener>(); + this.role = null; + this.timedCache = new TimedCache<Long>(100, 5*1000 ); // 5 seconds interval + this.portBroadcastCacheHitMap = new ConcurrentHashMap<Short, AtomicLong>(); + this.description = new OFDescriptionStatistics(); + this.lastMessageTime = System.currentTimeMillis(); + + this.portManager = new PortManager(); + + // Defaults properties for an ideal switch + this.setAttribute(PROP_FASTWILDCARDS, OFMatch.OFPFW_ALL); + this.setAttribute(PROP_SUPPORTS_OFPP_FLOOD, Boolean.valueOf(true)); + this.setAttribute(PROP_SUPPORTS_OFPP_TABLE, Boolean.valueOf(true)); + if (packetInRateThresholdHigh == 0) { + packetInRateThresholdHigh = Integer.MAX_VALUE; + } else { + packetInRateThresholdLow = packetInRateThresholdHigh / 2; + } + } + + + + /** + * Manages the ports of this switch. + * + * Provides methods to query and update the stored ports. The class ensures + * that every port name and port number is unique. When updating ports + * the class checks if port number <-> port name mappings have change due + * to the update. If a new port P has number and port that are inconsistent + * with the previous mapping(s) the class will delete all previous ports + * with name or number of the new port and then add the new port. + * + * Port names are stored as-is but they are compared case-insensitive + * + * The methods that change the stored ports return a list of + * PortChangeEvents that represent the changes that have been applied + * to the port list so that IOFSwitchListeners can be notified about the + * changes. + * + * Implementation notes: + * - We keep several different representations of the ports to allow for + * fast lookups + * - Ports are stored in unchangeable lists. When a port is modified new + * data structures are allocated. + * - We use a read-write-lock for synchronization, so multiple readers are + * allowed. + */ + protected class PortManager { + private final ReentrantReadWriteLock lock; + private List<ImmutablePort> portList; + private List<ImmutablePort> enabledPortList; + private List<Short> enabledPortNumbers; + private Map<Short,ImmutablePort> portsByNumber; + private Map<String,ImmutablePort> portsByName; + + + + + public PortManager() { + this.lock = new ReentrantReadWriteLock(); + this.portList = Collections.emptyList(); + this.enabledPortList = Collections.emptyList(); + this.enabledPortNumbers = Collections.emptyList(); + this.portsByName = Collections.emptyMap(); + this.portsByNumber = Collections.emptyMap(); + } + + /** + * Set the internal data structure storing this switch's port + * to the ports specified by newPortsByNumber + * + * CALLER MUST HOLD WRITELOCK + * + * @param newPortsByNumber + * @throws IllegaalStateException if called without holding the + * writelock + */ + private void updatePortsWithNewPortsByNumber( + Map<Short,ImmutablePort> newPortsByNumber) { + if (!lock.writeLock().isHeldByCurrentThread()) { + throw new IllegalStateException("Method called without " + + "holding writeLock"); + } + Map<String,ImmutablePort> newPortsByName = + new HashMap<String, ImmutablePort>(); + List<ImmutablePort> newPortList = + new ArrayList<ImmutablePort>(); + List<ImmutablePort> newEnabledPortList = + new ArrayList<ImmutablePort>(); + List<Short> newEnabledPortNumbers = new ArrayList<Short>(); + + for(ImmutablePort p: newPortsByNumber.values()) { + newPortList.add(p); + newPortsByName.put(p.getName().toLowerCase(), p); + if (p.isEnabled()) { + newEnabledPortList.add(p); + newEnabledPortNumbers.add(p.getPortNumber()); + } + } + portsByName = Collections.unmodifiableMap(newPortsByName); + portsByNumber = + Collections.unmodifiableMap(newPortsByNumber); + enabledPortList = + Collections.unmodifiableList(newEnabledPortList); + enabledPortNumbers = + Collections.unmodifiableList(newEnabledPortNumbers); + portList = Collections.unmodifiableList(newPortList); + } + + /** + * Handle a OFPortStatus delete message for the given port. + * Updates the internal port maps/lists of this switch and returns + * the PortChangeEvents caused by the delete. If the given port + * exists as it, it will be deleted. If the name<->number for the + * given port is inconsistent with the ports stored by this switch + * the method will delete all ports with the number or name of the + * given port. + * + * This method will increment error/warn counters and log + * + * @param delPort the port from the port status message that should + * be deleted. + * @return ordered collection of port changes applied to this switch + */ + private OrderedCollection<PortChangeEvent> + handlePortStatusDelete(ImmutablePort delPort) { + lock.writeLock().lock(); + OrderedCollection<PortChangeEvent> events = + new LinkedHashSetWrapper<PortChangeEvent>(); + try { + Map<Short,ImmutablePort> newPortByNumber = + new HashMap<Short, ImmutablePort>(portsByNumber); + ImmutablePort prevPort = + portsByNumber.get(delPort.getPortNumber()); + if (prevPort == null) { + // so such port. Do we have a port with the name? + prevPort = portsByName.get(delPort.getName()); + if (prevPort != null) { + newPortByNumber.remove(prevPort.getPortNumber()); + events.add(new PortChangeEvent(prevPort, + PortChangeType.DELETE)); + } + } else if (prevPort.getName().equals(delPort.getName())) { + // port exists with consistent name-number mapping + newPortByNumber.remove(delPort.getPortNumber()); + events.add(new PortChangeEvent(delPort, + PortChangeType.DELETE)); + } else { + // port with same number exists but its name differs. This + // is weird. The best we can do is to delete the existing + // port(s) that have delPort's name and number. + newPortByNumber.remove(delPort.getPortNumber()); + events.add(new PortChangeEvent(prevPort, + PortChangeType.DELETE)); + // is there another port that has delPort's name? + prevPort = portsByName.get(delPort.getName().toLowerCase()); + if (prevPort != null) { + newPortByNumber.remove(prevPort.getPortNumber()); + events.add(new PortChangeEvent(prevPort, + PortChangeType.DELETE)); + } + } + updatePortsWithNewPortsByNumber(newPortByNumber); + return events; + } finally { + lock.writeLock().unlock(); + } + } + + /** + * Handle a OFPortStatus message, update the internal data structures + * that store ports and return the list of OFChangeEvents. + * + * This method will increment error/warn counters and log + * + * @param ps + * @return + */ + public OrderedCollection<PortChangeEvent> handlePortStatusMessage(OFPortStatus ps) { + if (ps == null) { + throw new NullPointerException("OFPortStatus message must " + + "not be null"); + } + lock.writeLock().lock(); + try { + ImmutablePort port = + ImmutablePort.fromOFPhysicalPort(ps.getDesc()); + OFPortReason reason = OFPortReason.fromReasonCode(ps.getReason()); + if (reason == null) { + throw new IllegalArgumentException("Unknown PortStatus " + + "reason code " + ps.getReason()); + } + + if (log.isDebugEnabled()) { + log.debug("Handling OFPortStatus: {} for {}", + reason, port.toBriefString()); + } + + if (reason == OFPortReason.OFPPR_DELETE) + return handlePortStatusDelete(port); + + // We handle ADD and MODIFY the same way. Since OpenFlow + // doesn't specify what uniquely identifies a port the + // notion of ADD vs. MODIFY can also be hazy. So we just + // compare the new port to the existing ones. + Map<Short,ImmutablePort> newPortByNumber = + new HashMap<Short, ImmutablePort>(portsByNumber); + OrderedCollection<PortChangeEvent> events = getSinglePortChanges(port); + for (PortChangeEvent e: events) { + switch(e.type) { + case DELETE: + newPortByNumber.remove(e.port.getPortNumber()); + break; + case ADD: + if (reason != OFPortReason.OFPPR_ADD) { + // weird case + } + // fall through + case DOWN: + case OTHER_UPDATE: + case UP: + // update or add the port in the map + newPortByNumber.put(e.port.getPortNumber(), e.port); + break; + } + } + updatePortsWithNewPortsByNumber(newPortByNumber); + return events; + } finally { + lock.writeLock().unlock(); + } + + } + + /** + * Given a new or modified port newPort, returns the list of + * PortChangeEvents to "transform" the current ports stored by + * this switch to include / represent the new port. The ports stored + * by this switch are <b>NOT</b> updated. + * + * This method acquires the readlock and is thread-safe by itself. + * Most callers will need to acquire the write lock before calling + * this method though (if the caller wants to update the ports stored + * by this switch) + * + * @param newPort the new or modified port. + * @return the list of changes + */ + public OrderedCollection<PortChangeEvent> + getSinglePortChanges(ImmutablePort newPort) { + lock.readLock().lock(); + try { + OrderedCollection<PortChangeEvent> events = + new LinkedHashSetWrapper<PortChangeEvent>(); + // Check if we have a port by the same number in our + // old map. + ImmutablePort prevPort = + portsByNumber.get(newPort.getPortNumber()); + if (newPort.equals(prevPort)) { + // nothing has changed + return events; + } + + if (prevPort != null && + prevPort.getName().equals(newPort.getName())) { + // A simple modify of a exiting port + // A previous port with this number exists and it's name + // also matches the new port. Find the differences + if (prevPort.isEnabled() && !newPort.isEnabled()) { + events.add(new PortChangeEvent(newPort, + PortChangeType.DOWN)); + } else if (!prevPort.isEnabled() && newPort.isEnabled()) { + events.add(new PortChangeEvent(newPort, + PortChangeType.UP)); + } else { + events.add(new PortChangeEvent(newPort, + PortChangeType.OTHER_UPDATE)); + } + return events; + } + + if (prevPort != null) { + // There exists a previous port with the same port + // number but the port name is different (otherwise we would + // never have gotten here) + // Remove the port. Name-number mapping(s) have changed + events.add(new PortChangeEvent(prevPort, + PortChangeType.DELETE)); + } + + // We now need to check if there exists a previous port sharing + // the same name as the new/updated port. + prevPort = portsByName.get(newPort.getName().toLowerCase()); + if (prevPort != null) { + // There exists a previous port with the same port + // name but the port number is different (otherwise we + // never have gotten here). + // Remove the port. Name-number mapping(s) have changed + events.add(new PortChangeEvent(prevPort, + PortChangeType.DELETE)); + } + + // We always need to add the new port. Either no previous port + // existed or we just deleted previous ports with inconsistent + // name-number mappings + events.add(new PortChangeEvent(newPort, PortChangeType.ADD)); + return events; + } finally { + lock.readLock().unlock(); + } + } + + /** + * Compare the current ports of this switch to the newPorts list and + * return the changes that would be applied to transfort the current + * ports to the new ports. No internal data structures are updated + * see {@link #compareAndUpdatePorts(List, boolean)} + * + * @param newPorts the list of new ports + * @return The list of differences between the current ports and + * newPortList + */ + public OrderedCollection<PortChangeEvent> + comparePorts(Collection<ImmutablePort> newPorts) { + return compareAndUpdatePorts(newPorts, false); + } + + /** + * Compare the current ports of this switch to the newPorts list and + * return the changes that would be applied to transform the current + * ports to the new ports. No internal data structures are updated + * see {@link #compareAndUpdatePorts(List, boolean)} + * + * @param newPorts the list of new ports + * @return The list of differences between the current ports and + * newPortList + */ + public OrderedCollection<PortChangeEvent> + updatePorts(Collection<ImmutablePort> newPorts) { + return compareAndUpdatePorts(newPorts, true); + } + + /** + * Compare the current ports stored in this switch instance with the + * new port list given and return the differences in the form of + * PortChangeEvents. If the doUpdate flag is true, newPortList will + * replace the current list of this switch (and update the port maps) + * + * Implementation note: + * Since this method can optionally modify the current ports and + * since it's not possible to upgrade a read-lock to a write-lock + * we need to hold the write-lock for the entire operation. If this + * becomes a problem and if compares() are common we can consider + * splitting in two methods but this requires lots of code duplication + * + * @param newPorts the list of new ports. + * @param doUpdate If true the newPortList will replace the current + * port list for this switch. If false this switch will not be changed. + * @return The list of differences between the current ports and + * newPorts + * @throws NullPointerException if newPortsList is null + * @throws IllegalArgumentException if either port names or port numbers + * are duplicated in the newPortsList. + */ + private OrderedCollection<PortChangeEvent> compareAndUpdatePorts( + Collection<ImmutablePort> newPorts, + boolean doUpdate) { + if (newPorts == null) { + throw new NullPointerException("newPortsList must not be null"); + } + lock.writeLock().lock(); + try { + OrderedCollection<PortChangeEvent> events = + new LinkedHashSetWrapper<PortChangeEvent>(); + + Map<Short,ImmutablePort> newPortsByNumber = + new HashMap<Short, ImmutablePort>(); + Map<String,ImmutablePort> newPortsByName = + new HashMap<String, ImmutablePort>(); + List<ImmutablePort> newEnabledPortList = + new ArrayList<ImmutablePort>(); + List<Short> newEnabledPortNumbers = + new ArrayList<Short>(); + List<ImmutablePort> newPortsList = + new ArrayList<ImmutablePort>(newPorts); + + for (ImmutablePort p: newPortsList) { + if (p == null) { + throw new NullPointerException("portList must not " + + "contain null values"); + } + + // Add the port to the new maps and lists and check + // that every port is unique + ImmutablePort duplicatePort; + duplicatePort = newPortsByNumber.put(p.getPortNumber(), p); + if (duplicatePort != null) { + String msg = String.format("Cannot have two ports " + + "with the same number: %s <-> %s", + p.toBriefString(), + duplicatePort.toBriefString()); + throw new IllegalArgumentException(msg); + } + duplicatePort = + newPortsByName.put(p.getName().toLowerCase(), p); + if (duplicatePort != null) { + String msg = String.format("Cannot have two ports " + + "with the same name: %s <-> %s", + p.toBriefString(), + duplicatePort.toBriefString()); + throw new IllegalArgumentException(msg); + } + if (p.isEnabled()) { + newEnabledPortList.add(p); + newEnabledPortNumbers.add(p.getPortNumber()); + } + + // get changes + events.addAll(getSinglePortChanges(p)); + } + // find deleted ports + // We need to do this after looping through all the new ports + // to we can handle changed name<->number mappings correctly + // We could pull it into the loop of we address this but + // it's probably not worth it + for (ImmutablePort oldPort: this.portList) { + if (!newPortsByNumber.containsKey(oldPort.getPortNumber())) { + PortChangeEvent ev = + new PortChangeEvent(oldPort, + PortChangeType.DELETE); + events.add(ev); + } + } + + + if (doUpdate) { + portsByName = Collections.unmodifiableMap(newPortsByName); + portsByNumber = + Collections.unmodifiableMap(newPortsByNumber); + enabledPortList = + Collections.unmodifiableList(newEnabledPortList); + enabledPortNumbers = + Collections.unmodifiableList(newEnabledPortNumbers); + portList = Collections.unmodifiableList(newPortsList); + } + return events; + } finally { + lock.writeLock().unlock(); + } + } + + public ImmutablePort getPort(String name) { + if (name == null) { + throw new NullPointerException("Port name must not be null"); + } + lock.readLock().lock(); + try { + return portsByName.get(name.toLowerCase()); + } finally { + lock.readLock().unlock(); + } + } + + public ImmutablePort getPort(Short portNumber) { + lock.readLock().lock(); + try { + return portsByNumber.get(portNumber); + } finally { + lock.readLock().unlock(); + } + } + + public List<ImmutablePort> getPorts() { + lock.readLock().lock(); + try { + return portList; + } finally { + lock.readLock().unlock(); + } + } + + public List<ImmutablePort> getEnabledPorts() { + lock.readLock().lock(); + try { + return enabledPortList; + } finally { + lock.readLock().unlock(); + } + } + + public List<Short> getEnabledPortNumbers() { + lock.readLock().lock(); + try { + return enabledPortNumbers; + } finally { + lock.readLock().unlock(); + } + } + } + + + @Override + public boolean attributeEquals(String name, Object other) { + Object attr = this.attributes.get(name); + if (attr == null) + return false; + return attr.equals(other); + } + + + @Override + public Object getAttribute(String name) { + // returns null if key doesn't exist + return this.attributes.get(name); + } + + @Override + public void setAttribute(String name, Object value) { + this.attributes.put(name, value); + return; + } + + @Override + public Object removeAttribute(String name) { + return this.attributes.remove(name); + } + + @Override + public boolean hasAttribute(String name) { + return this.attributes.containsKey(name); + } + + @Override + @JsonIgnore + public void setChannel(Channel channel) { + this.channel = channel; + } + + // For driver subclass to set throttling + protected void enableWriteThrottle(boolean enable) { + this.writeThrottleEnabled = enable; + } + + @Override + public boolean isWriteThrottleEnabled() { + return this.writeThrottleEnabled; + } + + @Override + @LogMessageDocs({ + @LogMessageDoc(level="WARN", + message="Drop throttled OF message to switch {switch}", + explanation="The controller is sending more messages" + + "than the switch can handle. Some messages are dropped" + + "to prevent switch outage", + recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) + }) + public void writeThrottled(OFMessage m, FloodlightContext bc) + throws IOException { + if (channel == null || !isConnected()) + return; + /** + * By default, channel uses an unbounded send queue. Enable throttling + * prevents the queue from growing big. + * + * channel.isWritable() returns true when queue length is less than + * high water mark (64 kbytes). Once exceeded, isWritable() becomes + * false after queue length drops below low water mark (32 kbytes). + */ + if (!writeThrottleEnabled || channel.isWritable()) { + write(m, bc); + } else { + // Let logback duplicate filtering take care of excessive logs + ctrSwitchWriteDrops.updateCounterNoFlush(); + log.warn("Drop throttled OF message to switch {}", this); + } + } + + @Override + public void writeThrottled(List<OFMessage> msglist, FloodlightContext bc) + throws IOException { + if (!writeThrottleEnabled || channel.isWritable()) { + write(msglist, bc); + } else { + // Let logback duplicate filtering take care of excessive logs + ctrSwitchWriteDrops.updateCounterNoFlush(msglist.size()); + log.warn("Drop throttled OF messages to switch {}", this); + } + } + + @Override + public void write(OFMessage m, FloodlightContext bc) { + if (channel == null || !isConnected()) + return; + //throws IOException { + Map<IOFSwitch,List<OFMessage>> msg_buffer_map = local_msg_buffer.get(); + List<OFMessage> msg_buffer = msg_buffer_map.get(this); + if (msg_buffer == null) { + msg_buffer = new ArrayList<OFMessage>(); + msg_buffer_map.put(this, msg_buffer); + } + + this.floodlightProvider.handleOutgoingMessage(this, m, bc); + msg_buffer.add(m); + + if ((msg_buffer.size() >= Controller.BATCH_MAX_SIZE) || + ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) { + this.write(msg_buffer); + msg_buffer.clear(); + } + } + @Override + @LogMessageDoc(level="WARN", + message="Sending OF message that modifies switch " + + "state while in the slave role: {switch}", + explanation="An application has sent a message to a switch " + + "that is not valid when the switch is in a slave role", + recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) + public void write(List<OFMessage> msglist, + FloodlightContext bc) { + if (channel == null || !isConnected()) + return; + for (OFMessage m : msglist) { + if (role == Role.SLAVE) { + switch (m.getType()) { + case PACKET_OUT: + case FLOW_MOD: + case PORT_MOD: + log.warn("Sending OF message that modifies switch " + + "state while in the slave role: {}", + m.getType().name()); + break; + default: + break; + } + } + this.floodlightProvider.handleOutgoingMessage(this, m, bc); + } + this.write(msglist); + } + + /** + * Not callable by writers, but allow IOFSwitch implementation to override + * @param msglist + * @throws IOException + */ + protected void write(List<OFMessage> msglist) { + if (channel == null || !isConnected()) + return; + this.channel.write(msglist); + } + + @Override + public void disconnectOutputStream() { + if (channel == null) + return; + channel.close(); + } + + @Override + @JsonIgnore + public void setFeaturesReply(OFFeaturesReply featuresReply) { + if (stringId == null) { + /* ports are updated via port status message, so we + * only fill in ports on initial connection. + */ + List<ImmutablePort> immutablePorts = ImmutablePort + .immutablePortListOf(featuresReply.getPorts()); + portManager.updatePorts(immutablePorts); + } + this.datapathId = featuresReply.getDatapathId(); + this.stringId = HexString.toHexString(featuresReply.getDatapathId()); + this.capabilities = featuresReply.getCapabilities(); + this.buffers = featuresReply.getBuffers(); + this.actions = featuresReply.getActions(); + this.tables = featuresReply.getTables(); +} + + @Override + @JsonIgnore + public Collection<ImmutablePort> getEnabledPorts() { + return portManager.getEnabledPorts(); + } + + @Override + @JsonIgnore + public Collection<Short> getEnabledPortNumbers() { + return portManager.getEnabledPortNumbers(); + } + + @Override + public ImmutablePort getPort(short portNumber) { + return portManager.getPort(portNumber); + } + + @Override + public ImmutablePort getPort(String portName) { + return portManager.getPort(portName); + } + + @Override + @JsonIgnore + public OrderedCollection<PortChangeEvent> + processOFPortStatus(OFPortStatus ps) { + return portManager.handlePortStatusMessage(ps); + } + + @Override + @JsonProperty("ports") + public Collection<ImmutablePort> getPorts() { + return portManager.getPorts(); + } + + @Override + public OrderedCollection<PortChangeEvent> + comparePorts(Collection<ImmutablePort> ports) { + return portManager.comparePorts(ports); + } + + @Override + @JsonIgnore + public OrderedCollection<PortChangeEvent> + setPorts(Collection<ImmutablePort> ports) { + return portManager.updatePorts(ports); + } + + @Override + public boolean portEnabled(short portNumber) { + ImmutablePort p = portManager.getPort(portNumber); + if (p == null) return false; + return p.isEnabled(); + } + + @Override + public boolean portEnabled(String portName) { + ImmutablePort p = portManager.getPort(portName); + if (p == null) return false; + return p.isEnabled(); + } + + @Override + @JsonSerialize(using=DPIDSerializer.class) + @JsonProperty("dpid") + public long getId() { + if (this.stringId == null) + throw new RuntimeException("Features reply has not yet been set"); + return this.datapathId; + } + + @JsonIgnore + @Override + public String getStringId() { + return stringId; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + String channelString = + (channel != null) ? channel.getRemoteAddress().toString() : + "?"; + return "OFSwitchBase [" + channelString + " DPID[" + ((stringId != null) ? stringId : "?") + "]]"; + } + + @Override + public ConcurrentMap<Object, Object> getAttributes() { + return this.attributes; + } + + @Override + public Date getConnectedSince() { + return connectedSince; + } + + @JsonIgnore + @Override + public int getNextTransactionId() { + return this.transactionIdSource.incrementAndGet(); + } + + @Override + public void sendStatsQuery(OFStatisticsRequest request, int xid, + IOFMessageListener caller) throws IOException { + request.setXid(xid); + this.iofMsgListenersMap.put(xid, caller); + List<OFMessage> msglist = new ArrayList<OFMessage>(1); + msglist.add(request); + this.write(msglist); + return; + } + + @Override + public Future<List<OFStatistics>> queryStatistics(OFStatisticsRequest request) throws IOException { + request.setXid(getNextTransactionId()); + OFStatisticsFuture future = new OFStatisticsFuture(threadPool, this, request.getXid()); + this.statsFutureMap.put(request.getXid(), future); + List<OFMessage> msglist = new ArrayList<OFMessage>(1); + msglist.add(request); + this.write(msglist); + return future; + } + + @Override + public void deliverStatisticsReply(OFStatisticsReply reply) { + checkForTableStats(reply); + OFStatisticsFuture future = this.statsFutureMap.get(reply.getXid()); + if (future != null) { + future.deliverFuture(this, reply); + // The future will ultimately unregister itself and call + // cancelStatisticsReply + return; + } + /* Transaction id was not found in statsFutureMap.check the other map */ + IOFMessageListener caller = this.iofMsgListenersMap.get(reply.getXid()); + if (caller != null) { + caller.receive(this, reply, null); + } + } + + @LogMessageDocs({ + @LogMessageDoc(level="INFO", + message="Switch {switch} flow table is full", + explanation="The switch flow table at least 98% full, " + + "this requires attention if using reactive flow setup"), + @LogMessageDoc(level="INFO", + message="Switch {switch} flow table capacity back to normal", + explanation="The switch flow table is less than 90% full") + }) + private void checkForTableStats(OFStatisticsReply statReply) { + if (statReply.getStatisticType() != OFStatisticsType.TABLE) { + return; + } + List<? extends OFStatistics> stats = statReply.getStatistics(); + // Assume a single table only + OFStatistics stat = stats.get(0); + if (stat instanceof OFTableStatistics) { + OFTableStatistics tableStat = (OFTableStatistics) stat; + int activeCount = tableStat.getActiveCount(); + int maxEntry = tableStat.getMaximumEntries(); + log.debug("Switch {} active entries {} max entries {}", + new Object[] { this.stringId, activeCount, maxEntry}); + int percentFull = activeCount * 100 / maxEntry; + if (flowTableFull && percentFull < 90) { + log.info("Switch {} flow table capacity is back to normal", + toString()); + floodlightProvider.addSwitchEvent(this.datapathId, + "SWITCH_FLOW_TABLE_NORMAL < 90% full", false); + } else if (percentFull >= 98) { + log.info("Switch {} flow table is almost full", toString()); + floodlightProvider.addSwitchEvent(this.datapathId, + "SWITCH_FLOW_TABLE_ALMOST_FULL >= 98% full", false); + } + } + } + + + @Override + public void cancelStatisticsReply(int transactionId) { + if (null == this.statsFutureMap.remove(transactionId)) { + this.iofMsgListenersMap.remove(transactionId); + } + } + + @Override + public void cancelAllStatisticsReplies() { + /* we don't need to be synchronized here. Even if another thread + * modifies the map while we're cleaning up the future will eventuall + * timeout */ + for (OFStatisticsFuture f : statsFutureMap.values()) { + f.cancel(true); + } + statsFutureMap.clear(); + iofMsgListenersMap.clear(); + } + + + /** + * @param floodlightProvider the floodlightProvider to set + */ + @JsonIgnore + public void setFloodlightProvider( + IFloodlightProviderService floodlightProvider) { + this.floodlightProvider = floodlightProvider; + } + + @Override + @JsonIgnore + public void setThreadPoolService(IThreadPoolService tp) { + this.threadPool = tp; + } + + @Override + @JsonIgnore + public void setDebugCounterService(IDebugCounterService debugCounters) + throws CounterException { + this.debugCounters = debugCounters; + registerOverloadCounters(); + } + + @JsonIgnore + @Override + public boolean isConnected() { + // no lock needed since we use volatile + return connected; + } + + @JsonIgnore + @Override + public boolean isActive() { + // no lock needed since we use volatile + return isConnected() && this.role == Role.MASTER; + } + + @Override + @JsonIgnore + public void setConnected(boolean connected) { + // No lock needed since we use volatile + if (connected && this.connectedSince == null) + this.connectedSince = new Date(); + else if (!connected) + this.connectedSince = null; + this.connected = connected; + } + + @Override + public Role getHARole() { + return role; + } + + @JsonIgnore + @Override + public void setHARole(Role role) { + this.role = role; + } + + @LogMessageDoc(level="INFO", + message="Switch {switch} flow cleared", + explanation="The switch flow table has been cleared, " + + "this normally happens on switch connection") + @Override + public void clearAllFlowMods() { + if (channel == null || !isConnected()) + return; + // Delete all pre-existing flows + log.info("Clearing all flows on switch {}", this); + OFMatch match = new OFMatch().setWildcards(OFMatch.OFPFW_ALL); + OFMessage fm = ((OFFlowMod) floodlightProvider.getOFMessageFactory() + .getMessage(OFType.FLOW_MOD)) + .setMatch(match) + .setCommand(OFFlowMod.OFPFC_DELETE) + .setOutPort(OFPort.OFPP_NONE) + .setLength(U16.t(OFFlowMod.MINIMUM_LENGTH)); + fm.setXid(getNextTransactionId()); + OFMessage barrierMsg = floodlightProvider.getOFMessageFactory().getMessage( + OFType.BARRIER_REQUEST); + barrierMsg.setXid(getNextTransactionId()); + List<OFMessage> msglist = new ArrayList<OFMessage>(2); + msglist.add(fm); + msglist.add(barrierMsg); + channel.write(msglist); + } + + @Override + public boolean updateBroadcastCache(Long entry, Short port) { + if (timedCache.update(entry)) { + AtomicLong count = portBroadcastCacheHitMap.get(port); + if(count == null) { + AtomicLong newCount = new AtomicLong(0); + AtomicLong retrieved; + if((retrieved = portBroadcastCacheHitMap.putIfAbsent(port, newCount)) == null ) { + count = newCount; + } else { + count = retrieved; + } + } + count.incrementAndGet(); + return true; + } else { + return false; + } + } + + @Override + @JsonIgnore + public Map<Short, Long> getPortBroadcastHits() { + Map<Short, Long> res = new HashMap<Short, Long>(); + for (Map.Entry<Short, AtomicLong> entry : portBroadcastCacheHitMap.entrySet()) { + res.put(entry.getKey(), entry.getValue().get()); + } + return res; + } + + @Override + public void flush() { + Map<IOFSwitch,List<OFMessage>> msg_buffer_map = local_msg_buffer.get(); + List<OFMessage> msglist = msg_buffer_map.get(this); + if ((msglist != null) && (msglist.size() > 0)) { + /* ============================ BIG CAVEAT =============================== + * This code currently works, but relies on undocumented behavior of + * netty. + * + * The method org.jboss.netty.channel.Channel.write(Object) + * (invoked from this.write(List<OFMessage> msg) is currently + * documented to be <emph>asynchronous</emph>. If the method /were/ truely + * asynchronous, this would break our code (because we are clearing the + * msglist right after calling write. + * + * For now, Netty actually invokes the conversion pipeline before doing + * anything asynchronous, so we are safe. But we should probably change + * that behavior. + */ + this.write(msglist); + msglist.clear(); + } + } + + public static void flush_all() { + Map<IOFSwitch,List<OFMessage>> msg_buffer_map = local_msg_buffer.get(); + for (IOFSwitch sw : msg_buffer_map.keySet()) { + sw.flush(); + } + } + + + /** + * Get the IP Address for the switch + * @return the inet address + */ + @Override + @JsonSerialize(using=ToStringSerializer.class) + public SocketAddress getInetAddress() { + if (channel == null) + return null; + return channel.getRemoteAddress(); + } + + @Override + public Future<OFFeaturesReply> querySwitchFeaturesReply() + throws IOException { + OFMessage request = + floodlightProvider.getOFMessageFactory(). + getMessage(OFType.FEATURES_REQUEST); + request.setXid(getNextTransactionId()); + OFFeaturesReplyFuture future = + new OFFeaturesReplyFuture(threadPool, this, request.getXid()); + this.featuresFutureMap.put(request.getXid(), future); + List<OFMessage> msglist = new ArrayList<OFMessage>(1); + msglist.add(request); + this.write(msglist); + return future; + } + + @Override + public void deliverOFFeaturesReply(OFMessage reply) { + OFFeaturesReplyFuture future = this.featuresFutureMap.get(reply.getXid()); + if (future != null) { + future.deliverFuture(this, reply); + // The future will ultimately unregister itself and call + // cancelFeaturesReply + return; + } + log.error("Switch {}: received unexpected featureReply", this); + } + + @Override + public void cancelFeaturesReply(int transactionId) { + this.featuresFutureMap.remove(transactionId); + } + + + @Override + public int getBuffers() { + return buffers; + } + + + @Override + public int getActions() { + return actions; + } + + + @Override + public int getCapabilities() { + return capabilities; + } + + + @Override + public byte getTables() { + return tables; + } + + @Override + public OFDescriptionStatistics getDescriptionStatistics() { + return new OFDescriptionStatistics(description); + } + + + @Override + public void setFloodlightProvider(Controller controller) { + floodlightProvider = controller; + } + + + /** + * For switch drivers to set thresholds, all rates in per second + * @param pktInHigh - above this start throttling + * @param pktInLow - below this stop throttling + * @param pktInPerMac - block host if unique pktIn rate reaches this + * @param pktInPerPort - block port if unique pktIn rate reaches this + */ + @JsonIgnore + protected void setInputThrottleThresholds(int pktInHigh, int pktInLow, + int pktInPerMac, int pktInPerPort) { + packetInRateThresholdHigh = pktInHigh; + packetInRateThresholdLow = pktInLow; + packetInRatePerMacThreshold = pktInPerMac; + packetInRatePerPortThreshold = pktInPerPort; + } + + /** + * Return if switch has exceeded the high threshold of packet in rate. + * @return + */ + @Override + public boolean isOverloaded() { + return packetInThrottleEnabled; + } + + /** + * Determine if this message should be dropped. + * + * We compute the current rate by taking a timestamp every 100 messages. + * Could change to a more complex scheme if more accuracy is needed. + * + * Enable throttling if the rate goes above packetInRateThresholdHigh + * Disable throttling when the rate drops below packetInRateThresholdLow + * + * While throttling is enabled, we do the following: + * - Remove duplicate packetIn's mapped to the same OFMatch + * - After filtering, if packetIn rate per host (mac) is above + * packetInRatePerMacThreshold, push a flow mod to block mac on port + * - After filtering, if packetIn rate per port is above + * packetInRatePerPortThreshold, push a flow mod to block port + * - Allow blocking flow mods have a hard timeout and expires automatically + * + * TODO: keep a history of all events related in input throttling + * + * @param ofm + * @return + */ + @Override + public boolean inputThrottled(OFMessage ofm) { + if (ofm.getType() != OFType.PACKET_IN) { + return false; + } + ctrSwitchPktin.updateCounterNoFlush(); + // Compute current packet in rate + messageCount++; + if (messageCount % 1000 == 0) { + long now = System.currentTimeMillis(); + if (now != lastMessageTime) { + currentRate = (int) (1000000 / (now - lastMessageTime)); + lastMessageTime = now; + } else { + currentRate = Integer.MAX_VALUE; + } + } + if (!packetInThrottleEnabled) { + if (currentRate <= packetInRateThresholdHigh) { + return false; // most common case + } + enablePacketInThrottle(); + } else if (currentRate < packetInRateThresholdLow) { + disablePacketInThrottle(); + return false; + } + + // Now we are in the slow path where we need to do filtering + // First filter based on OFMatch + OFPacketIn pin = (OFPacketIn)ofm; + OFMatch match = new OFMatch(); + match.loadFromPacket(pin.getPacketData(), pin.getInPort()); + if (ofMatchCache.update(match)) { + ctrSwitchPktinDrops.updateCounterNoFlush(); + return true; + } + + // We have packet in with a distinct flow, check per mac rate + messageCountUniqueOFMatch++; + if ((messageCountUniqueOFMatch % packetInRatePerMacThreshold) == 1) { + checkPerSourceMacRate(pin); + } + + // Check per port rate + if ((messageCountUniqueOFMatch % packetInRatePerPortThreshold) == 1) { + checkPerPortRate(pin); + } + return false; + } + + /** + * We rely on the fact that packet in processing is single threaded + * per packet-in, so no locking is necessary. + */ + private void disablePacketInThrottle() { + ofMatchCache = null; + macCache = null; + macBlockedCache = null; + portCache = null; + portBlockedCache = null; + packetInThrottleEnabled = false; + floodlightProvider.addSwitchEvent(this.datapathId, + "SWITCH_OVERLOAD_THROTTLE_DISABLED ==>" + + "Pktin rate " + currentRate + "/s", false); + log.info("Packet in rate is {}, disable throttling on {}", + currentRate, this); + } + + private void enablePacketInThrottle() { + ofMatchCache = new TimedCache<OFMatch>(2048, 5000); // 5 second interval + macCache = new TimedCache<Long>(64, 1000 ); // remember last second + macBlockedCache = new TimedCache<Long>(256, 5000 ); // 5 second interval + portCache = new TimedCache<Short>(16, 1000 ); // rememeber last second + portBlockedCache = new TimedCache<Short>(64, 5000 ); // 5 second interval + packetInThrottleEnabled = true; + messageCountUniqueOFMatch = 0; + floodlightProvider.addSwitchEvent(this.datapathId, + "SWITCH_OVERLOAD_THROTTLE_ENABLED ==>" + + "Pktin rate " + currentRate + "/s", false); + log.info("Packet in rate is {}, enable throttling on {}", + currentRate, this); + } + + private void registerOverloadCounters() throws CounterException { + if (debugCountersRegistered) { + return; + } + if (debugCounters == null) { + log.error("Debug Counter Service not found"); + debugCounters = new NullDebugCounter(); + debugCountersRegistered = true; + } + // every level of the hierarchical counter has to be registered + // even if they are not used + ctrSwitch = debugCounters.registerCounter( + PACKAGE , stringId, + "Counter for this switch", + CounterType.ALWAYS_COUNT); + ctrSwitchPktin = debugCounters.registerCounter( + PACKAGE, stringId + "/pktin", + "Packet in counter for this switch", + CounterType.ALWAYS_COUNT); + ctrSwitchWrite = debugCounters.registerCounter( + PACKAGE, stringId + "/write", + "Write counter for this switch", + CounterType.ALWAYS_COUNT); + ctrSwitchPktinDrops = debugCounters.registerCounter( + PACKAGE, stringId + "/pktin/drops", + "Packet in throttle drop count", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + ctrSwitchWriteDrops = debugCounters.registerCounter( + PACKAGE, stringId + "/write/drops", + "Switch write throttle drop count", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + } + + /** + * Check if we have sampled this mac in the last second. + * Since we check every packetInRatePerMacThreshold packets, + * the presence of the mac in the macCache means the rate is + * above the threshold in a statistical sense. + * + * Take care not to block topology probing packets. Also don't + * push blocking flow mod if we have already done so within the + * last 5 seconds. + * + * @param pin + * @return + */ + private void checkPerSourceMacRate(OFPacketIn pin) { + byte[] data = pin.getPacketData(); + byte[] mac = Arrays.copyOfRange(data, 6, 12); + MACAddress srcMac = MACAddress.valueOf(mac); + short ethType = (short) (((data[12] & 0xff) << 8) + (data[13] & 0xff)); + if (ethType != Ethernet.TYPE_LLDP && ethType != Ethernet.TYPE_BSN && + macCache.update(srcMac.toLong())) { + // Check if we already pushed a flow in the last 5 seconds + if (macBlockedCache.update(srcMac.toLong())) { + return; + } + // write out drop flow per srcMac + int port = pin.getInPort(); + SwitchPort swPort = new SwitchPort(getId(), port); + ForwardingBase.blockHost(floodlightProvider, + swPort, srcMac.toLong(), (short) 5, + AppCookie.makeCookie(OFSWITCH_APP_ID, 0)); + floodlightProvider.addSwitchEvent(this.datapathId, + "SWITCH_PORT_BLOCKED_TEMPORARILY " + + "OFPort " + port + " mac " + srcMac, false); + log.info("Excessive packet in from {} on {}, block host for 5 sec", + srcMac.toString(), swPort); + } + } + + /** + * Works in a similar way as checkPerSourceMacRate(). + * + * TODO Don't block ports with links? + * + * @param pin + * @return + */ + private void checkPerPortRate(OFPacketIn pin) { + Short port = pin.getInPort(); + if (portCache.update(port)) { + // Check if we already pushed a flow in the last 5 seconds + if (portBlockedCache.update(port)) { + return; + } + // write out drop flow per port + SwitchPort swPort = new SwitchPort(getId(), port); + ForwardingBase.blockHost(floodlightProvider, + swPort, -1L, (short) 5, + AppCookie.makeCookie(OFSWITCH_APP_ID, 1)); + floodlightProvider.addSwitchEvent(this.datapathId, + "SWITCH_PORT_BLOCKED_TEMPORARILY " + + "OFPort " + port, false); + log.info("Excessive packet in from {}, block port for 5 sec", + swPort); + } + } + + @Override + @JsonIgnore + @LogMessageDoc(level="WARN", + message="Switch {switch} flow table is full", + explanation="The controller received flow table full " + + "message from the switch, could be caused by increased " + + "traffic pattern", + recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) + public void setTableFull(boolean isFull) { + if (isFull && !flowTableFull) { + floodlightProvider.addSwitchEvent(this.datapathId, + "SWITCH_FLOW_TABLE_FULL " + + "Table full error from switch", false); + log.warn("Switch {} flow table is full", stringId); + } + 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; + } + + @Override + public void startDriverHandshake() { + if (startDriverHandshakeCalled) + throw new SwitchDriverSubHandshakeAlreadyStarted(); + startDriverHandshakeCalled = true; + } + + @Override + public boolean isDriverHandshakeComplete() { + if (!startDriverHandshakeCalled) + throw new SwitchDriverSubHandshakeNotStarted(); + return true; + } + + @Override + public void processDriverHandshakeMessage(OFMessage m) { + if (startDriverHandshakeCalled) + throw new SwitchDriverSubHandshakeCompleted(m); + else + throw new SwitchDriverSubHandshakeNotStarted(); + } +} diff --git a/src/main/java/net/floodlightcontroller/core/PortChangeEvent.java b/src/main/java/net/floodlightcontroller/core/PortChangeEvent.java deleted file mode 100644 index cca90d3a35d8c1988c00f69282be51e8d123d65c..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/PortChangeEvent.java +++ /dev/null @@ -1,54 +0,0 @@ -package net.floodlightcontroller.core; - -import org.projectfloodlight.openflow.protocol.OFPortDesc; - - -/** - * Describes a change of an open flow port - */ -public class PortChangeEvent { - public final OFPortDesc port; - public final PortChangeType type; - /** - * @param port - * @param type - */ - public PortChangeEvent(OFPortDesc port, - PortChangeType type) { - this.port = port; - this.type = type; - } - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((port == null) ? 0 : port.hashCode()); - result = prime * result + ((type == null) ? 0 : type.hashCode()); - return result; - } - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - PortChangeEvent other = (PortChangeEvent) obj; - if (port == null) { - if (other.port != null) return false; - } else if (!port.equals(other.port)) return false; - if (type != other.type) return false; - return true; - } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "[" + type + " " + String.format("%s (%d)", port.getName(), port.getPortNo()) + "]"; - } -} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/PortChangeType.java b/src/main/java/net/floodlightcontroller/core/PortChangeType.java deleted file mode 100644 index 178d27b7603eebaa1c8400160843fd4e5d04be33..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/PortChangeType.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.floodlightcontroller.core; - -/** - * the type of change that happened to an open flow port - */ -public enum PortChangeType { - ADD, OTHER_UPDATE, DELETE, UP, DOWN, -} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/RoleInfo.java b/src/main/java/net/floodlightcontroller/core/RoleInfo.java index 31ec6464c95932a094717a02b18f400d98764d8f..1e6c186f759e2d7b81b529f91438d3b37210f46e 100644 --- a/src/main/java/net/floodlightcontroller/core/RoleInfo.java +++ b/src/main/java/net/floodlightcontroller/core/RoleInfo.java @@ -16,34 +16,67 @@ package net.floodlightcontroller.core; +import java.text.SimpleDateFormat; import java.util.Date; +import java.util.TimeZone; + +import net.floodlightcontroller.core.IFloodlightProviderService.Role; import com.fasterxml.jackson.annotation.JsonProperty; public class RoleInfo { - private final HARole role; - private final String roleChangeDescription; - private final Date roleChangeDateTime; + protected String role; + protected String roleChangeDescription; + protected Date roleChangeDateTime; - public RoleInfo(HARole role, String description, Date dt) { - this.role = role; + public RoleInfo() { + } + + public RoleInfo(RoleInfo o) { + role = o.role; + roleChangeDescription = o.roleChangeDescription; + roleChangeDateTime = (Date)o.roleChangeDateTime.clone(); + } + + public RoleInfo(String role) { + setRole(role); + } + + public RoleInfo(Role role, String description) { + this.role = (role != null) ? role.name() : "DISABLED"; + this.roleChangeDescription = description; + } + + public RoleInfo(Role role, String description, Date dt) { + this.role = (role != null) ? role.name() : "DISABLED"; this.roleChangeDescription = description; this.roleChangeDateTime = dt; } - public HARole getRole() { + public String getRole() { return role; } + public void setRole(String role) { + this.role = role; + } + @JsonProperty(value="change-description") public String getRoleChangeDescription() { return roleChangeDescription; } - + @JsonProperty(value="change-description") + public void setRoleChangeDescription(String roleChangeDescription) { + this.roleChangeDescription = roleChangeDescription; + } @JsonProperty(value="change-date-time") - public Date getRoleChangeDateTime() { - return roleChangeDateTime; + public String getRoleChangeDateTime() { + SimpleDateFormat formatter = + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + formatter.setTimeZone(TimeZone.getTimeZone("UTC")); + return roleChangeDateTime == null ? + "" : formatter.format(roleChangeDateTime); } -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/SwitchDescription.java b/src/main/java/net/floodlightcontroller/core/SwitchDescription.java deleted file mode 100644 index 57697d70916554da6cb73881b1237fadcd863101..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/SwitchDescription.java +++ /dev/null @@ -1,182 +0,0 @@ -package net.floodlightcontroller.core; - -import org.projectfloodlight.openflow.protocol.OFDescStatsReply; - -/** - * Encapsulates the switch information return from the description stats request - * - * @author Rob Vaterlaus <rob.vaterlaus@bigswitch.com> - */ -public class SwitchDescription { - - public static class Builder { - - private String manufacturerDescription; - private String hardwareDescription; - private String softwareDescription; - private String serialNumber; - private String datapathDescription; - - public Builder() { - manufacturerDescription = ""; - hardwareDescription = ""; - softwareDescription = ""; - serialNumber = ""; - datapathDescription = ""; - } - - public Builder setManufacturerDescription(String manufacturerDescription) { - this.manufacturerDescription = manufacturerDescription; - return this; - } - - public Builder setHardwareDescription(String hardwareDescription) { - this.hardwareDescription = hardwareDescription; - return this; - } - - public Builder setSoftwareDescription(String softwareDescription) { - this.softwareDescription = softwareDescription; - return this; - } - - public Builder setSerialNumber(String serialNumber) { - this.serialNumber = serialNumber; - return this; - } - - public Builder setDatapathDescription(String datapathDescription) { - this.datapathDescription = datapathDescription; - return this; - } - - public SwitchDescription build() { - return new SwitchDescription(manufacturerDescription, - hardwareDescription, softwareDescription, serialNumber, - datapathDescription); - } - } - - private final String manufacturerDescription; - private final String hardwareDescription; - private final String softwareDescription; - private final String serialNumber; - private final String datapathDescription; - - public static Builder builder() { - return new Builder(); - } - - // FIXME: Should make this private - public SwitchDescription() { - this("", "", "", "", ""); - } - - // FIXME: Should make this private - public SwitchDescription(String manufacturerDescription, - String hardwareDescription, String softwareDescription, - String serialNumber, String datapathDescription) { - this.manufacturerDescription = manufacturerDescription; - this.hardwareDescription = hardwareDescription; - this.softwareDescription = softwareDescription; - this.serialNumber = serialNumber; - this.datapathDescription = datapathDescription; - } - - public SwitchDescription(OFDescStatsReply descStatsReply) { - this(descStatsReply.getMfrDesc(), descStatsReply.getHwDesc(), - descStatsReply.getSwDesc(), descStatsReply.getSerialNum(), - descStatsReply.getDpDesc()); - } - - public String getManufacturerDescription() { - return manufacturerDescription; - } - - public String getHardwareDescription() { - return hardwareDescription; - } - - public String getSoftwareDescription() { - return softwareDescription; - } - - public String getSerialNumber() { - return serialNumber; - } - - public String getDatapathDescription() { - return datapathDescription; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime - * result - + ((datapathDescription == null) ? 0 : datapathDescription - .hashCode()); - result = prime - * result - + ((hardwareDescription == null) ? 0 : hardwareDescription - .hashCode()); - result = prime - * result - + ((manufacturerDescription == null) ? 0 - : manufacturerDescription.hashCode()); - result = prime * result - + ((serialNumber == null) ? 0 : serialNumber.hashCode()); - result = prime - * result - + ((softwareDescription == null) ? 0 : softwareDescription - .hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - SwitchDescription other = (SwitchDescription) obj; - if (datapathDescription == null) { - if (other.datapathDescription != null) - return false; - } else if (!datapathDescription.equals(other.datapathDescription)) - return false; - if (hardwareDescription == null) { - if (other.hardwareDescription != null) - return false; - } else if (!hardwareDescription.equals(other.hardwareDescription)) - return false; - if (manufacturerDescription == null) { - if (other.manufacturerDescription != null) - return false; - } else if (!manufacturerDescription - .equals(other.manufacturerDescription)) - return false; - if (serialNumber == null) { - if (other.serialNumber != null) - return false; - } else if (!serialNumber.equals(other.serialNumber)) - return false; - if (softwareDescription == null) { - if (other.softwareDescription != null) - return false; - } else if (!softwareDescription.equals(other.softwareDescription)) - return false; - return true; - } - - @Override - public String toString() { - return "SwitchDescription [manufacturerDescription=" + manufacturerDescription - + ", hardwareDescription=" + hardwareDescription + ", softwareDescription=" - + softwareDescription + ", serialNumber=" + serialNumber - + ", datapathDescription=" + datapathDescription + "]"; - } -} diff --git a/src/main/java/net/floodlightcontroller/core/SwitchDisconnectedException.java b/src/main/java/net/floodlightcontroller/core/SwitchDisconnectedException.java deleted file mode 100644 index 21b74ec7470071ee2fee36333c0316fd3381b5d7..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/SwitchDisconnectedException.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.floodlightcontroller.core; - -import org.projectfloodlight.openflow.types.DatapathId; - -public class SwitchDisconnectedException extends Exception { - private static final long serialVersionUID = 1L; - - private final DatapathId id; - - public SwitchDisconnectedException(DatapathId id) { - super(genMessage(id)); - this.id = id; - } - - private static String genMessage(DatapathId id) { - return String.format("Switch %s disconnected", id); - } - - public DatapathId getId() { - return id; - } - -} diff --git a/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeAlreadyStarted.java b/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeAlreadyStarted.java new file mode 100644 index 0000000000000000000000000000000000000000..eb5541bf960694a1961882c24bf79e2536b41644 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeAlreadyStarted.java @@ -0,0 +1,15 @@ +package net.floodlightcontroller.core; + +/** + * Thrown when IOFSwitch.startDriverHandshake() is called more than once. + * @author gregor + * + */ +public class SwitchDriverSubHandshakeAlreadyStarted extends + SwitchDriverSubHandshakeException { + private static final long serialVersionUID = -5491845708752443501L; + + public SwitchDriverSubHandshakeAlreadyStarted() { + super(); + } +} diff --git a/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeCompleted.java b/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeCompleted.java new file mode 100644 index 0000000000000000000000000000000000000000..ed39b5f44bc34597b41d3fc848d2b3410c7943b4 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeCompleted.java @@ -0,0 +1,19 @@ +package net.floodlightcontroller.core; + +import org.openflow.protocol.OFMessage; + +/** + * Indicates that a message was passed to a switch driver's subhandshake + * handling code but the driver has already completed the sub-handshake + * @author gregor + * + */ +public class SwitchDriverSubHandshakeCompleted + extends SwitchDriverSubHandshakeException { + private static final long serialVersionUID = -8817822245846375995L; + + public SwitchDriverSubHandshakeCompleted(OFMessage m) { + super("Sub-Handshake is already complete but received message " + + m.getType()); + } +} diff --git a/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeException.java b/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeException.java new file mode 100644 index 0000000000000000000000000000000000000000..0c0c87390468b304db6865c04a228d7e351a73dc --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeException.java @@ -0,0 +1,27 @@ +package net.floodlightcontroller.core; + +/** + * Base class for exception thrown by switch driver sub-handshake processing + * @author gregor + * + */ +public class SwitchDriverSubHandshakeException extends RuntimeException { + private static final long serialVersionUID = -6257836781419604438L; + + protected SwitchDriverSubHandshakeException() { + super(); + } + + protected SwitchDriverSubHandshakeException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + + protected SwitchDriverSubHandshakeException(String arg0) { + super(arg0); + } + + protected SwitchDriverSubHandshakeException(Throwable arg0) { + super(arg0); + } + +} diff --git a/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeNotStarted.java b/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeNotStarted.java new file mode 100644 index 0000000000000000000000000000000000000000..67ec68be2c1540f56db63632a0e82757cb92a719 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeNotStarted.java @@ -0,0 +1,16 @@ +package net.floodlightcontroller.core; + +/** + * Thrown when a switch driver's sub-handshake has not been started but an + * operation requiring the sub-handshake has been attempted. + * @author gregor + * + */ +public class SwitchDriverSubHandshakeNotStarted extends + SwitchDriverSubHandshakeException { + private static final long serialVersionUID = -5491845708752443501L; + + public SwitchDriverSubHandshakeNotStarted() { + super(); + } +} diff --git a/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeStateException.java b/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeStateException.java new file mode 100644 index 0000000000000000000000000000000000000000..1f49aea7ab60c9a58d77f7753f617b25015e4346 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/SwitchDriverSubHandshakeStateException.java @@ -0,0 +1,16 @@ +package net.floodlightcontroller.core; + +/** + * Thrown when a switch driver's sub-handshake state-machine receives an + * unexpected OFMessage and/or is in an invald state + * @author gregor + * + */ +public class SwitchDriverSubHandshakeStateException extends + SwitchDriverSubHandshakeException { + private static final long serialVersionUID = -8249926069195147051L; + + public SwitchDriverSubHandshakeStateException(String msg) { + super(msg); + } +} diff --git a/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java b/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java index ea583510844a32655d7019afe2a10d620a02c892..6284ba3c38785c2e5dc21421d012147f74c165ed 100644 --- a/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java +++ b/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java @@ -2,20 +2,16 @@ package net.floodlightcontroller.core; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.Set; -import org.projectfloodlight.openflow.protocol.OFFactory; -import org.projectfloodlight.openflow.protocol.OFFeaturesReply; -import org.projectfloodlight.openflow.protocol.OFPortDesc; +import net.floodlightcontroller.util.EnumBitmaps; +import net.floodlightcontroller.util.MACAddress; -import net.floodlightcontroller.core.SwitchDescription; - -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.util.HexString; -import org.projectfloodlight.openflow.protocol.OFCapabilities; -import org.projectfloodlight.openflow.protocol.OFActionType; +import org.openflow.protocol.OFFeaturesReply; +import org.openflow.protocol.OFPhysicalPort; +import org.openflow.protocol.OFPhysicalPort.OFPortState; +import org.openflow.protocol.statistics.OFDescriptionStatistics; +import org.openflow.util.HexString; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -31,11 +27,27 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class SwitchSyncRepresentation { public static class SyncedPort { @JsonProperty - public OFPortDesc port; + public short portNumber; + @JsonProperty + public long hardwareAddress; + @JsonProperty + public String name; + @JsonProperty + public int config; + @JsonProperty + public int state; + @JsonProperty + public int currentFeatures; + @JsonProperty + public int advertisedFeatures; + @JsonProperty + public int supportedFeatures; + @JsonProperty + public int peerFeatures; - /*public static SyncedPort fromImmutablePort(OFPortDesc p) { + public static SyncedPort fromImmutablePort(ImmutablePort p) { SyncedPort rv = new SyncedPort(); - rv.port = OFPortDesc.of(p.getPortNumber()); + rv.portNumber = p.getPortNumber(); if (p.getHardwareAddress() == null) { rv.hardwareAddress = 0; } else { @@ -54,38 +66,32 @@ public class SwitchSyncRepresentation { EnumBitmaps.toBitmap(p.getSupportedFeatures()); rv.peerFeatures = EnumBitmaps.toBitmap(p.getPeerFeatures()); return rv; - }*/ - - public static SyncedPort fromOFPortDesc(OFPortDesc ofpd) { - SyncedPort sp = new SyncedPort(); - sp.port = ofpd; - return sp; - } - - public OFPortDesc toOFPortDesc(OFFactory factory) { - OFPortDesc.Builder builder = factory.buildPortDesc(); - builder.setPortNo(port.getPortNo()); - builder.setHwAddr(port.getHwAddr()); - builder.setName(port.getName()); - builder.setConfig(port.getConfig()); - builder.setState(port.getState()); - builder.setCurr(port.getCurr()); - builder.setAdvertised(port.getAdvertised()); - builder.setSupported(port.getSupported()); - builder.setPeer(port.getPeer()); - return builder.build(); + } + + public OFPhysicalPort toOFPhysicalPort() { + OFPhysicalPort p = new OFPhysicalPort(); + p.setPortNumber(portNumber); + p.setHardwareAddress(MACAddress.valueOf(hardwareAddress).toBytes()); + p.setName(name); + p.setConfig(config); + p.setState(state); + p.setCurrentFeatures(currentFeatures); + p.setAdvertisedFeatures(advertisedFeatures); + p.setSupportedFeatures(supportedFeatures); + p.setPeerFeatures(peerFeatures); + return p; } } // From FeaturesReply - private final DatapathId dpid; - private final long buffers; - private final short tables; - private final Set<OFCapabilities> capabilities; - private final Set<OFActionType> actions; + private final long dpid; + private final int buffers; + private final byte tables; + private final int capabilities; + private final int actions; private final List<SyncedPort> ports; - // From OFDescStatsReply + // From OFDescriptionStatistics private final String manufacturerDescription; private final String hardwareDescription; private final String softwareDescription; @@ -109,11 +115,11 @@ public class SwitchSyncRepresentation { */ @JsonCreator public SwitchSyncRepresentation( - @JsonProperty("dpid") DatapathId dpid, + @JsonProperty("dpid") long dpid, @JsonProperty("buffers") int buffers, @JsonProperty("tables") byte tables, - @JsonProperty("capabilities") Set<OFCapabilities> capabilities, - @JsonProperty("actions") Set<OFActionType> actions, + @JsonProperty("capabilities") int capabilities, + @JsonProperty("actions") int actions, @JsonProperty("ports") List<SyncedPort> ports, @JsonProperty("manufacturerDescription") String manufacturerDescription, @JsonProperty("hardwareDescription") String hardwareDescription, @@ -141,7 +147,7 @@ public class SwitchSyncRepresentation { this.actions = sw.getActions(); this.ports = toSyncedPortList(sw.getPorts()); - SwitchDescription d = sw.getSwitchDescription(); + OFDescriptionStatistics d = sw.getDescriptionStatistics(); this.manufacturerDescription = d.getManufacturerDescription(); this.hardwareDescription = d.getHardwareDescription(); this.softwareDescription = d.getSoftwareDescription(); @@ -150,13 +156,14 @@ public class SwitchSyncRepresentation { } public SwitchSyncRepresentation(OFFeaturesReply fr, - SwitchDescription d) { + OFDescriptionStatistics d) { this.dpid = fr.getDatapathId(); - this.buffers = fr.getNBuffers(); - this.tables = fr.getNTables(); + this.buffers = fr.getBuffers(); + this.tables = fr.getTables(); this.capabilities = fr.getCapabilities(); this.actions = fr.getActions(); - this.ports = toSyncedPortList(fr.getPorts()); + this.ports = toSyncedPortList( + ImmutablePort.immutablePortListOf(fr.getPorts())); this.manufacturerDescription = d.getManufacturerDescription(); this.hardwareDescription = d.getHardwareDescription(); @@ -165,73 +172,65 @@ public class SwitchSyncRepresentation { this.datapathDescription = d.getDatapathDescription(); } - private static List<SyncedPort> toSyncedPortList(Collection<OFPortDesc> ports) { + private static List<SyncedPort> toSyncedPortList(Collection<ImmutablePort> ports) { List<SyncedPort> rv = new ArrayList<SyncedPort>(ports.size()); - for (OFPortDesc p: ports) { - rv.add(SyncedPort.fromOFPortDesc(p)); + for (ImmutablePort p: ports) { + rv.add(SyncedPort.fromImmutablePort(p)); } return rv; } - private static List<OFPortDesc> toOFPortDescList(OFFactory factory, Collection<SyncedPort> ports) { - List<OFPortDesc> rv = new ArrayList<OFPortDesc>(ports.size()); + private static List<OFPhysicalPort> toOFPhysicalPortList(Collection<SyncedPort> ports) { + List<OFPhysicalPort> rv = new ArrayList<OFPhysicalPort>(ports.size()); for (SyncedPort p: ports) { - rv.add(p.toOFPortDesc(factory)); + rv.add(p.toOFPhysicalPort()); } return rv; } @JsonIgnore - public OFFeaturesReply getFeaturesReply(OFFactory factory) { - /** - * FIXME Icky work around; if a null actions got written to storage - * then fake up an empty one so the builder() doesn't throw - * a NPE. Need to root cause why someone would write a null actions. - * This code will all be removed shortly -- needed to unblock BVS team. - */ - Set<OFActionType> workAroundActions; - if (actions != null) - workAroundActions = actions; - else - workAroundActions = Collections.<OFActionType> emptySet(); - - OFFeaturesReply featuresReply = factory.buildFeaturesReply() - .setXid(0) - .setDatapathId(dpid) - .setNBuffers(buffers) - .setNTables(tables) - .setCapabilities(capabilities) - .setActions(workAroundActions) - .setPorts(toOFPortDescList(factory, ports)) - .build(); - return featuresReply; + public OFFeaturesReply getFeaturesReply() { + OFFeaturesReply fr = new OFFeaturesReply(); + fr.setDatapathId(dpid); + fr.setBuffers(buffers); + fr.setTables(tables); + fr.setCapabilities(capabilities); + fr.setActions(actions); + fr.setPorts(toOFPhysicalPortList(ports)); + return fr; } @JsonIgnore - public SwitchDescription getDescription() { - return new SwitchDescription(manufacturerDescription, - hardwareDescription, softwareDescription, softwareDescription, - datapathDescription); + public OFDescriptionStatistics getDescription() { + OFDescriptionStatistics desc = new OFDescriptionStatistics(); + desc.setManufacturerDescription(manufacturerDescription); + desc.setHardwareDescription(hardwareDescription); + desc.setSoftwareDescription(softwareDescription); + desc.setSerialNumber(serialNumber); + desc.setDatapathDescription(datapathDescription); + return desc; } - public DatapathId getDpid() { + + + public long getDpid() { return dpid; } - public long getBuffers() { + public int getBuffers() { return buffers; } - public short getTables() { + public byte getTables() { return tables; } - public Set<OFCapabilities> getCapabilities() { + public int getCapabilities() { return capabilities; } - public Set<OFActionType> getActions() { + public int getActions() { return actions; } @@ -262,7 +261,7 @@ public class SwitchSyncRepresentation { @Override public String toString() { String dpidString; - dpidString = HexString.toHexString(dpid.getLong()); + dpidString = HexString.toHexString(dpid); return "SwitchSyncRepresentation [DPID=" + dpidString + "]"; } } diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java new file mode 100644 index 0000000000000000000000000000000000000000..7d6227d108b6a1279056b450622c05750f77e90c --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -0,0 +1,2696 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* 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 net.floodlightcontroller.core.internal; + +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import java.util.Stack; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import net.floodlightcontroller.core.FloodlightContext; +import net.floodlightcontroller.core.HAListenerTypeMarker; +import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IHAListener; +import net.floodlightcontroller.core.IInfoProvider; +import net.floodlightcontroller.core.IListener.Command; +import net.floodlightcontroller.core.IOFMessageListener; +import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.core.IOFSwitch.PortChangeEvent; +import net.floodlightcontroller.core.IOFSwitch.PortChangeType; +import net.floodlightcontroller.core.IOFSwitchDriver; +import net.floodlightcontroller.core.IOFSwitchListener; +import net.floodlightcontroller.core.IReadyForReconcileListener; +import net.floodlightcontroller.core.ImmutablePort; +import net.floodlightcontroller.core.OFSwitchBase; +import net.floodlightcontroller.core.RoleInfo; +import net.floodlightcontroller.core.SwitchSyncRepresentation; +import net.floodlightcontroller.core.annotations.LogMessageDoc; +import net.floodlightcontroller.core.annotations.LogMessageDocs; +import net.floodlightcontroller.core.module.FloodlightModuleException; +import net.floodlightcontroller.core.util.ListenerDispatcher; +import net.floodlightcontroller.core.web.CoreWebRoutable; +import net.floodlightcontroller.counter.ICounterStoreService; +import net.floodlightcontroller.debugcounter.IDebugCounter; +import net.floodlightcontroller.debugcounter.IDebugCounterService; +import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException; +import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType; +import net.floodlightcontroller.debugevent.IDebugEventService; +import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn; +import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType; +import net.floodlightcontroller.debugevent.IEventUpdater; +import net.floodlightcontroller.debugevent.NullDebugEvent; +import net.floodlightcontroller.debugevent.IDebugEventService.EventType; +import net.floodlightcontroller.debugevent.IDebugEventService.MaxEventsRegistered; +import net.floodlightcontroller.notification.INotificationManager; +import net.floodlightcontroller.notification.NotificationManagerFactory; +import net.floodlightcontroller.packet.Ethernet; +import net.floodlightcontroller.perfmon.IPktInProcessingTimeService; +import net.floodlightcontroller.restserver.IRestApiService; +import net.floodlightcontroller.storage.IResultSet; +import net.floodlightcontroller.storage.IStorageSourceListener; +import net.floodlightcontroller.storage.IStorageSourceService; +import net.floodlightcontroller.storage.StorageException; +import net.floodlightcontroller.threadpool.IThreadPoolService; +import net.floodlightcontroller.util.LoadMonitor; +import net.floodlightcontroller.util.TimedCache; + +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.group.ChannelGroup; +import org.jboss.netty.channel.group.DefaultChannelGroup; +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFType; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.statistics.OFDescriptionStatistics; +import org.openflow.util.HexString; +import org.openflow.vendor.nicira.OFNiciraVendorExtensions; +import org.sdnplatform.sync.IClosableIterator; +import org.sdnplatform.sync.IStoreClient; +import org.sdnplatform.sync.IStoreListener; +import org.sdnplatform.sync.ISyncService; +import org.sdnplatform.sync.ISyncService.Scope; +import org.sdnplatform.sync.Versioned; +import org.sdnplatform.sync.error.ObsoleteVersionException; +import org.sdnplatform.sync.error.SyncException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.bigswitch.floodlight.vendor.OFVendorActions; + + + +/** + * The main controller class. Handles all setup and network listeners + */ +public class Controller implements IFloodlightProviderService, + IStorageSourceListener, IInfoProvider { + + protected static final Logger log = LoggerFactory.getLogger(Controller.class); + protected static final INotificationManager notifier = + NotificationManagerFactory.getNotificationManager(Controller.class); + + static final String ERROR_DATABASE = + "The controller could not communicate with the system database."; + static final String SWITCH_SYNC_STORE_NAME = + Controller.class.getCanonicalName() + ".stateStore"; + + protected BasicFactory factory; + protected ConcurrentMap<OFType, + ListenerDispatcher<OFType,IOFMessageListener>> + messageListeners; + + // OFSwitch driver binding map and order + private ISwitchDriverRegistry driverRegistry; + + // The controllerNodeIPsCache maps Controller IDs to their IP address. + // It's only used by handleControllerNodeIPsChanged + protected HashMap<String, String> controllerNodeIPsCache; + + protected Set<IOFSwitchListener> switchListeners; + protected ListenerDispatcher<HAListenerTypeMarker,IHAListener> haListeners; + protected Set<IReadyForReconcileListener> readyForReconcileListeners; + protected Map<String, List<IInfoProvider>> providerMap; + protected BlockingQueue<IUpdate> updates; + + // Module dependencies + private IRestApiService restApi; + private ICounterStoreService counterStore = null; + private IDebugCounterService debugCounters; + protected IDebugEventService debugEvents; + private IStorageSourceService storageSource; + private IPktInProcessingTimeService pktinProcTime; + private IThreadPoolService threadPool; + private ScheduledExecutorService ses; + private ISyncService syncService; + private IStoreClient<Long, SwitchSyncRepresentation> storeClient; + + // Configuration options + protected String openFlowHost = null; + protected int openFlowPort = 6633; + protected int workerThreads = 0; + + + // This controller's current role that modules can use/query to decide + // if they should operate in master or slave mode. + // TODO: potentially we need to get rid of this field and modules must + // then rely on the role notifications alone... + protected volatile Role notifiedRole; + + private static final String + INITIAL_ROLE_CHANGE_DESCRIPTION = "Controller startup."; + private RoleManager roleManager; + private SwitchManager switchManager; + + private static final int DEFAULT_CONSOLIDATE_STORE_TIME_DELAY_MS = + 15*1000; // 15s + private int consolidateStoreTimeDelayMs = + DEFAULT_CONSOLIDATE_STORE_TIME_DELAY_MS; + + + // Flag to always flush flow table on switch reconnect (HA or otherwise) + private boolean alwaysClearFlowsOnSwActivate = false; + private TimedCache<Long> swConnectCache; + + // Storage table names + protected static final String CONTROLLER_TABLE_NAME = "controller_controller"; + protected static final String CONTROLLER_ID = "id"; + + protected static final String SWITCH_CONFIG_TABLE_NAME = "controller_switchconfig"; + protected static final String SWITCH_CONFIG_CORE_SWITCH = "core_switch"; + + protected static final String CONTROLLER_INTERFACE_TABLE_NAME = "controller_controllerinterface"; + protected static final String CONTROLLER_INTERFACE_ID = "id"; + protected static final String CONTROLLER_INTERFACE_CONTROLLER_ID = "controller_id"; + protected static final String CONTROLLER_INTERFACE_TYPE = "type"; + 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 = 128 * 1024; + public static final int BATCH_MAX_SIZE = 100; + protected static final boolean ALWAYS_DECODE_ETH = true; + + // Set of port name prefixes that will be classified as uplink ports, + // hence will not be autoportfast. + Set<String> uplinkPortPrefixSet; + + @Override + public Set<String> getUplinkPortPrefixSet() { + return uplinkPortPrefixSet; + } + + public void setUplinkPortPrefixSet(Set<String> prefixSet) { + this.uplinkPortPrefixSet = prefixSet; + } + + // Event IDs for debug events + protected IEventUpdater<SwitchEvent> evSwitch; + + // Load monitor for overload protection + protected final boolean overload_drop = + Boolean.parseBoolean(System.getProperty("overload_drop", "false")); + protected final LoadMonitor loadmonitor = new LoadMonitor(log); + + private class NotificationSwitchListener implements IOFSwitchListener { + + @Override + public void switchAdded(long switchId) { + notifier.postNotification("Switch " + HexString.toHexString(switchId) + " connected."); + } + + @Override + public void switchRemoved(long switchId) { + notifier.postNotification("Switch " + HexString.toHexString(switchId) + " disconnected."); + } + + @Override + public void switchActivated(long switchId) { + } + + @Override + public void switchPortChanged(long switchId, ImmutablePort port, + PortChangeType type) { + String msg = String.format("Switch %s port %s changed: %s", + HexString.toHexString(switchId), + port.getName(), + type.toString()); + notifier.postNotification(msg); + } + + @Override + public void switchChanged(long switchId) { + } + } + public static class Counters { + public static final String prefix = Controller.class.getPackage().getName(); + public IDebugCounter setRoleEqual; + public IDebugCounter setSameRole; + public IDebugCounter setRoleMaster; + public IDebugCounter remoteStoreNotification; + public IDebugCounter invalidPortsChanged; + public IDebugCounter invalidSwitchActivatedWhileSlave; + public IDebugCounter invalidStoreEventWhileMaster; + public IDebugCounter switchDisconnectedWhileSlave; + public IDebugCounter switchActivated; + public IDebugCounter errorSameSwitchReactivated; // err + public IDebugCounter switchWithSameDpidActivated; // warn + public IDebugCounter newSwitchActivated; // new switch + public IDebugCounter syncedSwitchActivated; + public IDebugCounter readyForReconcile; + public IDebugCounter newSwitchFromStore; + public IDebugCounter updatedSwitchFromStore; + public IDebugCounter switchDisconnected; + public IDebugCounter syncedSwitchRemoved; + public IDebugCounter unknownSwitchRemovedFromStore; + public IDebugCounter consolidateStoreRunCount; + public IDebugCounter consolidateStoreInconsistencies; + public IDebugCounter storeSyncError; + public IDebugCounter switchesNotReconnectingToNewMaster; + public IDebugCounter switchPortChanged; + public IDebugCounter switchOtherChange; + public IDebugCounter dispatchMessageWhileSlave; + public IDebugCounter dispatchMessage; // does this cnt make sense? more specific?? per type? count stops? + public IDebugCounter controllerNodeIpsChanged; + public IDebugCounter messageReceived; + public IDebugCounter messageInputThrottled; + public IDebugCounter switchDisconnectReadTimeout; + public IDebugCounter switchDisconnectHandshakeTimeout; + public IDebugCounter switchDisconnectIOError; + public IDebugCounter switchDisconnectParseError; + public IDebugCounter switchDisconnectSwitchStateException; + public IDebugCounter rejectedExecutionException; + public IDebugCounter switchDisconnectOtherException; + public IDebugCounter switchConnected; + public IDebugCounter unhandledMessage; + public IDebugCounter packetInWhileSwitchIsSlave; + public IDebugCounter epermErrorWhileSwitchIsMaster; + public IDebugCounter roleNotResentBecauseRolePending; + public IDebugCounter roleRequestSent; + public IDebugCounter roleReplyTimeout; + public IDebugCounter roleReplyReceived; // expected RoleReply received + public IDebugCounter roleReplyErrorUnsupported; + public IDebugCounter switchCounterRegistrationFailed; + + void createCounters(IDebugCounterService debugCounters) throws CounterException { + setRoleEqual = + debugCounters.registerCounter( + prefix, "set-role-equal", + "Controller received a role request with role of "+ + "EQUAL which is unusual", + CounterType.ALWAYS_COUNT); + setSameRole = + debugCounters.registerCounter( + prefix, "set-same-role", + "Controller received a role request for the same " + + "role the controller already had", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + + setRoleMaster = + debugCounters.registerCounter( + prefix, "set-role-master", + "Controller received a role request with role of " + + "MASTER. This counter can be at most 1.", + CounterType.ALWAYS_COUNT); + + remoteStoreNotification = + debugCounters.registerCounter( + prefix, "remote-store-notification", + "Received a notification from the sync service " + + "indicating that switch information has changed", + CounterType.ALWAYS_COUNT); + + invalidPortsChanged = + debugCounters.registerCounter( + prefix, "invalid-ports-changed", + "Received an unexpected ports changed " + + "notification while the controller was in " + + "SLAVE role.", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + + invalidSwitchActivatedWhileSlave = + debugCounters.registerCounter( + prefix, "invalid-switch-activated-while-slave", + "Received an unexpected switchActivated " + + "notification while the controller was in " + + "SLAVE role.", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + + invalidStoreEventWhileMaster = + debugCounters.registerCounter( + prefix, "invalid-store-event-while-master", + "Received an unexpected notification from " + + "the sync store while the controller was in " + + "MASTER role.", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + + switchDisconnectedWhileSlave = + debugCounters.registerCounter( + prefix, "switch-disconnected-while-slave", + "A switch disconnected and the controller was " + + "in SLAVE role.", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + + switchActivated = + debugCounters.registerCounter( + prefix, "switch-activated", + "A switch connected to this controller is now " + + "in MASTER role", + CounterType.ALWAYS_COUNT); + + errorSameSwitchReactivated = // err + debugCounters.registerCounter( + prefix, "error-same-switch-reactivated", + "A switch that was already in active state " + + "was activated again. This indicates a " + + "controller defect", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_ERROR); + + switchWithSameDpidActivated = // warn + debugCounters.registerCounter( + prefix, "switch-with-same-dpid-activated", + "A switch with the same DPID as another switch " + + "connected to the controller. This can be " + + "caused by multiple switches configured with " + + "the same DPID or by a switch reconnecting very " + + "quickly.", + CounterType.COUNT_ON_DEMAND, + IDebugCounterService.CTR_MDATA_WARN); + + newSwitchActivated = // new switch + debugCounters.registerCounter( + prefix, "new-switch-activated", + "A new switch has completed the handshake as " + + "MASTER. The switch was not known to any other " + + "controller in the cluster", + CounterType.ALWAYS_COUNT); + syncedSwitchActivated = + debugCounters.registerCounter( + prefix, "synced-switch-activated", + "A switch has completed the handshake as " + + "MASTER. The switch was known to another " + + "controller in the cluster", + CounterType.ALWAYS_COUNT); + + readyForReconcile = + debugCounters.registerCounter( + prefix, "ready-for-reconcile", + "Controller is ready for flow reconciliation " + + "after Slave to Master transition. Either all " + + "previously known switches are now active " + + "or they have timed out and have been removed." + + "This counter will be 0 or 1.", + CounterType.ALWAYS_COUNT); + + newSwitchFromStore = + debugCounters.registerCounter( + prefix, "new-switch-from-store", + "A new switch has connected to another " + + "another controller in the cluster. This " + + "controller instance has received a sync store " + + "notification for it.", + CounterType.ALWAYS_COUNT); + + updatedSwitchFromStore = + debugCounters.registerCounter( + prefix, "updated-switch-from-store", + "Information about a switch connected to " + + "another controller instance was updated in " + + "the sync store. This controller instance has " + + "received a notification for it", + CounterType.ALWAYS_COUNT); + + switchDisconnected = + debugCounters.registerCounter( + prefix, "switch-disconnected", + "FIXME: switch has disconnected", + CounterType.ALWAYS_COUNT); + + syncedSwitchRemoved = + debugCounters.registerCounter( + prefix, "synced-switch-removed", + "A switch connected to another controller " + + "instance has disconnected from the controller " + + "cluster. This controller instance has " + + "received a notification for it", + CounterType.ALWAYS_COUNT); + + unknownSwitchRemovedFromStore = + debugCounters.registerCounter( + prefix, "unknown-switch-removed-from-store", + "This controller instances has received a sync " + + "store notification that a switch has " + + "disconnected but this controller instance " + + "did not have the any information about the " + + "switch", // might be less than warning + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + + consolidateStoreRunCount = + debugCounters.registerCounter( + prefix, "consolidate-store-run-count", + "This controller has transitioned from SLAVE " + + "to MASTER and waited for switches to reconnect. " + + "The controller has finished waiting and has " + + "reconciled switch entries in the sync store " + + "with live state", + CounterType.ALWAYS_COUNT); + + consolidateStoreInconsistencies = + debugCounters.registerCounter( + prefix, "consolidate-store-inconsistencies", + "During switch sync store consolidation: " + + "Number of switches that were in the store " + + "but not otherwise known plus number of " + + "switches that were in the store previously " + + "but are now missing plus number of " + + "connected switches that were absent from " + + "the store although this controller has " + + "written them. A non-zero count " + + "indicates a brief split-brain dual MASTER " + + "situation during fail-over", + CounterType.ALWAYS_COUNT); + + storeSyncError = + debugCounters.registerCounter( + prefix, "store-sync-error", + "Number of times a sync store operation failed " + + "due to a store sync exception or an entry in " + + "in the store had invalid data.", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_ERROR); + + switchesNotReconnectingToNewMaster = + debugCounters.registerCounter( + prefix, "switches-not-reconnecting-to-new-master", + "Switches that were connected to another " + + "controller instance in the cluster but that " + + "did not reconnect to this controller after it " + + "transitioned to MASTER", // might be less than warning + CounterType.ALWAYS_COUNT); + + switchPortChanged = + debugCounters.registerCounter( + prefix, "switch-port-changed", + "Number of times switch ports have changed", + CounterType.ALWAYS_COUNT); + switchOtherChange = + debugCounters.registerCounter( + prefix, "switch-other-change", + "Number of times other information of a switch " + + "has changed.", + CounterType.ALWAYS_COUNT); + + dispatchMessageWhileSlave = + debugCounters.registerCounter( + prefix, "dispatch-message-while-slave", + "Number of times an OF message was received " + + "and supposed to be dispatched but the " + + "controller was in SLAVE role and the message " + + "was not dispatched", + CounterType.ALWAYS_COUNT); + + dispatchMessage = // does this cnt make sense? more specific?? per type? count stops? + debugCounters.registerCounter( + prefix, "dispatch-message", + "Number of times an OF message was dispatched " + + "to registered modules", + CounterType.ALWAYS_COUNT); + + controllerNodeIpsChanged = + debugCounters.registerCounter( + prefix, "controller-nodes-ips-changed", + "IP addresses of controller nodes have changed", + CounterType.ALWAYS_COUNT); + + //------------------------ + // channel handler counters. Factor them out ?? + messageReceived = + debugCounters.registerCounter( + prefix, "message-received", + "Number of OpenFlow messages received. Some of " + + "these might be throttled", + CounterType.ALWAYS_COUNT); + messageInputThrottled = + debugCounters.registerCounter( + prefix, "message-input-throttled", + "Number of OpenFlow messages that were " + + "throttled due to high load from the sender", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + // TODO: more counters in messageReceived ?? + + switchDisconnectReadTimeout = + debugCounters.registerCounter( + prefix, "switch-disconnect-read-timeout", + "Number of times a switch was disconnected due " + + "due the switch failing to send OpenFlow " + + "messages or responding to OpenFlow ECHOs", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_ERROR); + switchDisconnectHandshakeTimeout = + debugCounters.registerCounter( + prefix, "switch-disconnect-handshake-timeout", + "Number of times a switch was disconnected " + + "because it failed to complete the handshake " + + "in time.", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_ERROR); + switchDisconnectIOError = + debugCounters.registerCounter( + prefix, "switch-disconnect-io-error", + "Number of times a switch was disconnected " + + "due to IO errors on the switch connection.", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_ERROR); + switchDisconnectParseError = + debugCounters.registerCounter( + prefix, "switch-disconnect-parse-error", + "Number of times a switch was disconnected " + + "because it sent an invalid packet that could " + + "not be parsed", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_ERROR); + + switchDisconnectSwitchStateException = + debugCounters.registerCounter( + prefix, "switch-disconnect-switch-state-exception", + "Number of times a switch was disconnected " + + "because it sent messages that were invalid " + + "given the switch connection's state.", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_ERROR); + rejectedExecutionException = + debugCounters.registerCounter( + prefix, "rejected-execution-exception", + "TODO", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_ERROR); + + switchDisconnectOtherException = + debugCounters.registerCounter( + prefix, "switch-disconnect-other-exception", + "Number of times a switch was disconnected " + + "due to an exceptional situation not covered " + + "by other counters", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_ERROR); + + switchConnected = + debugCounters.registerCounter( + prefix, "switch-connected", + "Number of times a new switch connection was " + + "established", + CounterType.ALWAYS_COUNT); + + unhandledMessage = + debugCounters.registerCounter( + prefix, "unhandled-message", + "Number of times an OpenFlow message was " + + "received that the controller ignored because " + + "it was inapproriate given the switch " + + "connection's state.", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + // might be less than warning + + packetInWhileSwitchIsSlave = + debugCounters.registerCounter( + prefix, "packet-in-while-switch-is-slave", + "Number of times a packet in was received " + + "from a switch that was in SLAVE role. " + + "Possibly inidicates inconsistent roles.", + CounterType.ALWAYS_COUNT); + epermErrorWhileSwitchIsMaster = + debugCounters.registerCounter( + prefix, "eperm-error-while-switch-is-master", + "Number of times a permission error was " + + "received while the switch was in MASTER role. " + + "Possibly inidicates inconsistent roles.", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + + roleNotResentBecauseRolePending = + debugCounters.registerCounter( + prefix, "role-not-resent-because-role-pending", + "The controller tried to reestablish a role " + + "with a switch but did not do so because a " + + "previous role request was still pending", + CounterType.ALWAYS_COUNT); + roleRequestSent = + debugCounters.registerCounter( + prefix, "role-request-sent", + "Number of times the controller sent a role " + + "request to a switch.", + CounterType.ALWAYS_COUNT); + roleReplyTimeout = + debugCounters.registerCounter( + prefix, "role-reply-timeout", + "Number of times a role request message did not " + + "receive the expected reply from a switch", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + + roleReplyReceived = // expected RoleReply received + debugCounters.registerCounter( + prefix, "role-reply-received", + "Number of times the controller received the " + + "expected role reply message from a switch", + CounterType.ALWAYS_COUNT); + + roleReplyErrorUnsupported = + debugCounters.registerCounter( + prefix, "role-reply-error-unsupported", + "Number of times the controller received an " + + "error from a switch in response to a role " + + "request indicating that the switch does not " + + "support roles.", + CounterType.ALWAYS_COUNT); + + switchCounterRegistrationFailed = + debugCounters.registerCounter(prefix, + "switch-counter-registration-failed", + "Number of times the controller failed to " + + "register per-switch debug counters", + CounterType.ALWAYS_COUNT, + IDebugCounterService.CTR_MDATA_WARN); + } + } + + private Counters counters; + + Counters getCounters() { + return this.counters; + } + + /** + * A utility class to manage the <i>controller roles</i>. + * + * A utility class to manage the <i>controller roles</i> as opposed + * to the switch roles. The class manages the controllers current role, + * handles role change requests, and maintains the list of connected + * switch(-channel) so it can notify the switches of role changes. + * + * We need to ensure that every connected switch is always send the + * correct role. Therefore, switch add, sending of the intial role, and + * changing role need to use mutexes to ensure this. This has the ugly + * side-effect of requiring calls between controller and OFChannelHandler + * + * This class is fully thread safe. Its method can safely be called from + * any thread. + * + * @author gregor + * + */ + private class RoleManager { + // This role represents the role that has been set by setRole. This + // role might or might now have been notified to listeners just yet. + // This is updated by setRole. doSetRole() will use this value as + private Role role; + private String roleChangeDescription; + + // The current role info. This is updated /after/ dampening + // switches and + // listener notifications have been enqueued (but potentially before + // they have been dispatched) + private RoleInfo currentRoleInfo; + private final Set<OFChannelHandler> connectedChannelHandlers; + + /** + * @param role initial role + * @param roleChangeDescription initial value of the change description + * @throws NullPointerException if role or roleChangeDescription is null + * @throws IllegalArgumentException if role is EQUAL + */ + public RoleManager(Role role, String roleChangeDescription) { + if (role == null) + throw new NullPointerException("role must not be null"); + if (role == Role.EQUAL) + throw new IllegalArgumentException("role must not be EQUAL"); + if (roleChangeDescription == null) { + throw new NullPointerException("roleChangeDescription must " + + "not be null"); + } + + this.role = role; + this.roleChangeDescription = roleChangeDescription; + this.connectedChannelHandlers = new HashSet<OFChannelHandler>(); + this.currentRoleInfo = new RoleInfo(this.role, + this.roleChangeDescription, + new Date()); + } + + /** + * Add a newly connected OFChannelHandler. The channel handler is added + * we send the current role to the channel handler. All subsequent role + * changes will be send to all connected + * @param h The OFChannelHandler to add + */ + public synchronized void + addOFChannelHandlerAndSendRole(OFChannelHandler h) { + connectedChannelHandlers.add(h); + h.sendRoleRequest(this.role); + } + + /** + * Remove OFChannelHandler. E.g., due do disconnect. + * @param h The OFChannelHandler to remove. + */ + public synchronized void removeOFChannelHandler(OFChannelHandler h) { + connectedChannelHandlers.remove(h); + } + + /** + * Re-assert a role for the given channel handler. + * + * The caller specifies the role that should be reasserted. We only + * reassert the role if the controller's current role matches the + * reasserted role and there is no role request for the reasserted role + * pending. + * @param h The OFChannelHandler on which we should reassert. + * @param role The role to reassert + */ + public synchronized void reassertRole(OFChannelHandler h, Role role) { + // check if the requested reassertion actually makes sense + if (this.role != role) + return; + h.sendRoleRequestIfNotPending(this.role); + } + + /** + * Set the controller's new role and notify switches. + * + * This method updates the controllers current role and notifies all + * connected switches of the new role is different from the current + * role. We dampen calls to this method. See class description for + * details. + * + * @param role The new role. + * @param roleChangeDescription A textual description of why the role + * was changed. For information purposes only. + * @throws NullPointerException if role or roleChangeDescription is null + */ + public synchronized void setRole(Role role, String roleChangeDescription) { + if (role == null) + throw new NullPointerException("role must not be null"); + if (roleChangeDescription == null) { + throw new NullPointerException("roleChangeDescription must " + + "not be null"); + } + if (role == Role.EQUAL) { + counters.setRoleEqual.updateCounterWithFlush(); + log.debug("Received role request for EQUAL, setting to MASTER" + + " instead"); + role = Role.MASTER; + } + if (role == this.role) { + counters.setSameRole.updateCounterWithFlush(); + log.debug("Received role request for {} but controller is " + + "already {}. Ignoring it.", role, this.role); + return; + } + if (this.role == Role.MASTER && role == Role.SLAVE) { + log.info("Received role request to transition from MASTER to " + + " SLAVE (reason: {}). Terminating floodlight.", + roleChangeDescription); + System.exit(0); + } + + // At this point we are guaranteed that we will execute the code + // below exactly once during the lifetime of this process! And + // it will be a to MASTER transition + counters.setRoleMaster.updateCounterWithFlush(); + log.info("Received role request for {} (reason: {})." + + " Initiating transition", role, roleChangeDescription); + + this.role = role; + this.roleChangeDescription = roleChangeDescription; + + // TODO: we currently notify switches synchronously from the REST + // API handler. We could (should?) do this asynchronously. + currentRoleInfo = new RoleInfo(this.role, + this.roleChangeDescription, + new Date()); + Controller.this.switchManager.setRole(this.role); + for (OFChannelHandler h: connectedChannelHandlers) + h.sendRoleRequest(this.role); + + Controller.this.addUpdateToQueue(new HARoleUpdate(this.role)); + } + + /** + * Return the RoleInfo object describing the current role. + * + * Return the RoleInfo object describing the current role. The + * RoleInfo object is used by REST API users. We need to return + * a defensive copy. + * @return the current RoleInfo object + */ + public synchronized RoleInfo getRoleInfo() { + return new RoleInfo(currentRoleInfo); + } + } + + + /** + * This is a utility class to encapsulate code that deals with switch + * life cycles. It interacts with the sync store to read/write switches + * to/from the store and it maintains the switch maps. + * @author gregor + * + */ + private class SwitchManager implements IStoreListener<Long> { + private Role role; + private final ConcurrentHashMap<Long,IOFSwitch> activeSwitches; + private final ConcurrentHashMap<Long,IOFSwitch> syncedSwitches; + + public SwitchManager(Role role) { + this.role = role; + this.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>(); + this.syncedSwitches = new ConcurrentHashMap<Long, IOFSwitch>(); + } + + @Override + public void keysModified(Iterator<Long> keys, UpdateType type) { + if (type == UpdateType.LOCAL) { + // We only care for remote updates + return; + } + counters.remoteStoreNotification.updateCounterWithFlush(); + while(keys.hasNext()) { + Long key = keys.next(); + Versioned<SwitchSyncRepresentation> versionedSwitch = null; + try { + versionedSwitch = storeClient.get(key); + } catch (SyncException e) { + counters.storeSyncError.updateCounterWithFlush(); + log.error("Exception while retrieving switch " + + HexString.toHexString(key) + + " from sync store. Skipping", e); + continue; + } + if (log.isTraceEnabled()) { + log.trace("Reveiced switch store notification: key={}, " + + "entry={}", key, versionedSwitch.getValue()); + } + // versionedSwtich won't be null. storeClient.get() always + // returns a non-null or throws an exception + if (versionedSwitch.getValue() == null) { + switchRemovedFromStore(key); + continue; + } + SwitchSyncRepresentation storedSwitch = + versionedSwitch.getValue(); + IOFSwitch sw = getOFSwitchInstance(storedSwitch.getDescription()); + sw.setFeaturesReply(storedSwitch.getFeaturesReply()); + if (!key.equals(storedSwitch.getFeaturesReply().getDatapathId())) { + counters.storeSyncError.updateCounterWithFlush(); + log.error("Inconsistent DPIDs from switch sync store: " + + "key is {} but sw.getId() says {}. Ignoring", + HexString.toHexString(key), sw.getStringId()); + continue; + } + switchAddedToStore(sw); + } + } + + + public synchronized void setRole(Role role) { + this.role = role; + Runnable consolidateStoreTask = new Runnable() { + @Override + public void run() { + consolidateStore(); + } + }; + if ((role == Role.MASTER) && + this.syncedSwitches.isEmpty()) + addUpdateToQueue(new ReadyForReconcileUpdate()); + + Controller.this.ses.schedule(consolidateStoreTask, + consolidateStoreTimeDelayMs, + TimeUnit.MILLISECONDS); + } + + @LogMessageDocs({ + @LogMessageDoc(level="ERROR", + message="Switch {switch} activated but was already active", + explanation="A switch that was already activated was " + + "activated again. This should not happen.", + recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG + ), + @LogMessageDoc(level="WARN", + message="New switch added {switch} for already-added switch {switch}", + explanation="A switch with the same DPID as another switch " + + "connected to the controller. This can be caused by " + + "multiple switches configured with the same DPID, or " + + "by a switch reconnected very quickly after " + + "disconnecting.", + recommendation="If this happens repeatedly, it is likely there " + + "are switches with duplicate DPIDs on the network. " + + "Reconfigure the appropriate switches. If it happens " + + "very rarely, then it is likely this is a transient " + + "network problem that can be ignored." + ) + }) + /** + * Called when a switch is activated, i.e., when it enters master + * role relative to this controller. + * @param sw + */ + public synchronized void switchActivated(IOFSwitch sw) { + if (role != Role.MASTER) { + counters.invalidSwitchActivatedWhileSlave.updateCounterWithFlush(); + return; // only react to switch connections when master + // FIXME: should we disconnect the switch? When can this happen? + } + Long dpid = sw.getId(); + counters.switchActivated.updateCounterWithFlush(); + IOFSwitch oldSw = this.activeSwitches.put(dpid, sw); + // Update event history + evSwitch.updateEventWithFlush(new SwitchEvent(dpid, "connected")); + + if (oldSw == sw) { + // Note == for object equality, not .equals for value + // TODO: should we wipe the flow table if + // alwaysClearFlowsOnSwAdd is set? OTOH this case should + // really never happen. + counters.errorSameSwitchReactivated.updateCounterWithFlush(); + log.error("Switch {} activated but was already active", sw); + addSwitchToStore(sw); + return; + } + + if (oldSw != null) { + // This happens either when we have switches with duplicate + // DPIDs or when a switch reconnects before we saw the + // disconnect + counters.switchWithSameDpidActivated.updateCounterWithFlush(); + log.warn("New switch added {} for already-added switch {}", + sw, oldSw); + // We need to disconnect and remove the old switch + // TODO: we notify switch listeners that the switch has been + // removed and then we notify them that the new one has been + // added. One could argue that a switchChanged notification + // might be more appropriate in this case.... + oldSw.cancelAllStatisticsReplies(); + addUpdateToQueue(new SwitchUpdate(dpid, + SwitchUpdateType.REMOVED)); + oldSw.disconnectOutputStream(); + // Add the new switch and clear FlowMods + // TODO: if this is the same switch re-connecting rather than + // a DPID collision it would make sense to not wipe the flow + // table. + sw.clearAllFlowMods(); + addUpdateToQueue(new SwitchUpdate(dpid, + SwitchUpdateType.ADDED)); + addUpdateToQueue(new SwitchUpdate(dpid, + SwitchUpdateType.ACTIVATED)); + addSwitchToStore(sw); + return; + } + + IOFSwitch storedSwitch = this.syncedSwitches.remove(sw.getId()); + if (storedSwitch == null) { + // The switch isn't known to the controller cluster. We + // need to send a switchAdded notification and clear all + // flows. + if (!swConnectCache.update(sw.getId())) + sw.clearAllFlowMods(); + addUpdateToQueue(new SwitchUpdate(dpid, + SwitchUpdateType.ADDED)); + addUpdateToQueue(new SwitchUpdate(dpid, + SwitchUpdateType.ACTIVATED)); + counters.newSwitchActivated.updateCounterWithFlush(); + } else { + // FIXME: switch was in store. check if ports or anything else + // has changed and send update. + if (alwaysClearFlowsOnSwActivate) { + sw.clearAllFlowMods(); + } + if (sw.attributeEquals(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true)) { + // We have a stored switch and the newly activated switch + // supports roles. This indicates that the switch was + // previously connected as slave. Since we don't update + // ports while slave, we need to set the ports on the + // new switch from the ports on the stored switch + // No need to send notifications, since we've dispatched + // them as we receive them from the store + sw.setPorts(storedSwitch.getPorts()); + } + addUpdateToQueue(new SwitchUpdate(dpid, + SwitchUpdateType.ACTIVATED)); + sendNotificationsIfSwitchDiffers(storedSwitch, sw); + counters.syncedSwitchActivated.updateCounterWithFlush(); + if (this.syncedSwitches.isEmpty()) { + // we have just activated the last synced switch. I.e., + // all previously known switch are now active. Send + // notification + // update dispatcher will increment counter + addUpdateToQueue(new ReadyForReconcileUpdate()); + } + } + addSwitchToStore(sw); + } + + /** + * Called when ports on the given switch have changed. Writes the + * updated switch to the sync store and queues a switch notification + * to listeners + * @param sw + */ + public synchronized void switchPortsChanged(IOFSwitch sw, + ImmutablePort port, + PortChangeType type) { + if (role != Role.MASTER) { + counters.invalidPortsChanged.updateCounterWithFlush(); + return; + } + if (!this.activeSwitches.containsKey(sw.getId())) { + counters.invalidPortsChanged.updateCounterWithFlush(); + return; + } + // update switch in store + addSwitchToStore(sw); + // no need to count here. SwitchUpdate.dispatch will count + // the portchanged + SwitchUpdate update = new SwitchUpdate(sw.getId(), + SwitchUpdateType.PORTCHANGED, + port, type); + addUpdateToQueue(update); + } + + /** + * Called when we receive a store notification about a new or updated + * switch. + * @param sw + */ + private synchronized void switchAddedToStore(IOFSwitch sw) { + if (role != Role.SLAVE) { + counters.invalidStoreEventWhileMaster.updateCounterWithFlush(); + return; // only read from store if slave + } + Long dpid = sw.getId(); + + IOFSwitch oldSw = syncedSwitches.put(dpid, sw); + if (oldSw == null) { + counters.newSwitchFromStore.updateCounterWithFlush(); + addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.ADDED)); + } else { + // The switch already exists in storage, see if anything + // has changed + sendNotificationsIfSwitchDiffers(oldSw, sw); + counters.updatedSwitchFromStore.updateCounterWithFlush(); + } + } + + /** + * Called when we receive a store notification about a switch that + * has been removed from the sync store + * @param dpid + */ + private synchronized void switchRemovedFromStore(long dpid) { + if (role != Role.SLAVE) { + counters.invalidStoreEventWhileMaster.updateCounterWithFlush(); + return; // only read from store if slave + } + IOFSwitch oldSw = syncedSwitches.remove(dpid); + if (oldSw != null) { + counters.syncedSwitchRemoved.updateCounterWithFlush(); + addUpdateToQueue(new SwitchUpdate(dpid, + SwitchUpdateType.REMOVED)); + } else { + // TODO: the switch was deleted (tombstone) before we ever + // knew about it (or was deleted repeatedly). Can this + // happen? When/how? + counters.unknownSwitchRemovedFromStore.updateCounterWithFlush(); + } + } + + public synchronized void switchDeactivated(IOFSwitch sw) { + // ignore. we don't handle MASTER -> SLAVE transitions. We + // expect a restart + } + + /** + * Called when a switch disconnects + * @param sw + */ + public synchronized void switchDisconnected(IOFSwitch sw) { + if (role == Role.SLAVE) { + counters.switchDisconnectedWhileSlave.updateCounterWithFlush(); + return; // only react to switch connections when master + } + long dpid = sw.getId(); + // Update event history + // TODO: this is asymmetric with respect to connect event + // in switchActivated(). Should we have events on the + // slave as well? + evSwitch.updateEventWithFlush(new SwitchEvent(dpid, "disconnected")); + counters.switchDisconnected.updateCounterWithFlush(); + IOFSwitch oldSw = this.activeSwitches.get(dpid); + if (oldSw != sw) { + // This can happen if the disconnected switch was inactive + // (SLAVE) then oldSw==null. Or if we previously had the + // "added switch for already added switch case". + // Either way we shouldn't notify or do anything else + log.debug("removeSwitch called for switch {} but have {} in" + + " activeSwitches map. Ignoring", sw, oldSw); + return; + } + log.debug("removeSwitch {}", sw); + swConnectCache.update(sw.getId()); + this.activeSwitches.remove(sw.getId()); + removeSwitchFromStore(sw.getId()); + // We cancel all outstanding statistics replies if the switch transition + // from active. In the future we might allow statistics requests + // from slave controllers. Then we need to move this cancelation + // to switch disconnect + sw.cancelAllStatisticsReplies(); + addUpdateToQueue(new SwitchUpdate(sw.getId(), + SwitchUpdateType.REMOVED)); + } + + /** + * Write the given switch to the sync store. + * @param sw + */ + private synchronized void addSwitchToStore(IOFSwitch sw) { + // Add to store + // FIXME: do we need to use a put that takes a versioned here? + // need to verify + try { + storeClient.put(sw.getId(), new SwitchSyncRepresentation(sw)); + } catch (ObsoleteVersionException e) { + // FIXME: what's the right behavior here. Can the store client + // even throw this error? Should not since all local store + // access is synchronized + } catch (SyncException e) { + counters.storeSyncError.updateCounterWithFlush(); + log.error("Could not write switch " + sw.getStringId() + + " to sync store:", e); + } + } + + /** + * Write the given switch to the sync store if it's not already + * there + * TODO: should this be merged with addSwitchToStore + * @param sw + * @return true if the switch was absent, false otherwise + */ + private synchronized boolean addSwitchToStoreIfAbsent(IOFSwitch sw) { + try { + Versioned<SwitchSyncRepresentation> versionedSSr = + storeClient.get(sw.getId()); + if (versionedSSr.getValue() == null) { + // switch is absent + versionedSSr.setValue(new SwitchSyncRepresentation(sw)); + storeClient.put(sw.getId(), versionedSSr); + return true; + } else { + return false; + } + } catch (ObsoleteVersionException e) { + // FIXME: what's the right behavior here. Can the store client + // even throw this error? Should not since all local store + // access is synchronized + } catch (SyncException e) { + counters.storeSyncError.updateCounterWithFlush(); + log.error("Could not write switch " + sw.getStringId() + + " to sync store:", e); + } + return false; + } + + /** + * Remove the given switch from the sync store. + * @param dpid + */ + private synchronized void removeSwitchFromStore(long dpid) { + try { + storeClient.delete(dpid); + } catch (SyncException e) { + counters.storeSyncError.updateCounterWithFlush(); + // ObsoleteVerisonException can't happend because all + // store modifications are synchronized + log.error("Could not remove switch " + + HexString.toHexString(dpid) + + " from sync store:", e); + } + } + + /** + * Check if the two switches differ in their ports or in other + * fields and if they differ enqueue a switch update + * @param oldSw + * @param newSw + */ + private synchronized void + sendNotificationsIfSwitchDiffers(IOFSwitch oldSw, + IOFSwitch newSw) { + Collection<PortChangeEvent> portDiffs = + oldSw.comparePorts(newSw.getPorts()); + for (PortChangeEvent ev: portDiffs) { + SwitchUpdate update = + new SwitchUpdate(newSw.getId(), + SwitchUpdateType.PORTCHANGED, + ev.port, ev.type); + addUpdateToQueue(update); + } + } + /** + * Remove all entries from the store that don't correspond to an + * active switch. + * TODO: is it a problem that this is fully synchronized + */ + private synchronized void consolidateStore() { + if (role == Role.SLAVE) + return; + boolean shouldNotifyReadyForReconcile = false; + counters.consolidateStoreRunCount.updateCounterWithFlush(); + log.info("Consolidating synced switches after MASTER transition"); + IClosableIterator<Map.Entry<Long,Versioned<SwitchSyncRepresentation>>> + iter = null; + try { + iter = storeClient.entries(); + } catch (SyncException e) { + counters.storeSyncError.updateCounterWithFlush(); + log.error("Failed to read switches from sync store", e); + return; + } + try { + while(iter.hasNext()) { + Entry<Long, Versioned<SwitchSyncRepresentation>> entry = + iter.next(); + if (!this.activeSwitches.containsKey(entry.getKey())) { + removeSwitchFromStore(entry.getKey()); + if (this.syncedSwitches.remove(entry.getKey()) != null) { + // a switch that's in the store and in synced + // switches but that is not active. I.e., a + // switch known to the old master that hasn't + // reconnected to this controller. + counters.switchesNotReconnectingToNewMaster + .updateCounterWithFlush(); + shouldNotifyReadyForReconcile = true; + addUpdateToQueue(new SwitchUpdate(entry.getKey(), + SwitchUpdateType.REMOVED)); + } else { + // A switch was in the store but it's neither in + // activeSwitches nor syncedSwitches. This could + // happen if the old Master has added this entry + // to the store after this controller has + // stopped reacting to store notifications (due + // to MASTER transition) + counters.consolidateStoreInconsistencies + .updateCounterWithFlush(); + } + } + } + } finally { + if (iter != null) + iter.close(); + } + // In general, syncedSwitches should now be empty. However, + // the old Master could have removed a switch from the store + // after this controller has stopped reacting to store + // notification (because it's now MASTER). We need to remove + // these switches. + Iterator<Long> it = this.syncedSwitches.keySet().iterator(); + while (it.hasNext()) { + counters.switchesNotReconnectingToNewMaster.updateCounterWithFlush(); + counters.consolidateStoreInconsistencies.updateCounterWithFlush(); + Long dpid = it.next(); + shouldNotifyReadyForReconcile = true; + addUpdateToQueue(new SwitchUpdate(dpid, + SwitchUpdateType.REMOVED)); + it.remove(); + } + if (shouldNotifyReadyForReconcile) { + // at least one previously known switch has been removed. + addUpdateToQueue(new ReadyForReconcileUpdate()); + } + + // FIXME: do we need this final check here. + // Now iterate through all active switches and determine if + // any of them are missing from the sync store. This can only + // happen if another controller has removed them (because we know + // that we have written them to the store). + for (IOFSwitch sw: this.activeSwitches.values()) { + if (addSwitchToStoreIfAbsent(sw)) + counters.consolidateStoreInconsistencies.updateCounterWithFlush(); + } + } + + // FIXME: remove this method + public Map<Long,IOFSwitch> getAllSwitchMap() { + // this.syncedSwitches will be empty after the master transition + Map<Long,IOFSwitch> switches = + new HashMap<Long, IOFSwitch>(this.syncedSwitches); + if (this.role != Role.SLAVE) + switches.putAll(this.activeSwitches); + return switches; + } + + public Set<Long> getAllSwitchDpids() { + // this.syncedSwitches will be empty after the master transition + Set<Long> dpids = new HashSet<Long>(this.syncedSwitches.keySet()); + if (this.role != Role.SLAVE) + dpids.addAll(this.activeSwitches.keySet()); + return dpids; + } + + public IOFSwitch getSwitch(long dpid) { + if (this.role == Role.SLAVE) + return this.syncedSwitches.get(dpid); + // MASTER: if the switch is found in the active map return + // otherwise look up the switch in the bigSync map. The bigSync map + // wil be cleared after the transition is complete. + IOFSwitch sw = this.activeSwitches.get(dpid); + if (sw != null) + return sw; + return this.syncedSwitches.get(dpid); + } + + public void addSwitchEvent(long dpid, String reason, boolean flushNow) { + if (flushNow) + evSwitch.updateEventWithFlush(new SwitchEvent(dpid, reason)); + else + evSwitch.updateEventNoFlush(new SwitchEvent(dpid, reason)); + } + + } + + + /** + * Updates handled by the main loop + */ + interface IUpdate { + /** + * Calls the appropriate listeners + */ + public void dispatch(); + } + + /** + * Update message that indicates that the controller can now start + * flow reconciliation after a SLAVE->MASTER transition + */ + private class ReadyForReconcileUpdate implements IUpdate { + @Override + public void dispatch() { + counters.readyForReconcile.updateCounterWithFlush(); + if (readyForReconcileListeners != null) { + for (IReadyForReconcileListener listener: + readyForReconcileListeners) { + listener.readyForReconcile(); + } + } + } + } + + enum SwitchUpdateType { + ADDED, + REMOVED, + ACTIVATED, + DEACTIVATED, + PORTCHANGED, + OTHERCHANGE + } + /** + * Update message indicating a switch was added or removed + */ + private class SwitchUpdate implements IUpdate { + private final long swId; + private final SwitchUpdateType switchUpdateType; + private final ImmutablePort port; + private final PortChangeType changeType; + + + public SwitchUpdate(long swId, SwitchUpdateType switchUpdateType) { + this(swId, switchUpdateType, null, null); + } + public SwitchUpdate(long swId, + SwitchUpdateType switchUpdateType, + ImmutablePort port, + PortChangeType changeType) { + if (switchUpdateType == SwitchUpdateType.PORTCHANGED) { + if (port == null) { + throw new NullPointerException("Port must not be null " + + "for PORTCHANGED updates"); + } + if (changeType == null) { + throw new NullPointerException("ChangeType must not be " + + "null for PORTCHANGED updates"); + } + } else { + if (port != null || changeType != null) { + throw new IllegalArgumentException("port and changeType " + + "must be null for " + switchUpdateType + + " updates"); + } + } + this.swId = swId; + this.switchUpdateType = switchUpdateType; + this.port = port; + this.changeType = changeType; + } + @Override + public void dispatch() { + if (log.isTraceEnabled()) { + log.trace("Dispatching switch update {} {}", + HexString.toHexString(swId), switchUpdateType); + } + if (switchListeners != null) { + for (IOFSwitchListener listener : switchListeners) { + switch(switchUpdateType) { + case ADDED: + // don't count here. We have more specific + // counters before the update is created + listener.switchAdded(swId); + break; + case REMOVED: + // don't count here. We have more specific + // counters before the update is created + listener.switchRemoved(swId); + break; + case PORTCHANGED: + counters.switchPortChanged.updateCounterWithFlush(); + listener.switchPortChanged(swId, port, changeType); + break; + case ACTIVATED: + // don't count here. We have more specific + // counters before the update is created + listener.switchActivated(swId); + break; + case DEACTIVATED: + // ignore + break; + case OTHERCHANGE: + counters.switchOtherChange.updateCounterWithFlush(); + listener.switchChanged(swId); + break; + } + } + } + } + } + + /** + * Update message indicating controller's role has changed. + * RoleManager, which enqueues these updates gurantees that we will + * only have a single transition from SLAVE to MASTER. + */ + private class HARoleUpdate implements IUpdate { + private final Role newRole; + public HARoleUpdate(Role newRole) { + if (newRole != Role.MASTER) + throw new IllegalArgumentException("Only legal role change is" + + "to MASTER. Got to " + + newRole); + this.newRole = newRole; + } + @Override + public void dispatch() { + if (log.isDebugEnabled()) { + log.debug("Dispatching HA Role update newRole = {}", + newRole); + } + for (IHAListener listener : haListeners.getOrderedListeners()) { + if (log.isTraceEnabled()) { + log.trace("Calling HAListener {} with transitionToMaster", + listener.getName()); + } + listener.transitionToMaster(); + } + if (newRole != Role.SLAVE) { + Controller.this.notifiedRole = newRole; + } + } + } + + /** + * Update message indicating + * IPs of controllers in controller cluster have changed. + */ + private class HAControllerNodeIPUpdate implements IUpdate { + public final Map<String,String> curControllerNodeIPs; + public final Map<String,String> addedControllerNodeIPs; + public final Map<String,String> removedControllerNodeIPs; + public HAControllerNodeIPUpdate( + HashMap<String,String> curControllerNodeIPs, + HashMap<String,String> addedControllerNodeIPs, + HashMap<String,String> removedControllerNodeIPs) { + this.curControllerNodeIPs = curControllerNodeIPs; + this.addedControllerNodeIPs = addedControllerNodeIPs; + this.removedControllerNodeIPs = removedControllerNodeIPs; + } + @Override + public void dispatch() { + if (log.isTraceEnabled()) { + log.trace("Dispatching HA Controller Node IP update " + + "curIPs = {}, addedIPs = {}, removedIPs = {}", + new Object[] { curControllerNodeIPs, addedControllerNodeIPs, + removedControllerNodeIPs } + ); + } + if (haListeners != null) { + for (IHAListener listener: haListeners.getOrderedListeners()) { + listener.controllerNodeIPsChanged(curControllerNodeIPs, + addedControllerNodeIPs, removedControllerNodeIPs); + } + } + } + } + + // *************** + // Getters/Setters + // *************** + + void setStorageSourceService(IStorageSourceService storageSource) { + this.storageSource = storageSource; + } + + IStorageSourceService getStorageSourceService() { + return this.storageSource; + } + + void setCounterStore(ICounterStoreService counterStore) { + this.counterStore = counterStore; + } + + void setDebugCounter(IDebugCounterService debugCounters) { + this.debugCounters = debugCounters; + } + + public void setDebugEvent(IDebugEventService debugEvent) { + this.debugEvents = debugEvent; + } + + IDebugCounterService getDebugCounter() { + return this.debugCounters; + } + + void setSyncService(ISyncService syncService) { + this.syncService = syncService; + } + void setPktInProcessingService(IPktInProcessingTimeService pits) { + this.pktinProcTime = pits; + } + + void setRestApiService(IRestApiService restApi) { + this.restApi = restApi; + } + + void setThreadPoolService(IThreadPoolService tp) { + this.threadPool = tp; + } + + IThreadPoolService getThreadPoolService() { + return this.threadPool; + } + + @Override + public Role getRole() { + // FIXME: + return notifiedRole; + } + + @Override + public RoleInfo getRoleInfo() { + return roleManager.getRoleInfo(); + } + + @Override + public void setRole(Role role, String roleChangeDescription) { + roleManager.setRole(role, roleChangeDescription); + } + + // **************** + // Message handlers + // **************** + + /** + * Indicates that ports on the given switch have changed. Enqueue a + * switch update. + * @param sw + */ + void notifyPortChanged(IOFSwitch sw, + ImmutablePort port, + PortChangeType changeType) { + if (sw == null) { + String msg = String.format("Switch must not be null. " + + "port=%s, changeType=%s", port, changeType); + throw new NullPointerException(msg); + } + if (port == null) { + String msg = String.format("Port must not be null. " + + "switch=%s, changeType=%s", sw, changeType); + throw new NullPointerException(msg); + } + if (changeType == null) { + String msg = String.format("ChangeType must not be null. " + + "switch=%s, port=%s", sw, port); + throw new NullPointerException(msg); + } + this.switchManager.switchPortsChanged(sw, port, changeType); + } + + /** + * flcontext_cache - Keep a thread local stack of contexts + */ + protected static final ThreadLocal<Stack<FloodlightContext>> flcontext_cache = + new ThreadLocal <Stack<FloodlightContext>> () { + @Override + protected Stack<FloodlightContext> initialValue() { + return new Stack<FloodlightContext>(); + } + }; + + /** + * flcontext_alloc - pop a context off the stack, if required create a new one + * @return FloodlightContext + */ + protected static FloodlightContext flcontext_alloc() { + FloodlightContext flcontext = null; + + if (flcontext_cache.get().empty()) { + flcontext = new FloodlightContext(); + } + else { + flcontext = flcontext_cache.get().pop(); + } + + return flcontext; + } + + /** + * flcontext_free - Free the context to the current thread + * @param flcontext + */ + protected void flcontext_free(FloodlightContext flcontext) { + flcontext.getStorage().clear(); + flcontext_cache.get().push(flcontext); + } + + + /** + * + * Handle and dispatch a message to IOFMessageListeners. + * + * We only dispatch messages to listeners if the controller's role is MASTER. + * + * @param sw The switch sending the message + * @param m The message the switch sent + * @param flContext The floodlight context to use for this message. If + * null, a new context will be allocated. + * @throws IOException + * + * FIXME: this method and the ChannelHandler disagree on which messages + * should be dispatched and which shouldn't + */ + @LogMessageDocs({ + @LogMessageDoc(level="ERROR", + message="Ignoring PacketIn (Xid = {xid}) because the data" + + " field is empty.", + explanation="The switch sent an improperly-formatted PacketIn" + + " message", + recommendation=LogMessageDoc.CHECK_SWITCH), + @LogMessageDoc(level="WARN", + message="Unhandled OF Message: {} from {}", + explanation="The switch sent a message not handled by " + + "the controller") + }) + protected void handleMessage(IOFSwitch sw, OFMessage m, + FloodlightContext bContext) + throws IOException { + Ethernet eth = null; + + if (this.notifiedRole == Role.SLAVE) { + counters.dispatchMessageWhileSlave.updateCounterNoFlush(); + // We are SLAVE. Do not dispatch messages to listeners. + return; + } + counters.dispatchMessage.updateCounterNoFlush(); + + switch (m.getType()) { + case PACKET_IN: + OFPacketIn pi = (OFPacketIn)m; + + if (pi.getPacketData().length <= 0) { + log.error("Ignoring PacketIn (Xid = " + pi.getXid() + + ") because the data field is empty."); + return; + } + + if (Controller.ALWAYS_DECODE_ETH) { + eth = new Ethernet(); + eth.deserialize(pi.getPacketData(), 0, + pi.getPacketData().length); + counterStore.updatePacketInCountersLocal(sw, m, eth); + } + // fall through to default case... + + default: + + List<IOFMessageListener> listeners = null; + if (messageListeners.containsKey(m.getType())) { + listeners = messageListeners.get(m.getType()). + getOrderedListeners(); + } + + FloodlightContext bc = null; + if (listeners != null) { + // Check if floodlight context is passed from the calling + // function, if so use that floodlight context, otherwise + // allocate one + if (bContext == null) { + bc = flcontext_alloc(); + } else { + bc = bContext; + } + if (eth != null) { + IFloodlightProviderService.bcStore.put(bc, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD, + eth); + } + + // Get the starting time (overall and per-component) of + // the processing chain for this packet if performance + // monitoring is turned on + pktinProcTime.recordStartTimePktIn(); + Command cmd; + for (IOFMessageListener listener : listeners) { + pktinProcTime.recordStartTimeComp(listener); + cmd = listener.receive(sw, m, bc); + pktinProcTime.recordEndTimeComp(listener); + + if (Command.STOP.equals(cmd)) { + break; + } + } + pktinProcTime.recordEndTimePktIn(sw, m, bc); + } else { + if (m.getType() != OFType.BARRIER_REPLY) + log.warn("Unhandled OF Message: {} from {}", m, sw); + else + log.debug("Received a Barrier Reply, no listeners for it"); + } + + if ((bContext == null) && (bc != null)) flcontext_free(bc); + } + } + + void switchActivated(IOFSwitch sw) { + this.switchManager.switchActivated(sw); + } + + void switchDeactivated(IOFSwitch sw) { + this.switchManager.switchDeactivated(sw); + } + + void switchDisconnected(IOFSwitch sw) { + this.switchManager.switchDisconnected(sw); + } + + // *************** + // IFloodlightProvider + // *************** + + /** + * Forward to RoleManager + * @param h + */ + void addSwitchChannelAndSendInitialRole(OFChannelHandler h) { + roleManager.addOFChannelHandlerAndSendRole(h); + } + + /** + * Forwards to RoleManager + * @param h + */ + void removeSwitchChannel(OFChannelHandler h) { + roleManager.removeOFChannelHandler(h); + } + + /** + * Forwards to RoleManager + * @param h + * @param role + */ + void reassertRole(OFChannelHandler h, Role role) { + roleManager.reassertRole(h, role); + } + + // FIXME: remove this method + @Override + public Map<Long,IOFSwitch> getAllSwitchMap() { + return this.switchManager.getAllSwitchMap(); + } + + @Override + public Set<Long> getAllSwitchDpids() { + return this.switchManager.getAllSwitchDpids(); + } + + @Override + public IOFSwitch getSwitch(long dpid) { + return this.switchManager.getSwitch(dpid); + } + + @Override + public void addOFSwitchListener(IOFSwitchListener listener) { + this.switchListeners.add(listener); + } + + @Override + public void removeOFSwitchListener(IOFSwitchListener listener) { + this.switchListeners.remove(listener); + } + + @Override + public synchronized void addOFMessageListener(OFType type, + IOFMessageListener listener) { + ListenerDispatcher<OFType, IOFMessageListener> ldd = + messageListeners.get(type); + if (ldd == null) { + ldd = new ListenerDispatcher<OFType, IOFMessageListener>(); + messageListeners.put(type, ldd); + } + ldd.addListener(type, listener); + } + + @Override + public synchronized void removeOFMessageListener(OFType type, + IOFMessageListener listener) { + ListenerDispatcher<OFType, IOFMessageListener> ldd = + messageListeners.get(type); + if (ldd != null) { + ldd.removeListener(listener); + } + } + + private void logListeners() { + for (Map.Entry<OFType, + ListenerDispatcher<OFType, + IOFMessageListener>> entry + : messageListeners.entrySet()) { + + OFType type = entry.getKey(); + ListenerDispatcher<OFType, IOFMessageListener> ldd = + entry.getValue(); + + StringBuilder sb = new StringBuilder(); + sb.append("OFListeners for "); + sb.append(type); + sb.append(": "); + for (IOFMessageListener l : ldd.getOrderedListeners()) { + sb.append(l.getName()); + sb.append(","); + } + log.debug(sb.toString()); + } + + StringBuilder sb = new StringBuilder(); + sb.append("HAListeners: "); + for (IHAListener l: haListeners.getOrderedListeners()) { + sb.append(l.getName()); + sb.append(", "); + } + log.debug(sb.toString()); + } + + public void removeOFMessageListeners(OFType type) { + messageListeners.remove(type); + } + + @Override + public Map<OFType, List<IOFMessageListener>> getListeners() { + Map<OFType, List<IOFMessageListener>> lers = + new HashMap<OFType, List<IOFMessageListener>>(); + for(Entry<OFType, ListenerDispatcher<OFType, IOFMessageListener>> e : + messageListeners.entrySet()) { + lers.put(e.getKey(), e.getValue().getOrderedListeners()); + } + return Collections.unmodifiableMap(lers); + } + + @Override + @LogMessageDocs({ + @LogMessageDoc(level="ERROR", + message="Error reinjecting OFMessage on switch {switch}", + explanation="An I/O error occured while attempting to " + + "process an OpenFlow message", + recommendation=LogMessageDoc.CHECK_SWITCH) + }) + public boolean injectOfMessage(IOFSwitch sw, OFMessage msg, + FloodlightContext bc) { + if (sw == null) + throw new NullPointerException("Switch must not be null"); + if (msg == null) + throw new NullPointerException("OFMessage must not be null"); + + // FIXME: Do we need to be able to inject messages from switches + // where we're the slave controller (i.e. they're connected but + // not active)? + if (!sw.isActive()) return false; + + try { + // Pass Floodlight context to the handleMessages() + handleMessage(sw, msg, bc); + } catch (IOException e) { + log.error("Error reinjecting OFMessage on switch {}", + sw.getStringId()); + return false; + } + return true; + } + + @Override + @LogMessageDoc(message="Calling System.exit", + explanation="The controller is terminating") + public synchronized void terminate() { + log.info("Calling System.exit"); + System.exit(1); + } + + @Override + public boolean injectOfMessage(IOFSwitch sw, OFMessage msg) { + // call the overloaded version with floodlight context set to null + return injectOfMessage(sw, msg, null); + } + + @Override + public void handleOutgoingMessage(IOFSwitch sw, OFMessage m, + FloodlightContext bc) { + if (sw == null) + throw new NullPointerException("Switch must not be null"); + if (m == null) + throw new NullPointerException("OFMessage must not be null"); + if (bc == null) + bc = new FloodlightContext(); + if (log.isTraceEnabled()) { + String str = OFMessage.getDataAsString(sw, m, bc); + log.trace("{}", str); + } + + List<IOFMessageListener> listeners = null; + if (messageListeners.containsKey(m.getType())) { + listeners = + messageListeners.get(m.getType()).getOrderedListeners(); + } + + if (listeners != null) { + for (IOFMessageListener listener : listeners) { + if (Command.STOP.equals(listener.receive(sw, m, bc))) { + break; + } + } + } + } + + @Override + public BasicFactory getOFMessageFactory() { + return factory; + } + + // ************** + // Initialization + // ************** + + + /** + * Sets the initial role based on properties in the config params. + * It looks for two different properties. + * If the "role" property is specified then the value should be + * either "EQUAL", "MASTER", or "SLAVE" and the role of the + * controller is set to the specified value. If the "role" property + * is not specified then it looks next for the "role.path" property. + * In this case the value should be the path to a property file in + * the file system that contains a property called "floodlight.role" + * which can be one of the values listed above for the "role" property. + * The idea behind the "role.path" mechanism is that you have some + * separate heartbeat and master controller election algorithm that + * determines the role of the controller. When a role transition happens, + * it updates the current role in the file specified by the "role.path" + * file. Then if floodlight restarts for some reason it can get the + * correct current role of the controller from the file. + * @param configParams The config params for the FloodlightProvider service + * @return A valid role if role information is specified in the + * config params, otherwise null + */ + @LogMessageDocs({ + @LogMessageDoc(message="Controller role set to {role}", + explanation="Setting the initial HA role to "), + @LogMessageDoc(level="ERROR", + message="Invalid current role value: {role}", + explanation="An invalid HA role value was read from the " + + "properties file", + recommendation=LogMessageDoc.CHECK_CONTROLLER) + }) + protected Role getInitialRole(Map<String, String> configParams) { + Role role = Role.MASTER; + String roleString = configParams.get("role"); + if (roleString == null) { + String rolePath = configParams.get("rolepath"); + if (rolePath != null) { + Properties properties = new Properties(); + try { + properties.load(new FileInputStream(rolePath)); + roleString = properties.getProperty("floodlight.role"); + } + catch (IOException exc) { + // Don't treat it as an error if the file specified by the + // rolepath property doesn't exist. This lets us enable the + // HA mechanism by just creating/setting the floodlight.role + // property in that file without having to modify the + // floodlight properties. + } + } + } + + if (roleString != null) { + // Canonicalize the string to the form used for the enum constants + roleString = roleString.trim().toUpperCase(); + try { + role = Role.valueOf(roleString); + } + catch (IllegalArgumentException exc) { + log.error("Invalid current role value: {}", roleString); + } + } + if (role == Role.EQUAL) + role = Role.MASTER; + + log.info("Controller role set to {}", role); + + return role; + } + + /** + * Tell controller that we're ready to accept switches loop + * @throws IOException + */ + @Override + @LogMessageDocs({ + @LogMessageDoc(message="Listening for switch connections on {address}", + explanation="The controller is ready and listening for new" + + " switch connections"), + @LogMessageDoc(message="Storage exception in controller " + + "updates loop; terminating process", + explanation=ERROR_DATABASE, + recommendation=LogMessageDoc.CHECK_CONTROLLER), + @LogMessageDoc(level="ERROR", + message="Exception in controller updates loop", + explanation="Failed to dispatch controller event", + recommendation=LogMessageDoc.GENERIC_ACTION) + }) + public void run() { + if (log.isDebugEnabled()) { + logListeners(); + } + + try { + final ServerBootstrap bootstrap = createServerBootStrap(); + + bootstrap.setOption("reuseAddr", true); + bootstrap.setOption("child.keepAlive", true); + bootstrap.setOption("child.tcpNoDelay", true); + bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE); + + ChannelPipelineFactory pfact = + new OpenflowPipelineFactory(this, null); + bootstrap.setPipelineFactory(pfact); + InetSocketAddress sa = + (openFlowHost == null) + ? new InetSocketAddress(openFlowPort) + : new InetSocketAddress(openFlowHost, openFlowPort); + final ChannelGroup cg = new DefaultChannelGroup(); + cg.add(bootstrap.bind(sa)); + + log.info("Listening for switch connections on {}", sa); + } catch (Exception e) { + throw new RuntimeException(e); + } + + // main loop + while (true) { + try { + IUpdate update = updates.take(); + update.dispatch(); + } catch (InterruptedException e) { + log.error("Received interrupted exception in updates loop;" + + "terminating process"); + terminate(); + } catch (StorageException e) { + log.error("Storage exception in controller " + + "updates loop; terminating process", e); + terminate(); + } catch (Exception e) { + log.error("Exception in controller updates loop", e); + } + } + } + + private ServerBootstrap createServerBootStrap() { + if (workerThreads == 0) { + return new ServerBootstrap( + new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool())); + } else { + return new ServerBootstrap( + new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool(), workerThreads)); + } + } + + private void setConfigParams(Map<String, String> configParams) { + String ofPort = configParams.get("openflowport"); + if (ofPort != null) { + this.openFlowPort = Integer.parseInt(ofPort); + } + log.debug("OpenFlow port set to {}", this.openFlowPort); + String threads = configParams.get("workerthreads"); + if (threads != null) { + this.workerThreads = Integer.parseInt(threads); + } + log.debug("Number of worker threads set to {}", this.workerThreads); + + } + + private void initVendorMessages() { + // Configure openflowj to be able to parse the role request/reply + // vendor messages. + OFNiciraVendorExtensions.initialize(); + + // Register the standard Vendor actions that we support + OFVendorActions.registerStandardVendorActions(); + } + + /** + * Initialize internal data structures + */ + public void init(Map<String, String> configParams) { + // These data structures are initialized here because other + // module's startUp() might be called before ours + this.messageListeners = + new ConcurrentHashMap<OFType, + ListenerDispatcher<OFType, + IOFMessageListener>>(); + this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>(); + // add switch notification listener + this.addOFSwitchListener(new NotificationSwitchListener()); + this.readyForReconcileListeners = + new CopyOnWriteArraySet<IReadyForReconcileListener>(); + this.haListeners = + new ListenerDispatcher<HAListenerTypeMarker, IHAListener>(); + this.driverRegistry = new NaiiveSwitchDriverRegistry(); + this.controllerNodeIPsCache = new HashMap<String, String>(); + this.updates = new LinkedBlockingQueue<IUpdate>(); + this.factory = BasicFactory.getInstance(); + this.providerMap = new HashMap<String, List<IInfoProvider>>(); + setConfigParams(configParams); + Role initialRole = getInitialRole(configParams); + this.notifiedRole = initialRole; + initVendorMessages(); + + String option = configParams.get("flushSwitchesOnReconnect"); + + if (option != null && option.equalsIgnoreCase("true")) { + this.setAlwaysClearFlowsOnSwActivate(true); + log.info("Flush switches on reconnect -- Enabled."); + } else { + this.setAlwaysClearFlowsOnSwActivate(false); + log.info("Flush switches on reconnect -- Disabled"); + } + + uplinkPortPrefixSet = new HashSet<String>(); + uplinkPortPrefixSet.add("eth"); + uplinkPortPrefixSet.add("bond"); + String str = configParams.get("uplinkPortPrefix"); + if (str != null) { + List<String> items = Arrays.asList(str.split("\\s*,\\s*")); + if (items != null) { + for (String s: items) { + if (s.length() > 0) { + uplinkPortPrefixSet.add(s); + } + } + } + } + + this.roleManager = new RoleManager(this.notifiedRole, + INITIAL_ROLE_CHANGE_DESCRIPTION); + this.switchManager = new SwitchManager(this.notifiedRole); + this.counters = new Counters(); + this.swConnectCache = + new TimedCache<Long>(100, 5*1000 ); // 5 seconds interval + } + + /** + * Startup all of the controller's components + */ + @LogMessageDoc(message="Waiting for storage source", + explanation="The system database is not yet ready", + recommendation="If this message persists, this indicates " + + "that the system database has failed to start. " + + LogMessageDoc.CHECK_CONTROLLER) + public void startupComponents() throws FloodlightModuleException { + // Create the table names we use + storageSource.createTable(CONTROLLER_TABLE_NAME, null); + storageSource.createTable(CONTROLLER_INTERFACE_TABLE_NAME, null); + storageSource.createTable(SWITCH_CONFIG_TABLE_NAME, null); + storageSource.setTablePrimaryKeyName(CONTROLLER_TABLE_NAME, + 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( + this.threadPool.getScheduledExecutor()); + } + + // Add our REST API + restApi.addRestletRoutable(new CoreWebRoutable()); + + this.ses = threadPool.getScheduledExecutor(); + + try { + this.syncService.registerStore(SWITCH_SYNC_STORE_NAME, Scope.LOCAL); + this.storeClient = this.syncService + .getStoreClient(SWITCH_SYNC_STORE_NAME, + Long.class, + SwitchSyncRepresentation.class); + this.storeClient.addStoreListener(this.switchManager); + } catch (SyncException e) { + throw new FloodlightModuleException("Error while setting up sync service", e); + } + + try { + this.counters.createCounters(debugCounters); + } catch (CounterException e) { + throw new FloodlightModuleException(e.getMessage()); + } + + addInfoProvider("summary", this); + + 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() throws FloodlightModuleException { + if (debugEvents == null) { + debugEvents = new NullDebugEvent(); + } + try { + evSwitch = debugEvents.registerEvent( + Counters.prefix, "switchevent", + "Switch connected, disconnected or port changed", + EventType.ALWAYS_LOG, SwitchEvent.class, 100); + } catch (MaxEventsRegistered e) { + throw new FloodlightModuleException("Max events registered", e); + } + } + + public class SwitchEvent { + @EventColumn(name = "dpid", description = EventFieldType.DPID) + long dpid; + + @EventColumn(name = "reason", description = EventFieldType.STRING) + String reason; + + public SwitchEvent(long dpid, String reason) { + this.dpid = dpid; + this.reason = reason; + } + } + + @Override + public void addInfoProvider(String type, IInfoProvider provider) { + if (!providerMap.containsKey(type)) { + providerMap.put(type, new ArrayList<IInfoProvider>()); + } + providerMap.get(type).add(provider); + } + + @Override + public void removeInfoProvider(String type, IInfoProvider provider) { + if (!providerMap.containsKey(type)) { + log.debug("Provider type {} doesn't exist.", type); + return; + } + + providerMap.get(type).remove(provider); + } + + @Override + public Map<String, Object> getControllerInfo(String type) { + if (!providerMap.containsKey(type)) return null; + + Map<String, Object> result = new LinkedHashMap<String, Object>(); + for (IInfoProvider provider : providerMap.get(type)) { + result.putAll(provider.getInfo(type)); + } + + return result; + } + + @Override + public void addHAListener(IHAListener listener) { + this.haListeners.addListener(null,listener); + } + + @Override + public void removeHAListener(IHAListener listener) { + this.haListeners.removeListener(listener); + } + + @Override + public void addReadyForReconcileListener(IReadyForReconcileListener l) { + this.readyForReconcileListeners.add(l); + } + + + /** + * Handle changes to the controller nodes IPs and dispatch update. + */ + protected void handleControllerNodeIPChanges() { + HashMap<String,String> curControllerNodeIPs = new HashMap<String,String>(); + HashMap<String,String> addedControllerNodeIPs = new HashMap<String,String>(); + HashMap<String,String> removedControllerNodeIPs =new HashMap<String,String>(); + String[] colNames = { CONTROLLER_INTERFACE_CONTROLLER_ID, + CONTROLLER_INTERFACE_TYPE, + CONTROLLER_INTERFACE_NUMBER, + CONTROLLER_INTERFACE_DISCOVERED_IP }; + synchronized(controllerNodeIPsCache) { + // We currently assume that interface Ethernet0 is the relevant + // controller interface. Might change. + // We could (should?) implement this using + // predicates, but creating the individual and compound predicate + // seems more overhead then just checking every row. Particularly, + // since the number of rows is small and changes infrequent + IResultSet res = storageSource.executeQuery(CONTROLLER_INTERFACE_TABLE_NAME, + colNames,null, null); + while (res.next()) { + if (res.getString(CONTROLLER_INTERFACE_TYPE).equals("Ethernet") && + res.getInt(CONTROLLER_INTERFACE_NUMBER) == 0) { + String controllerID = res.getString(CONTROLLER_INTERFACE_CONTROLLER_ID); + String discoveredIP = res.getString(CONTROLLER_INTERFACE_DISCOVERED_IP); + String curIP = controllerNodeIPsCache.get(controllerID); + + curControllerNodeIPs.put(controllerID, discoveredIP); + if (curIP == null) { + // new controller node IP + addedControllerNodeIPs.put(controllerID, discoveredIP); + } + else if (!curIP.equals(discoveredIP)) { + // IP changed + removedControllerNodeIPs.put(controllerID, curIP); + addedControllerNodeIPs.put(controllerID, discoveredIP); + } + } + } + // Now figure out if rows have been deleted. We can't use the + // rowKeys from rowsDeleted directly, since the tables primary + // key is a compound that we can't disassemble + Set<String> curEntries = curControllerNodeIPs.keySet(); + Set<String> removedEntries = controllerNodeIPsCache.keySet(); + removedEntries.removeAll(curEntries); + for (String removedControllerID : removedEntries) + removedControllerNodeIPs.put(removedControllerID, + controllerNodeIPsCache.get(removedControllerID)); + controllerNodeIPsCache.clear(); + controllerNodeIPsCache.putAll(curControllerNodeIPs); + counters.controllerNodeIpsChanged.updateCounterWithFlush(); + HAControllerNodeIPUpdate update = new HAControllerNodeIPUpdate( + curControllerNodeIPs, addedControllerNodeIPs, + removedControllerNodeIPs); + if (!removedControllerNodeIPs.isEmpty() || !addedControllerNodeIPs.isEmpty()) { + addUpdateToQueue(update); + } + } + } + + @Override + public Map<String, String> getControllerNodeIPs() { + // We return a copy of the mapping so we can guarantee that + // the mapping return is the same as one that will be (or was) + // dispatched to IHAListeners + HashMap<String,String> retval = new HashMap<String,String>(); + synchronized(controllerNodeIPsCache) { + retval.putAll(controllerNodeIPsCache); + } + 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); + } + } + + @Override + public long getSystemStartTime() { + RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean(); + return rb.getStartTime(); + } + + @Override + public void setAlwaysClearFlowsOnSwActivate(boolean value) { + this.alwaysClearFlowsOnSwActivate = value; + } + + + @Override + public Map<String, Long> getMemory() { + Map<String, Long> m = new HashMap<String, Long>(); + Runtime runtime = Runtime.getRuntime(); + m.put("total", runtime.totalMemory()); + m.put("free", runtime.freeMemory()); + return m; + } + + @Override + public Long getUptime() { + RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean(); + return rb.getUptime(); + } + + + + @Override + public void addOFSwitchDriver(String manufacturerDescriptionPrefix, + IOFSwitchDriver driver) { + driverRegistry.addSwitchDriver(manufacturerDescriptionPrefix, driver); + } + + /** + * Forward to the registry to get an IOFSwitch instance. + * @param desc + * @return + */ + IOFSwitch getOFSwitchInstance(OFDescriptionStatistics desc) { + return driverRegistry.getOFSwitchInstance(desc); + } + + /** + * Switch Added/Deleted Events + */ + @Override + public void addSwitchEvent(long switchDPID, String reason, boolean flushNow) { + switchManager.addSwitchEvent(switchDPID, reason, flushNow); + } + + @LogMessageDoc(level="WARN", + message="Failure adding update {} to queue", + explanation="The controller tried to add an internal notification" + + " to its message queue but the add failed.", + recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) + private void addUpdateToQueue(IUpdate update) { + try { + this.updates.put(update); + } catch (InterruptedException e) { + // This should never happen + log.error("Failure adding update {} to queue.", update); + } + } + + void flushAll() { + // Flush all flow-mods/packet-out/stats generated from this "train" + OFSwitchBase.flush_all(); + counterStore.updateFlush(); + debugCounters.flushCounters(); + debugEvents.flushEvents(); + } + + short getAccessFlowPriority() { + return accessPriority; + } + + short getCoreFlowPriority() { + return corePriority; + } + + /** + * FOR TESTING ONLY. + * Dispatch all updates in the update queue until queue is empty + */ + void processUpdateQueueForTesting() { + while(!updates.isEmpty()) { + IUpdate update = updates.poll(); + if (update != null) + update.dispatch(); + } + } + + /** + * FOR TESTING ONLY + * check if update queue is empty + */ + boolean isUpdateQueueEmptyForTesting() { + return this.updates.isEmpty(); + } + + /** + * FOR TESTING ONLY + * @param update + */ + void setConsolidateStoreTaskDelay(int consolidateStoreTaskDelayMs) { + this.consolidateStoreTimeDelayMs = consolidateStoreTaskDelayMs; + } + + /** + * FOR TESTING ONLY + * returns the store listener so we can send events to the listener + */ + IStoreListener<Long> getStoreListener() { + return this.switchManager; + } + + @Override + public Map<String, Object> getInfo(String type) { + if (!"summary".equals(type)) return null; + + Map<String, Object> info = new HashMap<String, Object>(); + + info.put("# Switches", this.getAllSwitchDpids().size()); + return info; + } +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/ControllerCounters.java b/src/main/java/net/floodlightcontroller/core/internal/ControllerCounters.java deleted file mode 100644 index c511a09ee13d9d6604cbc3e9c095adea8d6c0b8b..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/ControllerCounters.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import net.floodlightcontroller.core.IOFSwitchBackend; -import net.floodlightcontroller.core.OFConnectionCounters; -import net.floodlightcontroller.debugcounter.IDebugCounter; -import net.floodlightcontroller.debugcounter.IDebugCounterService; -import net.floodlightcontroller.debugcounter.IDebugCounterService.MetaData; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - -@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", - justification = "It is ok to predefine Debug Counters that are not yet used") -public class ControllerCounters { - - public final String prefix = ControllerCounters.class.getSimpleName(); - public final String statsPrefix = IOFSwitchBackend.class.getPackage() - .getName(); - - public final IDebugCounter packetParsingError; - public final IDebugCounter dispatchMessageWhileStandby; - public final IDebugCounter dispatchMessage; - - public ControllerCounters(IDebugCounterService debugCounters) { - debugCounters.registerModule(prefix); - debugCounters.registerModule(OFConnectionCounters.COUNTER_MODULE); - - dispatchMessageWhileStandby = debugCounters.registerCounter(prefix, - "dispatch-message-while-slave", - "Number of times an OF message was received " - + "and supposed to be dispatched but the " - + "controller was in SLAVE role and the message " - + "was not dispatched"); - // does this cnt make sense? more specific?? per type? - // count stops? - dispatchMessage = debugCounters.registerCounter(prefix, - "dispatch-message", - "Number of times an OF message was dispatched " - + "to registered modules"); - - // TODO: FIXME - // Need a better way to handle these - packetParsingError = debugCounters.registerCounter(prefix, - "packet-parsing-error", - "Number of times the packet parsing " - + "encountered an error", - MetaData.ERROR); - } -} diff --git a/src/main/java/net/floodlightcontroller/core/internal/FloodlightProvider.java b/src/main/java/net/floodlightcontroller/core/internal/FloodlightProvider.java index 12716c4bab5c4b4afeb55089a5ee7264e2d46148..8299bb2c223f6c93437813db9dc8963acf386859 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/FloodlightProvider.java +++ b/src/main/java/net/floodlightcontroller/core/internal/FloodlightProvider.java @@ -21,11 +21,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; -import net.floodlightcontroller.core.internal.IOFSwitchService; -import net.floodlightcontroller.core.internal.Controller; -import net.floodlightcontroller.core.module.Run; - -import org.projectfloodlight.openflow.protocol.OFType; import org.sdnplatform.sync.ISyncService; import net.floodlightcontroller.core.IFloodlightProviderService; @@ -44,10 +39,6 @@ import net.floodlightcontroller.threadpool.IThreadPoolService; public class FloodlightProvider implements IFloodlightModule { Controller controller; - public FloodlightProvider() { - controller = new Controller(); - } - @Override public Collection<Class<? extends IFloodlightService>> getModuleServices() { Collection<Class<? extends IFloodlightService>> services = @@ -79,7 +70,6 @@ public class FloodlightProvider implements IFloodlightModule { dependencies.add(ICounterStoreService.class); dependencies.add(IDebugCounterService.class); dependencies.add(IDebugEventService.class); - dependencies.add(IOFSwitchService.class); dependencies.add(IThreadPoolService.class); dependencies.add(ISyncService.class); return dependencies; @@ -109,23 +99,6 @@ public class FloodlightProvider implements IFloodlightModule { @Override public void startUp(FloodlightModuleContext context) throws FloodlightModuleException { - controller.startupComponents(context.getModuleLoader()); + controller.startupComponents(); } - - @Run(mainLoop=true) - public void run() throws FloodlightModuleException { - controller.run(); - } - - @Override - public boolean isCallbackOrderingPrereq(OFType type, String name) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean isCallbackOrderingPostreq(OFType type, String name) { - // TODO Auto-generated method stub - return false; - } } diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFAuxException.java b/src/main/java/net/floodlightcontroller/core/internal/HandshakeTimeoutException.java similarity index 51% rename from src/main/java/net/floodlightcontroller/core/internal/OFAuxException.java rename to src/main/java/net/floodlightcontroller/core/internal/HandshakeTimeoutException.java index e2a6ccfdd8c0e1a6af30bfe91f4fecea3b6035d5..421ec1ac7dd3121a245b1b3b82fee0131b86f032 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFAuxException.java +++ b/src/main/java/net/floodlightcontroller/core/internal/HandshakeTimeoutException.java @@ -1,7 +1,7 @@ /** -* Copyright 2011, Big Switch Networks, Inc. +* Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University -* +* * 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 @@ -18,25 +18,12 @@ package net.floodlightcontroller.core.internal; /** - * This exception indicates an error or unexpected message during - * OF Aux handshaking. E.g., if a switch reports that it cannot supply us - * with the number of OF Aux connections needed. - * @author Jason Parraga <Jason.Parraga@bigswitch.com> + * Exception is thrown when the handshake fails to complete + * before a specified time + * @author readams */ -public class OFAuxException extends SwitchStateException{ - - private static final long serialVersionUID = 8452081020837079086L; - - public OFAuxException() { - super(); - } +public class HandshakeTimeoutException extends Exception { - public OFAuxException(String arg0) { - super(arg0); - } + private static final long serialVersionUID = 6859880268940337312L; - public OFAuxException(Throwable arg0) { - super(arg0); - } - -} \ No newline at end of file +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/HandshakeTimeoutHandler.java b/src/main/java/net/floodlightcontroller/core/internal/HandshakeTimeoutHandler.java index 89ef68109bdc715eb2c16ea3f59f6c2c7b33fc67..e89b3895c7e77ff7fde72e83e61aad25e1598fe1 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/HandshakeTimeoutHandler.java +++ b/src/main/java/net/floodlightcontroller/core/internal/HandshakeTimeoutHandler.java @@ -1,7 +1,7 @@ /** -* Copyright 2011, Big Switch Networks, Inc. +* Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University -* +* * 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 @@ -30,37 +30,36 @@ import org.jboss.netty.util.TimerTask; /** * Trigger a timeout if a switch fails to complete handshake soon enough */ -public class HandshakeTimeoutHandler +public class HandshakeTimeoutHandler extends SimpleChannelUpstreamHandler { - - static final HandshakeTimeoutException EXCEPTION = + static final HandshakeTimeoutException EXCEPTION = new HandshakeTimeoutException(); - - final OFChannelHandler handshakeHandler; + + final OFChannelHandler channelHandler; final Timer timer; final long timeoutNanos; volatile Timeout timeout; - - public HandshakeTimeoutHandler(OFChannelHandler handshakeHandler, + + public HandshakeTimeoutHandler(OFChannelHandler channelHandler, Timer timer, long timeoutSeconds) { super(); - this.handshakeHandler = handshakeHandler; + this.channelHandler = channelHandler; this.timer = timer; this.timeoutNanos = TimeUnit.SECONDS.toNanos(timeoutSeconds); } - + @Override public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { if (timeoutNanos > 0) { - timeout = timer.newTimeout(new HandshakeTimeoutTask(ctx), + timeout = timer.newTimeout(new HandshakeTimeoutTask(ctx), timeoutNanos, TimeUnit.NANOSECONDS); } ctx.sendUpstream(e); } - + @Override public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { @@ -69,7 +68,7 @@ public class HandshakeTimeoutHandler timeout = null; } } - + private final class HandshakeTimeoutTask implements TimerTask { private final ChannelHandlerContext ctx; @@ -87,7 +86,7 @@ public class HandshakeTimeoutHandler if (!ctx.getChannel().isOpen()) { return; } - if (!handshakeHandler.isSwitchHandshakeComplete()) + if (!channelHandler.isHandshakeComplete()) Channels.fireExceptionCaught(ctx, EXCEPTION); } } diff --git a/src/main/java/net/floodlightcontroller/core/internal/IAppHandshakePluginFactory.java b/src/main/java/net/floodlightcontroller/core/internal/IAppHandshakePluginFactory.java deleted file mode 100644 index fd45309f30208a9b965de54fa92c7b1e4f5859e2..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/IAppHandshakePluginFactory.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.floodlightcontroller.core.internal; - -/** - * This interface creates a contract used by the switch handshake handler. Each - * switch that is connected needs it's own running instance of the registered - * plugins. Thus is depends on a factory to churn out these instances. - * @author Jason Parraga <Jason.Parraga@bigswitch.com> - * - */ -public interface IAppHandshakePluginFactory { - - /** - * Create an instance of OFSwitchAppHandshakePlugin - * @return an instance of OFSwitchAppHandshakePlugin - */ - OFSwitchAppHandshakePlugin createPlugin(); -} - - diff --git a/src/main/java/net/floodlightcontroller/core/internal/INewOFConnectionListener.java b/src/main/java/net/floodlightcontroller/core/internal/INewOFConnectionListener.java deleted file mode 100644 index 9f03bc38e422bb3da8623b5e8bf2eddf245d7d90..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/INewOFConnectionListener.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import net.floodlightcontroller.core.IOFConnectionBackend; -import org.projectfloodlight.openflow.protocol.OFFeaturesReply; - -/** a listener that is notified when a new OFConnection has been opened and - * handshaked (i.e., the {@link OFFeaturesReply} has been received. - * - * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> - */ -public interface INewOFConnectionListener { - /** notify this listener that a new connection has been opened - * - * @param connection - the new connection - * @param featuresReply - the {@link OFFeaturesReply} that was received. - */ - void connectionOpened(IOFConnectionBackend connection, - OFFeaturesReply featuresReply); -} diff --git a/src/main/java/net/floodlightcontroller/core/internal/IOFConnectionListener.java b/src/main/java/net/floodlightcontroller/core/internal/IOFConnectionListener.java deleted file mode 100644 index f537e2d4fff00de8405346326f91fff9a93cba42..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/IOFConnectionListener.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import net.floodlightcontroller.core.IOFConnectionBackend; -import org.projectfloodlight.openflow.protocol.OFMessage; - -public interface IOFConnectionListener { - void connectionClosed(IOFConnectionBackend connection); - - void messageReceived(IOFConnectionBackend connection, OFMessage m); - - boolean isSwitchHandshakeComplete(IOFConnectionBackend connection); -} diff --git a/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchManager.java b/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchManager.java deleted file mode 100644 index 81a85346ca78e944cf2cf6dd4d52d6fb7d74ee16..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchManager.java +++ /dev/null @@ -1,124 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import java.util.List; - -import net.floodlightcontroller.core.FloodlightContext; -import net.floodlightcontroller.core.IOFConnectionBackend; -import net.floodlightcontroller.core.IOFSwitch.SwitchStatus; -import net.floodlightcontroller.core.IOFSwitchBackend; -import net.floodlightcontroller.core.IOFSwitchDriver; -import net.floodlightcontroller.core.LogicalOFMessageCategory; -import net.floodlightcontroller.core.PortChangeType; -import net.floodlightcontroller.core.SwitchDescription; - -import org.projectfloodlight.openflow.protocol.OFFactory; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.types.DatapathId; - -import com.google.common.collect.ImmutableList; - -public interface IOFSwitchManager { - - /** - * Called when a switch is added. - * @param sw the added switch - */ - void switchAdded(IOFSwitchBackend sw); - - /** - * Called when a switch disconnects - * @param sw the added switch - */ - void switchDisconnected(IOFSwitchBackend sw); - - /** - * Indicates that ports on the given switch have changed. Enqueue a - * switch update. - * @param sw the added switch - */ - void notifyPortChanged(IOFSwitchBackend sw, OFPortDesc port, - PortChangeType type); - - /** - * Relays to ISwitchDriverRegistry - */ - IOFSwitchBackend getOFSwitchInstance(IOFConnectionBackend connection, - SwitchDescription description, - OFFactory factory, - DatapathId datapathId); - - /** - * Relays an upstream message to the controller to dispatch to listeners. - * @param sw The switch the message was received on. - * @param m The message received. - * @param bContext the Floodlight context of the message, normally null in this case. - */ - void handleMessage(IOFSwitchBackend sw, OFMessage m, FloodlightContext bContext); - - /** - * Gets an unmodifiable collection of OFSwitchHandshakeHandlers - * @return an unmodifiable collection of OFSwitchHandshakeHandlers - */ - ImmutableList<OFSwitchHandshakeHandler> getSwitchHandshakeHandlers(); - - /** - * Adds an OFSwitch driver - * @param manufacturerDescriptionPrefix Register the given prefix - * with the driver. - * @param driver A IOFSwitchDriver instance to handle IOFSwitch instantiation - * for the given manufacturer description prefix - * @throws IllegalStateException If the the manufacturer description is - * already registered - * @throws NullPointerExeption if manufacturerDescriptionPrefix is null - * @throws NullPointerExeption if driver is null - */ - void addOFSwitchDriver(String manufacturerDescriptionPrefix, - IOFSwitchDriver driver); - - /** - * Handles all changes to the switch status. Will alert listeners and manage - * the internal switch map appropriately. - * @param sw the switch that has changed - * @param oldStatus the switch's old status - * @param newStatus the switch's new status - */ - void switchStatusChanged(IOFSwitchBackend sw, SwitchStatus oldStatus, - SwitchStatus newStatus); - - /** - * Gets the number of connections required by the application - * @return the number of connections required by the applications - */ - int getNumRequiredConnections(); - - /** - * Record a switch event in in-memory debug-event - * @param switchDpid - * @param reason Reason for this event - * @param flushNow see debug-event flushing in IDebugEventService - */ - public void addSwitchEvent(DatapathId switchDpid, String reason, boolean flushNow); - - /** - * Get the list of handshake plugins necessary for the switch handshake. - * @return the list of handshake plugins registered by applications. - */ - List<IAppHandshakePluginFactory> getHandshakePlugins(); - - /** - * Get the switch manager's counters - * @return the switch manager's counters - */ - SwitchManagerCounters getCounters(); - - /** - * Checks to see if the supplied category has been registered with the controller - * @param category the logical OF Message category to check or - * @return true if registered - */ - boolean isCategoryRegistered(LogicalOFMessageCategory category); - - void handshakeDisconnected(DatapathId dpid); - -} diff --git a/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchService.java b/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchService.java deleted file mode 100644 index 1896d27207e2ba176abbf063cc80aac279831e74..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchService.java +++ /dev/null @@ -1,111 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.IOFSwitchListener; -import net.floodlightcontroller.core.LogicalOFMessageCategory; -import net.floodlightcontroller.core.module.IFloodlightService; -import net.floodlightcontroller.core.rest.SwitchRepresentation; -import org.projectfloodlight.openflow.types.DatapathId; - -public interface IOFSwitchService extends IFloodlightService { - - /** - * Get's the switch map stored in the switch manager - * @return the map of switches known by the switch manager - */ - Map<DatapathId, IOFSwitch> getAllSwitchMap(); - - /** - * If the switch with the given DPID is known to any controller in the - * cluster, this method returns the associated IOFSwitch instance. As such - * the returned switches not necessarily connected or in master role for - * the local controller. - * - * Multiple calls to this method with the same DPID may return different - * IOFSwitch references. A caller must not store or otherwise rely on - * IOFSwitch references to be constant over the lifecycle of a switch. - * - * @param dpid the dpid of the switch to query - * @return the IOFSwitch instance associated with the dpid, null if no - * switch with the dpid is known to the cluster - */ - IOFSwitch getSwitch(DatapathId dpid); - - /** - * If the switch with the given DPID is known to any controller in the - * cluster, this method returns the associated IOFSwitch instance. As such - * the returned switches not necessarily connected or in master role for - * the local controller. - * - * Multiple calls to this method with the same DPID may return different - * IOFSwitch references. A caller must not store or otherwise rely on - * IOFSwitch references to be constant over the lifecycle of a switch. - * - * @param dpid the dpid of the switch to query - * @return the IOFSwitch instance associated with the dpid, null if no - * switch with the dpid is known to the cluster OR if the switch status - * is not considered visible. - */ - IOFSwitch getActiveSwitch(DatapathId dpid); - - /** - * Add a switch listener - * @param listener The module that wants to listen for events - */ - void addOFSwitchListener(IOFSwitchListener listener); - - /** - * Remove a switch listener - * @param listener The The module that no longer wants to listen for events - */ - void removeOFSwitchListener(IOFSwitchListener listener); - - /** - * Registers a logical OFMessage category to be used by an application - * @param category the logical OFMessage category - */ - void registerLogicalOFMessageCategory(LogicalOFMessageCategory category); - - /** - * Registers an app handshake plugin to be used during switch handshaking. - * @param plugin the app handshake plugin to be used during switch handshaking. - */ - void registerHandshakePlugin(IAppHandshakePluginFactory plugin); - - /** - * Get the REST representations of the active switches. - * @return Representation wrappers of the active switches. - */ - List<SwitchRepresentation> getSwitchRepresentations(); - - /** - * Get the REST representation of a switch. - * @param dpid the dpid of the desired switch representation. - * @return The switch representation. - */ - SwitchRepresentation getSwitchRepresentation(DatapathId dpid); - - /** - * Returns a snapshot of the set DPIDs for all known switches. - * - * The returned set is owned by the caller: the caller can modify it at - * will and changes to the known switches are not reflected in the returned - * set. The caller needs to call getAllSwitchDpids() if an updated - * version is needed. - * - * See {@link #getSwitch(long)} for what "known" switch is. - * @return the set of DPIDs of all known switches - */ - Set<DatapathId> getAllSwitchDpids(); - - /** - * Gets an immutable list of handshake handlers. - * @return an immutable list of handshake handlers. - */ - List<OFSwitchHandshakeHandler> getSwitchHandshakeHandlers(); - -} diff --git a/src/main/java/net/floodlightcontroller/core/internal/ISwitchDriverRegistry.java b/src/main/java/net/floodlightcontroller/core/internal/ISwitchDriverRegistry.java new file mode 100644 index 0000000000000000000000000000000000000000..6251fd26db9eab73cef4824c28ef20100d3b756f --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/internal/ISwitchDriverRegistry.java @@ -0,0 +1,60 @@ +package net.floodlightcontroller.core.internal; + +import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.core.IOFSwitchDriver; + +import org.openflow.protocol.statistics.OFDescriptionStatistics; + +/** + * Maintain a registry for SwitchDrivers. Drivers can register with the + * registry and a user can get IOFSwitch instances based on the switch's + * OFDescriptionStatistics. + * + * A driver registers itself by specifying a <i>prefix string</i> of the + * switch's <i>manufacturer</i> description. When a user request an + * IOFSwitch instance the registry matches the manufacturer description + * of the switch against the prefixes in the registry. + * + * See getOFSwitchInstance() for a description of the lookup contract + * + * @author gregor + * + */ +public interface ISwitchDriverRegistry { + + /** + * Register an IOFSwitchDriver with the registry + * + * @param manufacturerDescriptionPrefix Register the given prefix + * with the driver. + * @param driver A IOFSwitchDriver instance to handle IOFSwitch instaniation + * for the given manufacturer description prefix + * @throws IllegalStateException If the the manufacturer description is + * already registered + * @throws NullPointerExeption if manufacturerDescriptionPrefix is null + * @throws NullPointerExeption if driver is null + */ + void addSwitchDriver(String manufacturerDescriptionPrefix, + IOFSwitchDriver driver); + /** + * Return an IOFSwitch instance according to the description stats. + * + * The driver with the <i>longest matching prefix</i> will be picked first. + * The description is then handed over to the choosen driver to return an + * IOFSwitch instance. If the driver does not return an IOFSwitch + * (returns null) the registry will continue to the next driver with + * a matching manufacturer description prefix. If no driver returns an + * IOFSwitch instance the registry returns a default OFSwitchImpl instance. + * + * The returned switch will have its description reply and + * switch properties set according to the DescriptionStats passed in + * + * @param description The OFDescriptionStatistics for which to return an + * IOFSwitch implementation + * @return A IOFSwitch implementation matching the description or an + * OFSwitchImpl if no driver returned a more concrete instance. + * @throws NullPointerException If the OFDescriptionStatistics or any + * of its members is null. + */ + IOFSwitch getOFSwitchInstance(OFDescriptionStatistics description); +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/NaiiveSwitchDriverRegistry.java b/src/main/java/net/floodlightcontroller/core/internal/NaiiveSwitchDriverRegistry.java new file mode 100644 index 0000000000000000000000000000000000000000..8109e3add7ebc6f580596eccac975e14b3bf7c6c --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/internal/NaiiveSwitchDriverRegistry.java @@ -0,0 +1,105 @@ +package net.floodlightcontroller.core.internal; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.openflow.protocol.statistics.OFDescriptionStatistics; + +import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.core.IOFSwitchDriver; + +/** + * This implementation of ISwitchDriverRegistry uses a naiive algorithm to + * perform longest prefix matching on the manufacturere description prefixes + * + * We maintain a map that maps prefixes to the drivers as well as a sorted + * set that contains the prefixes sorted by their length. We exploit the fact + * that lexicographical order defines that shorter strings are always less + * than longer strings with the same prefix). Thus we can use reverse order for + * our purposes. + * To perform a lookup we iterate through the sorted set until we find a prefix + * that matches the manufacturer description. Since the set is sorted this + * will be the longest matching prefix. + * + * @author gregor + */ +class NaiiveSwitchDriverRegistry implements ISwitchDriverRegistry { + private final SortedSet<String> switchDescSorted; + private final Map<String,IOFSwitchDriver> switchBindingMap; + + public NaiiveSwitchDriverRegistry() { + switchBindingMap = new HashMap<String, IOFSwitchDriver>(); + switchDescSorted = new TreeSet<String>(Collections.reverseOrder()); + } + + @Override + public synchronized void addSwitchDriver(String manufacturerDescPrefix, + IOFSwitchDriver driver) { + if (manufacturerDescPrefix == null) { + throw new NullPointerException("manufacturerDescrptionPrefix" + + " must not be null"); + } + if (driver == null) { + throw new NullPointerException("driver must not be null"); + } + + IOFSwitchDriver existingDriver = switchBindingMap.get(manufacturerDescPrefix); + if (existingDriver != null ) { + throw new IllegalStateException("Failed to add OFSwitch driver for " + + manufacturerDescPrefix + "already registered"); + } + switchBindingMap.put(manufacturerDescPrefix, driver); + switchDescSorted.add(manufacturerDescPrefix); + } + + @Override + // TODO: instead of synchronized we could actually use a r/w lock + // but it's probably not worth it. + public synchronized IOFSwitch + getOFSwitchInstance(OFDescriptionStatistics description) { + if (description == null) + throw new NullPointerException("description must not be null"); + if (description.getHardwareDescription() == null) { + throw new NullPointerException( + "hardware description must not be null"); + } + if (description.getManufacturerDescription() == null) { + throw new NullPointerException( + "manufacturer description must not be null"); + } + if (description.getSerialNumber() == null) { + throw new NullPointerException( + "serial number must not be null"); + } + if (description.getDatapathDescription() == null) { + throw new NullPointerException( + "datapath description must not be null"); + } + if (description.getSoftwareDescription() == null) { + throw new NullPointerException( + "software description must not be null"); + } + + + // Find the appropriate driver + for (String descPrefix: switchDescSorted) { + if (description.getManufacturerDescription() + .startsWith(descPrefix)) { + IOFSwitchDriver driver = switchBindingMap.get(descPrefix); + IOFSwitch sw = driver.getOFSwitchImpl(description); + if (sw != null) { + sw.setSwitchProperties(description); + return sw; + } + } + } + // no switch found + IOFSwitch sw = new OFSwitchImpl(); + sw.setSwitchProperties(description); + return sw; + } + +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/NaiveSwitchDriverRegistry.java b/src/main/java/net/floodlightcontroller/core/internal/NaiveSwitchDriverRegistry.java deleted file mode 100644 index 6f2fffed183dd3e58ab59729a5f8d636ad3a9009..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/NaiveSwitchDriverRegistry.java +++ /dev/null @@ -1,102 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.SortedSet; -import java.util.TreeSet; - -import javax.annotation.Nonnull; - -import net.floodlightcontroller.core.IOFConnectionBackend; -import net.floodlightcontroller.core.IOFSwitchBackend; -import net.floodlightcontroller.core.IOFSwitchDriver; -import net.floodlightcontroller.core.OFSwitch; -import net.floodlightcontroller.core.SwitchDescription; -import org.projectfloodlight.openflow.protocol.OFFactory; -import org.projectfloodlight.openflow.types.DatapathId; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Preconditions; - -/** - * This implementation of ISwitchDriverRegistry uses a naive algorithm to - * perform longest prefix matching on the manufacturer description prefixes - * - * We maintain a map that maps prefixes to the drivers as well as a sorted - * set that contains the prefixes sorted by their length. We exploit the fact - * that lexicographical order defines that shorter strings are always less - * than longer strings with the same prefix). Thus we can use reverse order for - * our purposes. - * To perform a lookup we iterate through the sorted set until we find a prefix - * that matches the manufacturer description. Since the set is sorted this - * will be the longest matching prefix. - * - * @author gregor - */ -class NaiveSwitchDriverRegistry implements ISwitchDriverRegistry { - - protected static final Logger log = LoggerFactory.getLogger(NaiveSwitchDriverRegistry.class); - private final SortedSet<String> switchDescSorted; - private final Map<String,IOFSwitchDriver> switchBindingMap; - private final IOFSwitchManager switchManager; - - public NaiveSwitchDriverRegistry(@Nonnull IOFSwitchManager switchManager) { - Preconditions.checkNotNull(switchManager, "switchManager must not be null"); - this.switchManager = switchManager; - switchBindingMap = new HashMap<String, IOFSwitchDriver>(); - switchDescSorted = new TreeSet<String>(Collections.reverseOrder()); - } - - @Override - public synchronized void addSwitchDriver(@Nonnull String manufacturerDescPrefix, - @Nonnull IOFSwitchDriver driver) { - Preconditions.checkNotNull(manufacturerDescPrefix, "manufactererDescProfix"); - Preconditions.checkNotNull(driver, "driver"); - - IOFSwitchDriver existingDriver = switchBindingMap.get(manufacturerDescPrefix); - if (existingDriver != null ) { - throw new IllegalStateException("Failed to add OFSwitch driver for " - + manufacturerDescPrefix + "already registered"); - } - switchBindingMap.put(manufacturerDescPrefix, driver); - switchDescSorted.add(manufacturerDescPrefix); - } - - @Override - // TODO: instead of synchronized we could actually use a r/w lock - // but it's probably not worth it. - public synchronized IOFSwitchBackend - getOFSwitchInstance(@Nonnull IOFConnectionBackend connection, @Nonnull SwitchDescription description, - @Nonnull OFFactory factory, @Nonnull DatapathId id) { - Preconditions.checkNotNull(connection, "connection"); - Preconditions.checkNotNull(description, "description"); - Preconditions.checkNotNull(factory, "factory"); - Preconditions.checkNotNull(id, "id"); - - Preconditions.checkNotNull(description.getHardwareDescription(), "hardware description"); - Preconditions.checkNotNull(description.getManufacturerDescription(), "manufacturer description"); - Preconditions.checkNotNull(description.getSerialNumber(), "serial number"); - Preconditions.checkNotNull(description.getDatapathDescription(), "datapath description"); - Preconditions.checkNotNull(description.getSoftwareDescription(), "software description"); - - // Find the appropriate driver - for (String descPrefix: switchDescSorted) { - if (description.getManufacturerDescription() - .startsWith(descPrefix)) { - IOFSwitchDriver driver = switchBindingMap.get(descPrefix); - IOFSwitchBackend sw = driver.getOFSwitchImpl(description, factory); - if (sw != null) { - sw.setSwitchProperties(description); - return sw; - } - } - } - // no switch found - IOFSwitchBackend sw = new OFSwitch(connection, factory, switchManager, id); - sw.setSwitchProperties(description); - return sw; - } - -} diff --git a/src/main/java/net/floodlightcontroller/core/internal/NiciraRoleUtils.java b/src/main/java/net/floodlightcontroller/core/internal/NiciraRoleUtils.java deleted file mode 100644 index bf84d89a9047bf4bdc91bb635e6aecd11d0cbf69..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/NiciraRoleUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import org.projectfloodlight.openflow.protocol.OFControllerRole; -import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole; -import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply; - -/** static utilities to convert between Pre-OF1.2 "Nicira Style" roles and OF1.2+ OpenFlow - * standard roles. - * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> - */ -public class NiciraRoleUtils { - private NiciraRoleUtils() {} - - public static OFControllerRole niciraToOFRole(OFNiciraControllerRoleReply roleReply) { - switch(roleReply.getRole()) { - case ROLE_MASTER: - return OFControllerRole.ROLE_MASTER; - case ROLE_OTHER: - return OFControllerRole.ROLE_EQUAL; - case ROLE_SLAVE: - return OFControllerRole.ROLE_SLAVE; - default: - throw new IllegalArgumentException("unknown Nicira role value: " + roleReply.getRole()); - } - } - - public static OFNiciraControllerRole ofRoleToNiciraRole(OFControllerRole role) { - switch(role) { - case ROLE_EQUAL: - return OFNiciraControllerRole.ROLE_OTHER; - case ROLE_MASTER: - return OFNiciraControllerRole.ROLE_MASTER; - case ROLE_SLAVE: - return OFNiciraControllerRole.ROLE_SLAVE; - default: - throw new IllegalArgumentException("Unknown role: " + role); - } - } - -} diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..76f819ac904d35ceebd619c7c24da8eeae7e0324 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java @@ -0,0 +1,1788 @@ +package net.floodlightcontroller.core.internal; + +import java.io.IOException; +import java.nio.channels.ClosedChannelException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.RejectedExecutionException; + +import net.floodlightcontroller.core.FloodlightContext; +import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.core.IFloodlightProviderService.Role; +import net.floodlightcontroller.core.IOFSwitch.PortChangeEvent; +import net.floodlightcontroller.core.annotations.LogMessageDoc; +import net.floodlightcontroller.core.annotations.LogMessageDocs; +import net.floodlightcontroller.core.internal.Controller.Counters; +import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException; +import net.floodlightcontroller.storage.IResultSet; +import net.floodlightcontroller.storage.StorageException; +import net.floodlightcontroller.util.LoadMonitor; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler; +import org.jboss.netty.handler.timeout.IdleStateEvent; +import org.jboss.netty.handler.timeout.ReadTimeoutException; +import org.openflow.protocol.OFBarrierReply; +import org.openflow.protocol.OFBarrierRequest; +import org.openflow.protocol.OFEchoReply; +import org.openflow.protocol.OFEchoRequest; +import org.openflow.protocol.OFError; +import org.openflow.protocol.OFFeaturesReply; +import org.openflow.protocol.OFFlowRemoved; +import org.openflow.protocol.OFGetConfigReply; +import org.openflow.protocol.OFGetConfigRequest; +import org.openflow.protocol.OFHello; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFPortStatus; +import org.openflow.protocol.OFQueueGetConfigReply; +import org.openflow.protocol.OFSetConfig; +import org.openflow.protocol.OFStatisticsReply; +import org.openflow.protocol.OFStatisticsRequest; +import org.openflow.protocol.OFSwitchConfig; +import org.openflow.protocol.OFType; +import org.openflow.protocol.OFVendor; +import org.openflow.protocol.OFError.OFBadActionCode; +import org.openflow.protocol.OFError.OFBadRequestCode; +import org.openflow.protocol.OFError.OFErrorType; +import org.openflow.protocol.OFError.OFFlowModFailedCode; +import org.openflow.protocol.OFError.OFHelloFailedCode; +import org.openflow.protocol.OFError.OFPortModFailedCode; +import org.openflow.protocol.OFError.OFQueueOpFailedCode; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.factory.MessageParseException; +import org.openflow.protocol.statistics.OFDescriptionStatistics; +import org.openflow.protocol.statistics.OFStatistics; +import org.openflow.protocol.statistics.OFStatisticsType; +import org.openflow.util.HexString; +import org.openflow.vendor.nicira.OFNiciraVendorData; +import org.openflow.vendor.nicira.OFRoleReplyVendorData; +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; + + + +/** + * Channel handler deals with the switch connection and dispatches + * switch messages to the appropriate locations. + * @author readams + */ +class OFChannelHandler + extends IdleStateAwareChannelHandler { + + 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; + private Channel channel; + // State needs to be volatile because the HandshakeTimeoutHandler + // needs to check if the handshake is complete + private volatile ChannelState state; + private RoleChanger roleChanger; + private OFFeaturesReply featuresReply; + + private final ArrayList<OFPortStatus> pendingPortStatusMsg; + + /** transaction Ids to use during handshake. Since only one thread + * calls into the OFChannelHandler we don't need atomic. + * We will count down + */ + private int handshakeTransactionIds = -1; + + + + /** + * When we remove a pending role request and set the role on the switch + * we use this enum to indicate how we arrived at the decision. + * @author gregor + */ + private enum RoleRecvStatus { + /** We receveived a role reply message from the switch */ + RECEIVED_REPLY, + /** The switch returned an error indicated that roles are not + * supported*/ + UNSUPPORTED, + /** The request timed out */ + NO_REPLY; + } + /** + * A utility class to handle role requests and replies for this channel. + * After a role request is submitted the role changer keeps track of the + * pending request, collects the reply (if any) and times out the request + * if necessary. + * + * To simplify role handling we only keep track of the /last/ pending + * role reply send to the switch. If multiple requests are pending and + * we receive replies for earlier requests we ignore them. However, this + * way of handling pending requests implies that we could wait forever if + * a new request is submitted before the timeout triggers. If necessary + * we could work around that though. + * @author gregor + */ + private class RoleChanger { + // indicates that a request is currently pending + // needs to be volatile to allow correct double-check idiom + private volatile boolean requestPending; + // the transactiong Id of the pending request + private int pendingXid; + // the role that's pending + private Role pendingRole; + // system time in MS when we send the request + private long roleSubmitTime; + // the timeout to use + private final long roleTimeoutMs; + + public RoleChanger(long roleTimeoutMs) { + this.requestPending = false; + this.roleSubmitTime = 0; + this.pendingXid = -1; + this.pendingRole = null; + this.roleTimeoutMs = roleTimeoutMs; + } + + /** + * Send NX role request message to the switch requesting the specified + * role. + * + * @param sw switch to send the role request message to + * @param role role to request + */ + private int sendNxRoleRequest(Role role) + throws IOException { + + int xid = sw.getNextTransactionId(); + // Convert the role enum to the appropriate integer constant used + // in the NX role request message + int nxRole = role.toNxRole(); + + // Construct the role request message + OFVendor roleRequest = (OFVendor)BasicFactory.getInstance() + .getMessage(OFType.VENDOR); + roleRequest.setXid(xid); + roleRequest.setVendor(OFNiciraVendorData.NX_VENDOR_ID); + OFRoleRequestVendorData roleRequestData = new OFRoleRequestVendorData(); + roleRequestData.setRole(nxRole); + roleRequest.setVendorData(roleRequestData); + roleRequest.setLengthU(OFVendor.MINIMUM_LENGTH + + roleRequestData.getLength()); + + // Send it to the switch + sw.write(Collections.<OFMessage>singletonList(roleRequest), + new FloodlightContext()); + + return xid; + } + + /** + * Send a role request for the given role only if no other role + * request is currently pending. + * @param role The role to send to the switch. + * @throws IOException + */ + synchronized void sendRoleRequestIfNotPending(Role role) + throws IOException { + if (!requestPending) + sendRoleRequest(role); + else + counters.roleNotResentBecauseRolePending.updateCounterWithFlush(); + } + + /** + * Send a role request with the given role to the switch. + * + * Send a role request with the given role to the switch and update + * the pending request and timestamp. + * + * @param role + * @throws IOException + */ + synchronized void sendRoleRequest(Role role) throws IOException { + /* + * There are three cases to consider for SUPPORTS_NX_ROLE: + * + * 1) unset. We have neither received a role reply from the + * switch nor has a request timed out. Send a request. + * 2) TRUE: We've already send a request earlier and received + * a reply. The switch supports role and we should send one. + * 3) FALSE: We have already send a role and received an error. + * The switch does not support roles. Don't send a role request, + * set the switch's role directly. + */ + Boolean supportsNxRole = (Boolean) + sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE); + if ((supportsNxRole != null) && !supportsNxRole) { + setSwitchRole(role, RoleRecvStatus.UNSUPPORTED); + } else { + pendingXid = sendNxRoleRequest(role); + pendingRole = role; + roleSubmitTime = System.currentTimeMillis(); + requestPending = true; + } + } + + /** + * Deliver a received role reply and set SWITCH_SUPPORTS_NX_ROLE. + * + * Check if a request is pending and if the received reply matches the + * the expected pending reply (we check both role and xid) we set + * the role for the switch/channel. + * + * If a request is pending but doesn't match the reply we ignore it. + * + * If no request is pending we disconnect. + * + * @param xid + * @param role + * @throws SwitchStateException if no request is pending + */ + synchronized void deliverRoleReply(int xid, Role role) { + if (!requestPending) { + // Maybe don't disconnect if the role reply we received is + // for the same role we are already in. + String msg = String.format("Switch: [%s], State: [%s], " + + "received unexpected RoleReply[%s]. " + + "No roles are pending", + OFChannelHandler.this.getSwitchInfoString(), + OFChannelHandler.this.state.toString(), + role); + throw new SwitchStateException(msg); + } + + if (pendingXid == xid && pendingRole == role) { + log.debug("Received role reply message from {}, setting role to {}", + getSwitchInfoString(), role); + counters.roleReplyReceived.updateCounterWithFlush(); + setSwitchRole(role, RoleRecvStatus.RECEIVED_REPLY); + } else { + log.debug("Received stale or unexpected role reply from " + + "switch {} ({}, xid={}). Ignoring. " + + "Waiting for {}, xid={}", + new Object[] { getSwitchInfoString(), role, xid, + pendingRole, pendingXid }); + } + } + + /** + * Called if we receive an error message. If the xid matches the + * pending request we handle it otherwise we ignore it. We also + * set SWITCH_SUPPORTS_NX_ROLE to false. + * + * Note: since we only keep the last pending request we might get + * error messages for earlier role requests that we won't be able + * to handle + * @param xid + * @return true if the error was handled by us, false otherwise + * @throws SwitchStateException if the error was for the pending + * role request but was unexpected + */ + synchronized boolean deliverError(OFError error) { + if (!requestPending) + return false; + + if (pendingXid == error.getXid()) { + boolean isBadRequestError = + (error.getErrorType() == OFError.OFErrorType. + OFPET_BAD_REQUEST.getValue()); + if (isBadRequestError) { + counters.roleReplyErrorUnsupported.updateCounterWithFlush(); + setSwitchRole(pendingRole, RoleRecvStatus.UNSUPPORTED); + } else { + // TODO: Is this the right thing to do if we receive + // some other error besides a bad request error? + // Presumably that means the switch did actually + // understand the role request message, but there + // was some other error from processing the message. + // OF 1.2 specifies a OFPET_ROLE_REQUEST_FAILED + // error code, but it doesn't look like the Nicira + // role request has that. Should check OVS source + // code to see if it's possible for any other errors + // to be returned. + // If we received an error the switch is not + // in the correct role, so we need to disconnect it. + // We could also resend the request but then we need to + // check if there are other pending request in which + // case we shouldn't resend. If we do resend we need + // to make sure that the switch eventually accepts one + // of our requests or disconnect the switch. This feels + // cumbersome. + String msg = String.format("Switch: [%s], State: [%s], " + + "Unexpected error %s in respone to our " + + "role request for %s.", + OFChannelHandler.this.getSwitchInfoString(), + OFChannelHandler.this.state.toString(), + getErrorString(error), + pendingRole); + throw new SwitchStateException(msg); + } + return true; + } + return false; + } + + /** + * Check if a pending role request has timed out. + */ + void checkTimeout() { + if (!requestPending) + return; + synchronized(this) { + if (!requestPending) + return; + long now = System.currentTimeMillis(); + if (now - roleSubmitTime > roleTimeoutMs) { + // timeout triggered. + counters.roleReplyTimeout.updateCounterWithFlush(); + setSwitchRole(pendingRole, RoleRecvStatus.NO_REPLY); + } + } + } + + /** + * Set the role for this switch / channel. + * + * If the status indicates that we received a reply we set the role. + * If the status indicates otherwise we disconnect the switch if + * the role is SLAVE. + * + * "Setting a role" means setting the appropriate ChannelState, + * setting the flags on the switch and + * notifying Controller.java about new role of the switch + * + * @param role The role to set. + * @param status How we derived at the decision to set this status. + */ + synchronized private void setSwitchRole(Role role, RoleRecvStatus status) { + requestPending = false; + if (status == RoleRecvStatus.RECEIVED_REPLY) + sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true); + else + sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); + sw.setHARole(role); + + if (role != Role.SLAVE) { + OFChannelHandler.this.setState(ChannelState.MASTER); + // TODO: should we really activate the switch again if it's + // already master?? + if (log.isDebugEnabled()) { + log.debug("Switch {} activated. Role is now MASTER", + getSwitchInfoString()); + } + controller.switchActivated(OFChannelHandler.this.sw); + } else { + OFChannelHandler.this.setState(ChannelState.SLAVE); + if (status != RoleRecvStatus.RECEIVED_REPLY) { + if (log.isDebugEnabled()) { + log.debug("Disconnecting switch {}. Doesn't support role" + + "({}) request and controller is now SLAVE", + getSwitchInfoString(), status); + } + // the disconnect will trigger a switch removed to + // controller so no need to signal anything else + sw.disconnectOutputStream(); + } else { + if (log.isDebugEnabled()) { + log.debug("Switch {} is now SLAVE", + getSwitchInfoString()); + } + controller.switchDeactivated(OFChannelHandler.this.sw); + } + } + } + } + + + /** + * The state machine for handling the switch/channel state. + * @author gregor + */ + enum ChannelState { + /** + * Initial state before channel is connected. + */ + INIT(false) { + @Override + void + processOFMessage(OFChannelHandler h, OFMessage m) + throws IOException { + illegalMessageReceived(h, m); + } + + @Override + void processOFError(OFChannelHandler h, OFError m) + throws IOException { + // need to implement since its abstract but it will never + // be called + } + + @Override + void processOFPortStatus(OFChannelHandler h, OFPortStatus m) + throws IOException { + unhandledMessageReceived(h, m); + } + }, + + /** + * We send a HELLO to the switch and wait for a reply. + * Once we receive the reply we send an OFFeaturesRequest and + * a request to clear all FlowMods. + * Next state is WAIT_FEATURES_REPLY + */ + WAIT_HELLO(false) { + @Override + void processOFHello(OFChannelHandler h, OFHello m) + throws IOException { + h.sendHandShakeMessage(OFType.FEATURES_REQUEST); + h.setState(WAIT_FEATURES_REPLY); + } + @Override + void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) + throws IOException { + illegalMessageReceived(h, m); + } + @Override + void processOFStatisticsReply(OFChannelHandler h, + OFStatisticsReply m) + throws IOException { + illegalMessageReceived(h, m); + } + @Override + void processOFError(OFChannelHandler h, OFError m) { + logErrorDisconnect(h, m); + } + + @Override + void processOFPortStatus(OFChannelHandler h, OFPortStatus m) + throws IOException { + unhandledMessageReceived(h, m); + } + }, + + /** + * 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 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); + } + + @Override + void processOFPortStatus(OFChannelHandler h, OFPortStatus m) + throws IOException { + unhandledMessageReceived(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, + OFStatisticsReply m) + 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); + } + }, + + /** + * We are waiting for a config reply message. Once we receive it + * we send a DescriptionStatsRequest to the switch. + * Next state: WAIT_DESCRIPTION_STAT_REPLY + */ + WAIT_CONFIG_REPLY(false) { + @Override + @LogMessageDocs({ + @LogMessageDoc(level="WARN", + message="Config Reply from {switch} has " + + "miss length set to {length}", + explanation="The controller requires that the switch " + + "use a miss length of 0xffff for correct " + + "function", + recommendation="Use a different switch to ensure " + + "correct function") + }) + void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m) + throws IOException { + if (m.getMissSendLength() == (short)0xffff) { + log.trace("Config Reply from switch {} confirms " + + "miss length set to 0xffff", + h.getSwitchInfoString()); + } else { + // FIXME: we can't really deal with switches that don't send + // full packets. Shouldn't we drop the connection here? + // FIXME: count?? + log.warn("Config Reply from switch {} has" + + "miss length set to {}", + h.getSwitchInfoString(), + m.getMissSendLength()); + } + h.sendHandshakeDescriptionStatsRequest(); + h.setState(WAIT_DESCRIPTION_STAT_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, + OFStatisticsReply m) + throws IOException { + illegalMessageReceived(h, m); + } + + @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); + } + + @Override + void processOFPortStatus(OFChannelHandler h, OFPortStatus m) + throws IOException { + h.pendingPortStatusMsg.add(m); + } + }, + + /** + * We are waiting for a OFDescriptionStat message from teh switch. + * Once we receive any stat message we try to parse it. If it's not + * a description stats message we disconnect. If its the expected + * description stats message, we: + * - use the switch driver to bind the switch and get an IOFSwitch + * instance, setup the switch instance + * - setup the IOFSwitch instance + * - add switch to FloodlightProvider and send the intial role + * request to the switch. + * Next state: WAIT_INITIAL_ROLE + * All following states will have a h.sw instance! + */ + WAIT_DESCRIPTION_STAT_REPLY(false) { + @LogMessageDoc(message="Switch {switch info} bound to class " + + "{switch driver}, description {switch description}", + explanation="The specified switch has been bound to " + + "a switch driver based on the switch description" + + "received from the switch") + @Override + void processOFStatisticsReply(OFChannelHandler h, + OFStatisticsReply m) { + // Read description, if it has been updated + OFDescriptionStatistics description = + new OFDescriptionStatistics(); + ChannelBuffer data = + ChannelBuffers.buffer(description.getLength()); + OFStatistics f = m.getFirstStatistics(); + f.writeTo(data); + description.readFrom(data); + h.sw = h.controller.getOFSwitchInstance(description); + // set switch information + // set features reply and channel first so we a DPID and + // channel info. + h.sw.setFeaturesReply(h.featuresReply); + h.sw.setConnected(true); + h.sw.setChannel(h.channel); + h.sw.setFloodlightProvider(h.controller); + h.sw.setThreadPoolService(h.controller.getThreadPoolService()); + try { + h.sw.setDebugCounterService(h.controller.getDebugCounter()); + } catch (CounterException e) { + h.counters.switchCounterRegistrationFailed + .updateCounterNoFlush(); + log.warn("Could not register counters for switch {} ", + h.getSwitchInfoString(), e); + } + h.sw.setAccessFlowPriority(h.controller.getAccessFlowPriority()); + h.sw.setCoreFlowPriority(h.controller.getCoreFlowPriority()); + for (OFPortStatus ps: h.pendingPortStatusMsg) + handlePortStatusMessage(h, ps, false); + h.pendingPortStatusMsg.clear(); + h.readPropertyFromStorage(); + log.info("Switch {} bound to class {}, writeThrottle={}," + + " description {}", + new Object[] { h.sw, h.sw.getClass(), + h.sw.isWriteThrottleEnabled(), + description }); + h.sw.startDriverHandshake(); + if (h.sw.isDriverHandshakeComplete()) + h.gotoWaitInitialRoleState(); + else + h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE); + } + + @Override + void processOFError(OFChannelHandler h, OFError m) { + logErrorDisconnect(h, m); + } + + @Override + void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) + throws IOException { + // TODO: we could re-set the features reply + illegalMessageReceived(h, m); + } + + @Override + void processOFPortStatus(OFChannelHandler h, OFPortStatus m) + throws IOException { + h.pendingPortStatusMsg.add(m); + } + }, + + WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(false) { + @Override + void processOFError(OFChannelHandler h, OFError m) + throws IOException { + // will never be called. We override processOFMessage + } + + @Override + void processOFMessage(OFChannelHandler h, OFMessage m) + throws IOException { + if (m.getType() == OFType.ECHO_REQUEST) + processOFEchoRequest(h, (OFEchoRequest)m); + else { + // FIXME: other message to handle here? + h.sw.processDriverHandshakeMessage(m); + if (h.sw.isDriverHandshakeComplete()) { + h.gotoWaitInitialRoleState(); + } + } + } + + @Override + void processOFPortStatus(OFChannelHandler h, OFPortStatus m) + throws IOException { + handlePortStatusMessage(h, m, false); + } + }, + + /** + * We are waiting for the intial role reply message (or error + * indication) from the switch. + * Next State: MASTER or SLAVE + */ + WAIT_INITIAL_ROLE(false) { + @Override + void processOFError(OFChannelHandler h, OFError m) { + // role changer will ignore the error if it isn't for it + boolean didHandle = h.roleChanger.deliverError(m); + if (!didHandle) { + logError(h, m); + } + } + + @Override + void processOFVendor(OFChannelHandler h, OFVendor m) + throws IOException { + Role role = extractNiciraRoleReply(h, m); + // If role == null it measn the message wasn't really a + // Nicira role reply. We ignore this case. + if (role != null) + h.roleChanger.deliverRoleReply(m.getXid(), role); + else + unhandledMessageReceived(h, m); + } + + @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, + OFStatisticsReply m) { + illegalMessageReceived(h, m); + } + + @Override + void processOFPortStatus(OFChannelHandler h, OFPortStatus m) + throws IOException { + handlePortStatusMessage(h, m, false); + + } + }, + + /** + * The switch is in MASTER role. We enter this state after a role + * reply from the switch is received (or the controller is MASTER + * and the switch doesn't support roles). The handshake is complete at + * this point. We only leave this state if the switch disconnects or + * if we send a role request for SLAVE /and/ receive the role reply for + * SLAVE. + */ + MASTER(true) { + @LogMessageDoc(level="WARN", + message="Received permission error from switch {} while" + + "being master. Reasserting master role.", + explanation="The switch has denied an operation likely " + + "indicating inconsistent controller roles", + recommendation="This situation can occurs transiently during role" + + " changes. If, however, the condition persists or happens" + + " frequently this indicates a role inconsistency. " + + LogMessageDoc.CHECK_CONTROLLER ) + @Override + void processOFError(OFChannelHandler h, OFError m) + throws IOException { + // role changer will ignore the error if it isn't for it + boolean didHandle = h.roleChanger.deliverError(m); + if (didHandle) + return; + if (m.getErrorType() == + OFErrorType.OFPET_BAD_REQUEST.getValue() && + m.getErrorCode() == + OFBadRequestCode.OFPBRC_EPERM.ordinal()) { + // We are the master controller and the switch returned + // a permission error. This is a likely indicator that + // the switch thinks we are slave. Reassert our + // role + // FIXME: this could be really bad during role transitions + // if two controllers are master (even if its only for + // a brief period). We might need to see if these errors + // persist before we reassert + h.counters.epermErrorWhileSwitchIsMaster.updateCounterWithFlush(); + log.warn("Received permission error from switch {} while" + + "being master. Reasserting master role.", + h.getSwitchInfoString()); + h.controller.reassertRole(h, Role.MASTER); + } + else if (m.getErrorType() == + OFErrorType.OFPET_PORT_MOD_FAILED.getValue() && + m.getErrorCode() == + OFFlowModFailedCode.OFPFMFC_ALL_TABLES_FULL.ordinal()) { + h.sw.setTableFull(true); + } + else { + logError(h, m); + } + h.dispatchMessage(m); + } + + @Override + void processOFStatisticsReply(OFChannelHandler h, + OFStatisticsReply m) { + h.sw.deliverStatisticsReply(m); + } + + @Override + void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) + throws IOException { + h.sw.setFeaturesReply(m); + h.sw.deliverOFFeaturesReply(m); + } + + @Override + void processOFVendor(OFChannelHandler h, OFVendor m) + throws IOException { + Role role = extractNiciraRoleReply(h, m); + // If role == null it means the message wasn't really a + // Nicira role reply. We ignore just dispatch it to the + // OFMessage listenersa in this case. + if (role != null) + h.roleChanger.deliverRoleReply(m.getXid(), role); + else + h.dispatchMessage(m); + } + + @Override + void processOFPortStatus(OFChannelHandler h, OFPortStatus m) + throws IOException { + handlePortStatusMessage(h, m, true); + } + + @Override + void processOFPacketIn(OFChannelHandler h, OFPacketIn m) throws IOException { + h.dispatchMessage(m); + } + + @Override + void processOFFlowRemoved(OFChannelHandler h, + OFFlowRemoved m) throws IOException { + h.dispatchMessage(m); + } + @Override + void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) throws IOException{ + h.dispatchMessage(m); + } + }, + + /** + * The switch is in SLAVE role. We enter this state after a role + * reply from the switch is received. The handshake is complete at + * this point. We only leave this state if the switch disconnects or + * if we send a role request for MASTER /and/ receive the role reply for + * MASTER. + * TODO: CURRENTLY, WE DO NOT DISPATCH ANY MESSAGE IN SLAVE. + */ + SLAVE(true) { + @Override + void processOFError(OFChannelHandler h, OFError m) + throws IOException { + // role changer will ignore the error if it isn't for it + boolean didHandle = h.roleChanger.deliverError(m); + if (!didHandle) { + logError(h, m); + } + } + + + + @Override + void processOFStatisticsReply(OFChannelHandler h, + OFStatisticsReply m) { + // FIXME. + h.sw.deliverStatisticsReply(m); + } + + @Override + void processOFVendor(OFChannelHandler h, OFVendor m) + throws IOException { + Role role = extractNiciraRoleReply(h, m); + // If role == null it means the message wasn't really a + // Nicira role reply. We ignore it. + if (role != null) + h.roleChanger.deliverRoleReply(m.getXid(), role); + else + unhandledMessageReceived(h, m); + } + + @Override + void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) + throws IOException { + // do nothing + } + + @Override + void processOFPortStatus(OFChannelHandler h, OFPortStatus m) + throws IOException { + // do nothing + } + + @Override + @LogMessageDoc(level="WARN", + message="Received PacketIn from switch {} while" + + "being slave. Reasserting slave role.", + explanation="The switch has receive a PacketIn despite being " + + "in slave role indicating inconsistent controller roles", + recommendation="This situation can occurs transiently during role" + + " changes. If, however, the condition persists or happens" + + " frequently this indicates a role inconsistency. " + + LogMessageDoc.CHECK_CONTROLLER ) + void processOFPacketIn(OFChannelHandler h, OFPacketIn m) throws IOException { + // we don't expect packetIn while slave, reassert we are slave + h.counters.packetInWhileSwitchIsSlave.updateCounterNoFlush(); + log.warn("Received PacketIn from switch {} while" + + "being slave. Reasserting slave role.", h.sw); + h.controller.reassertRole(h, Role.SLAVE); + } + }; + + private final boolean handshakeComplete; + ChannelState(boolean handshakeComplete) { + this.handshakeComplete = handshakeComplete; + } + + /** + * Is this a state in which the handshake has completed? + * @return true if the handshake is complete + */ + public boolean isHandshakeComplete() { + return handshakeComplete; + } + + /** + * Get a string specifying the switch connection, state, and + * message received. To be used as message for SwitchStateException + * or log messages + * @param h The channel handler (to get switch information_ + * @param m The OFMessage that has just been received + * @param details A string giving more details about the exact nature + * of the problem. + * @return + */ + // needs to be protected because enum members are acutally subclasses + protected String getSwitchStateMessage(OFChannelHandler h, + OFMessage m, + String details) { + return String.format("Switch: [%s], State: [%s], received: [%s]" + + ", details: %s", + h.getSwitchInfoString(), + this.toString(), + m.getType().toString(), + details); + } + + /** + * We have an OFMessage we didn't expect given the current state and + * we want to treat this as an error. + * We currently throw an exception that will terminate the connection + * However, we could be more forgiving + * @param h the channel handler that received the message + * @param m the message + * @throws SwitchStateExeption we always through the execption + */ + // needs to be protected because enum members are acutally subclasses + protected void illegalMessageReceived(OFChannelHandler h, OFMessage m) { + String msg = getSwitchStateMessage(h, m, + "Switch should never send this message in the current state"); + throw new SwitchStateException(msg); + + } + + /** + * We have an OFMessage we didn't expect given the current state and + * we want to ignore the message + * @param h the channel handler the received the message + * @param m the message + */ + protected void unhandledMessageReceived(OFChannelHandler h, + OFMessage m) { + h.counters.unhandledMessage.updateCounterNoFlush(); + if (log.isDebugEnabled()) { + String msg = getSwitchStateMessage(h, m, + "Ignoring unexpected message"); + log.debug(msg); + } + } + + /** + * Log an OpenFlow error message from a switch + * @param sw The switch that sent the error + * @param error The error message + */ + @LogMessageDoc(level="ERROR", + message="Error {error type} {error code} from {switch} " + + "in state {state}", + explanation="The switch responded with an unexpected error" + + "to an OpenFlow message from the controller", + recommendation="This could indicate improper network operation. " + + "If the problem persists restarting the switch and " + + "controller may help." + ) + protected void logError(OFChannelHandler h, OFError error) { + log.error("{} from switch {} in state {}", + new Object[] { + getErrorString(error), + h.getSwitchInfoString(), + this.toString()}); + } + + /** + * Log an OpenFlow error message from a switch and disconnect the + * channel + * @param sw The switch that sent the error + * @param error The error message + */ + protected void logErrorDisconnect(OFChannelHandler h, OFError error) { + logError(h, error); + h.channel.disconnect(); + } + + + /** + * Extract the role from an OFVendor message. + * + * Extract the role from an OFVendor message if the message is a + * Nicira role reply. Otherwise return null. + * + * @param h The channel handler receiving the message + * @param vendorMessage The vendor message to parse. + * @return The role in the message if the message is a Nicira role + * reply, null otherwise. + * @throws SwitchStateException If the message is a Nicira role reply + * but the numeric role value is unknown. + * FIXME: The message parser should make sure that the Nicira role is + * actually valid. Why do we need to take care of it ?!? + */ + protected Role extractNiciraRoleReply(OFChannelHandler h, + OFVendor vendorMessage) { + int vendor = vendorMessage.getVendor(); + if (vendor != OFNiciraVendorData.NX_VENDOR_ID) + return null; + if (! (vendorMessage.getVendorData() instanceof OFRoleReplyVendorData)) + return null; + OFRoleReplyVendorData roleReplyVendorData = + (OFRoleReplyVendorData) vendorMessage.getVendorData(); + Role role = Role.fromNxRole(roleReplyVendorData.getRole()); + if (role == null) { + String msg = String.format("Switch: [%s], State: [%s], " + + "received NX_ROLE_REPLY with invalid role " + + "value %d", + h.getSwitchInfoString(), + this.toString(), + roleReplyVendorData.getRole()); + throw new SwitchStateException(msg); + } + return role; + } + + /** + * Handle a port status message. + * + * Handle a port status message by updating the port maps in the + * IOFSwitch instance and notifying Controller about the change so + * it can dispatch a switch update. + * + * @param h The OFChannelHhandler that received the message + * @param m The PortStatus message we received + * @param doNotify if true switch port changed events will be + * dispatched + */ + protected void handlePortStatusMessage(OFChannelHandler h, + OFPortStatus m, + boolean doNotify) { + if (h.sw == null) { + String msg = getSwitchStateMessage(h, m, + "State machine error: switch is null. Should never " + + "happen"); + throw new SwitchStateException(msg); + } + Collection<PortChangeEvent> changes = h.sw.processOFPortStatus(m); + if (doNotify) { + for (PortChangeEvent ev: changes) + h.controller.notifyPortChanged(h.sw, ev.port, ev.type); + } + } + + /** + * Process an OF message received on the channel and + * update state accordingly. + * + * The main "event" of the state machine. Process the received message, + * send follow up message if required and update state if required. + * + * Switches on the message type and calls more specific event handlers + * for each individual OF message type. If we receive a message that + * is supposed to be sent from a controller to a switch we throw + * a SwitchStateExeption. + * + * The more specific handlers can also throw SwitchStateExceptions + * + * @param h The OFChannelHandler that received the message + * @param m The message we received. + * @throws SwitchStateException + * @throws IOException + */ + void processOFMessage(OFChannelHandler h, OFMessage m) throws IOException { + h.roleChanger.checkTimeout(); + switch(m.getType()) { + case HELLO: + processOFHello(h, (OFHello)m); + break; + case BARRIER_REPLY: + processOFBarrierReply(h, (OFBarrierReply)m); + break; + case ECHO_REPLY: + processOFEchoReply(h, (OFEchoReply)m); + break; + case ECHO_REQUEST: + processOFEchoRequest(h, (OFEchoRequest)m); + break; + case ERROR: + processOFError(h, (OFError)m); + break; + case FEATURES_REPLY: + processOFFeaturesReply(h, (OFFeaturesReply)m); + break; + case FLOW_REMOVED: + processOFFlowRemoved(h, (OFFlowRemoved)m); + break; + case GET_CONFIG_REPLY: + processOFGetConfigReply(h, (OFGetConfigReply)m); + break; + case PACKET_IN: + processOFPacketIn(h, (OFPacketIn)m); + break; + case PORT_STATUS: + processOFPortStatus(h, (OFPortStatus)m); + break; + case QUEUE_GET_CONFIG_REPLY: + processOFQueueGetConfigReply(h, (OFQueueGetConfigReply)m); + break; + case STATS_REPLY: + processOFStatisticsReply(h, (OFStatisticsReply)m); + break; + case VENDOR: + processOFVendor(h, (OFVendor)m); + break; + // The following messages are sent to switches. The controller + // should never receive them + case SET_CONFIG: + case GET_CONFIG_REQUEST: + case PACKET_OUT: + case PORT_MOD: + case QUEUE_GET_CONFIG_REQUEST: + case BARRIER_REQUEST: + case STATS_REQUEST: + case FEATURES_REQUEST: + case FLOW_MOD: + illegalMessageReceived(h, m); + break; + } + } + + /*----------------------------------------------------------------- + * Default implementation for message handlers in any state. + * + * Individual states must override these if they want a behavior + * that differs from the default. + * + * In general, these handlers simply ignore the message and do + * nothing. + * + * There are some exceptions though, since some messages really + * are handled the same way in every state (e.g., ECHO_REQUST) or + * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY + -----------------------------------------------------------------*/ + + void processOFHello(OFChannelHandler h, OFHello m) throws IOException { + // we only expect hello in the WAIT_HELLO state + illegalMessageReceived(h, m); + } + + void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) + throws IOException { + // Silently ignore. + } + + void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m) + throws IOException { + OFEchoReply reply = (OFEchoReply) + BasicFactory.getInstance().getMessage(OFType.ECHO_REPLY); + reply.setXid(m.getXid()); + reply.setPayload(m.getPayload()); + reply.setLengthU(m.getLengthU()); + h.channel.write(Collections.singletonList(reply)); + } + + void processOFEchoReply(OFChannelHandler h, OFEchoReply m) + throws IOException { + // Do nothing with EchoReplies !! + } + + // no default implementation for OFError + // every state must override it + abstract void processOFError(OFChannelHandler h, OFError m) + throws IOException; + + + void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) + throws IOException { + unhandledMessageReceived(h, m); + } + + void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m) + throws IOException { + unhandledMessageReceived(h, m); + } + + void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m) + throws IOException { + // we only expect config replies in the WAIT_CONFIG_REPLY state + // TODO: might use two different strategies depending on whether + // we got a miss length of 64k or not. + illegalMessageReceived(h, m); + } + + void processOFPacketIn(OFChannelHandler h, OFPacketIn m) + throws IOException { + unhandledMessageReceived(h, m); + } + + // bi default implementation. Every state needs to handle it. + abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m) + throws IOException; + + void processOFQueueGetConfigReply(OFChannelHandler h, + OFQueueGetConfigReply m) + throws IOException { + unhandledMessageReceived(h, m); + } + + void processOFStatisticsReply(OFChannelHandler h, OFStatisticsReply m) + throws IOException { + unhandledMessageReceived(h, m); + } + + void processOFVendor(OFChannelHandler h, OFVendor m) + throws IOException { + // TODO: it might make sense to parse the vendor message here + // into the known vendor messages we support and then call more + // spefic event handlers + unhandledMessageReceived(h, m); + } + } + + + /** + * Create a new unconnecte OFChannelHandler. + * @param controller + */ + OFChannelHandler(Controller controller) { + this.controller = controller; + this.counters = controller.getCounters(); + this.roleChanger = new RoleChanger(DEFAULT_ROLE_TIMEOUT_MS); + this.state = ChannelState.INIT; + this.pendingPortStatusMsg = new ArrayList<OFPortStatus>(); + } + + /** + * Is this a state in which the handshake has completed? + * @return true if the handshake is complete + */ + boolean isHandshakeComplete() { + return this.state.isHandshakeComplete(); + } + + /** + * Forwards to RoleChanger. See there. + * @param role + */ + void sendRoleRequestIfNotPending(Role role) { + try { + roleChanger.sendRoleRequestIfNotPending(role); + } catch (IOException e) { + log.error("Disconnecting switch {} due to IO Error: {}", + getSwitchInfoString(), e.getMessage()); + channel.close(); + } + } + + /** + * Forwards to RoleChanger. See there. + * @param role + */ + void sendRoleRequest(Role role) { + try { + roleChanger.sendRoleRequest(role); + } catch (IOException e) { + log.error("Disconnecting switch {} due to IO Error: {}", + getSwitchInfoString(), e.getMessage()); + channel.close(); + } + } + + + @Override + @LogMessageDoc(message="New switch connection from {ip address}", + explanation="A new switch has connected from the " + + "specified IP address") + public void channelConnected(ChannelHandlerContext ctx, + ChannelStateEvent e) throws Exception { + counters.switchConnected.updateCounterWithFlush(); + channel = e.getChannel(); + log.info("New switch connection from {}", + channel.getRemoteAddress()); + sendHandShakeMessage(OFType.HELLO); + setState(ChannelState.WAIT_HELLO); + } + + @Override + @LogMessageDoc(message="Disconnected switch {switch information}", + explanation="The specified switch has disconnected.") + public void channelDisconnected(ChannelHandlerContext ctx, + ChannelStateEvent e) throws Exception { + controller.removeSwitchChannel(this); + 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()); + } + + @Override + @LogMessageDocs({ + @LogMessageDoc(level="ERROR", + message="Disconnecting switch {switch} due to read timeout", + explanation="The connected switch has failed to send any " + + "messages or respond to echo requests", + recommendation=LogMessageDoc.CHECK_SWITCH), + @LogMessageDoc(level="ERROR", + message="Disconnecting switch {switch}: failed to " + + "complete handshake", + explanation="The switch did not respond correctly " + + "to handshake messages", + recommendation=LogMessageDoc.CHECK_SWITCH), + @LogMessageDoc(level="ERROR", + message="Disconnecting switch {switch} due to IO Error: {}", + explanation="There was an error communicating with the switch", + recommendation=LogMessageDoc.CHECK_SWITCH), + @LogMessageDoc(level="ERROR", + message="Disconnecting switch {switch} due to switch " + + "state error: {error}", + explanation="The switch sent an unexpected message", + recommendation=LogMessageDoc.CHECK_SWITCH), + @LogMessageDoc(level="ERROR", + message="Disconnecting switch {switch} due to " + + "message parse failure", + explanation="Could not parse a message from the switch", + recommendation=LogMessageDoc.CHECK_SWITCH), + @LogMessageDoc(level="ERROR", + message="Terminating controller due to storage exception", + explanation=Controller.ERROR_DATABASE, + recommendation=LogMessageDoc.CHECK_CONTROLLER), + @LogMessageDoc(level="ERROR", + message="Could not process message: queue full", + explanation="OpenFlow messages are arriving faster than " + + " the controller can process them.", + recommendation=LogMessageDoc.CHECK_CONTROLLER), + @LogMessageDoc(level="ERROR", + message="Error while processing message " + + "from switch {switch} {cause}", + explanation="An error occurred processing the switch message", + recommendation=LogMessageDoc.GENERIC_ACTION) + }) + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + if (e.getCause() instanceof ReadTimeoutException) { + // switch timeout + log.error("Disconnecting switch {} due to read timeout", + getSwitchInfoString()); + counters.switchDisconnectReadTimeout.updateCounterWithFlush(); + ctx.getChannel().close(); + } else if (e.getCause() instanceof HandshakeTimeoutException) { + log.error("Disconnecting switch {}: failed to complete handshake", + getSwitchInfoString()); + counters.switchDisconnectHandshakeTimeout.updateCounterWithFlush(); + ctx.getChannel().close(); + } else if (e.getCause() instanceof ClosedChannelException) { + log.debug("Channel for sw {} already closed", getSwitchInfoString()); + } else if (e.getCause() instanceof IOException) { + log.error("Disconnecting switch {} due to IO Error: {}", + getSwitchInfoString(), e.getCause().getMessage()); + if (log.isDebugEnabled()) { + // still print stack trace if debug is enabled + log.debug("StackTrace for previous Exception: ", e.getCause()); + } + counters.switchDisconnectIOError.updateCounterWithFlush(); + ctx.getChannel().close(); + } else if (e.getCause() instanceof SwitchStateException) { + log.error("Disconnecting switch {} due to switch state error: {}", + getSwitchInfoString(), e.getCause().getMessage()); + if (log.isDebugEnabled()) { + // still print stack trace if debug is enabled + log.debug("StackTrace for previous Exception: ", e.getCause()); + } + counters.switchDisconnectSwitchStateException.updateCounterWithFlush(); + ctx.getChannel().close(); + } else if (e.getCause() instanceof MessageParseException) { + log.error("Disconnecting switch " + + getSwitchInfoString() + + " due to message parse failure", + e.getCause()); + counters.switchDisconnectParseError.updateCounterWithFlush(); + ctx.getChannel().close(); + } else if (e.getCause() instanceof StorageException) { + log.error("Terminating controller due to storage exception", + e.getCause()); + this.controller.terminate(); + } else if (e.getCause() instanceof RejectedExecutionException) { + log.warn("Could not process message: queue full"); + counters.rejectedExecutionException.updateCounterWithFlush(); + } else { + log.error("Error while processing message from switch " + + getSwitchInfoString() + + "state " + this.state, e.getCause()); + counters.switchDisconnectOtherException.updateCounterWithFlush(); + ctx.getChannel().close(); + } + } + + @Override + public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e) + throws Exception { + OFMessage m = BasicFactory.getInstance().getMessage(OFType.ECHO_REQUEST); + e.getChannel().write(Collections.singletonList(m)); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) + throws Exception { + if (e.getMessage() instanceof List) { + @SuppressWarnings("unchecked") + List<OFMessage> msglist = (List<OFMessage>)e.getMessage(); + + LoadMonitor.LoadLevel loadlevel; + int packets_dropped = 0; + int packets_allowed = 0; + int lldps_allowed = 0; + + if (this.controller.overload_drop) { + loadlevel = this.controller.loadmonitor.getLoadLevel(); + } + else { + loadlevel = LoadMonitor.LoadLevel.OK; + } + + for (OFMessage ofm : msglist) { + counters.messageReceived.updateCounterNoFlush(); + // Per-switch input throttling + if (sw != null && sw.inputThrottled(ofm)) { + counters.messageInputThrottled.updateCounterNoFlush(); + continue; + } + try { + if (this.controller.overload_drop && + !loadlevel.equals(LoadMonitor.LoadLevel.OK)) { + switch (ofm.getType()) { + case PACKET_IN: + switch (loadlevel) { + case VERYHIGH: + // Drop all packet-ins, including LLDP/BDDPs + packets_dropped++; + continue; + case HIGH: + // Drop all packet-ins, except LLDP/BDDPs + byte[] data = ((OFPacketIn)ofm).getPacketData(); + if (data.length > 14) { + if (((data[12] == (byte)0x88) && + (data[13] == (byte)0xcc)) || + ((data[12] == (byte)0x89) && + (data[13] == (byte)0x42))) { + lldps_allowed++; + packets_allowed++; + break; + } + } + packets_dropped++; + continue; + default: + // Load not high, go ahead and process msg + packets_allowed++; + break; + } + break; + default: + // Process all non-packet-ins + packets_allowed++; + break; + } + } + + // Do the actual packet processing + state.processOFMessage(this, ofm); + + } + catch (Exception ex) { + // We are the last handler in the stream, so run the + // exception through the channel again by passing in + // ctx.getChannel(). + Channels.fireExceptionCaught(ctx.getChannel(), ex); + } + } + + if (loadlevel != LoadMonitor.LoadLevel.OK) { + if (log.isDebugEnabled()) { + log.debug( + "Overload: Detected {}, packets dropped={}", + loadlevel.toString(), packets_dropped); + log.debug( + "Overload: Packets allowed={} (LLDP/BDDPs allowed={})", + packets_allowed, lldps_allowed); + } + } + // Flush all thread local queues etc. generated by this train + // of messages. + this.controller.flushAll(); + } + else { + Channels.fireExceptionCaught(ctx.getChannel(), + new AssertionError("Message received from Channel is not a list")); + } + } + + + /** + * Get a useable error string from the OFError. + * @param error + * @return + */ + public static String getErrorString(OFError error) { + // TODO: this really should be OFError.toString. Sigh. + int etint = 0xffff & error.getErrorType(); + if (etint < 0 || etint >= OFErrorType.values().length) { + return String.format("Unknown error type %d", etint); + } + OFErrorType et = OFErrorType.values()[etint]; + switch (et) { + case OFPET_HELLO_FAILED: + OFHelloFailedCode hfc = + OFHelloFailedCode.values()[0xffff & error.getErrorCode()]; + return String.format("Error %s %s", et, hfc); + case OFPET_BAD_REQUEST: + OFBadRequestCode brc = + OFBadRequestCode.values()[0xffff & error.getErrorCode()]; + return String.format("Error %s %s", et, brc); + case OFPET_BAD_ACTION: + OFBadActionCode bac = + OFBadActionCode.values()[0xffff & error.getErrorCode()]; + return String.format("Error %s %s", et, bac); + case OFPET_FLOW_MOD_FAILED: + OFFlowModFailedCode fmfc = + OFFlowModFailedCode.values()[0xffff & error.getErrorCode()]; + return String.format("Error %s %s", et, fmfc); + case OFPET_PORT_MOD_FAILED: + OFPortModFailedCode pmfc = + OFPortModFailedCode.values()[0xffff & error.getErrorCode()]; + return String.format("Error %s %s", et, pmfc); + case OFPET_QUEUE_OP_FAILED: + OFQueueOpFailedCode qofc = + OFQueueOpFailedCode.values()[0xffff & error.getErrorCode()]; + return String.format("Error %s %s", et, qofc); + case OFPET_VENDOR_ERROR: + // no codes known for vendor error + return String.format("Error %s", et); + } + return null; + } + + private void dispatchMessage(OFMessage m) throws IOException { + // handleMessage will count + this.controller.handleMessage(this.sw, m, null); + } + + /** + * Return a string describing this switch based on the already available + * information (DPID and/or remote socket) + * @return + */ + private String getSwitchInfoString() { + if (sw != null) + return sw.toString(); + String channelString; + if (channel == null || channel.getRemoteAddress() == null) { + channelString = "?"; + } else { + channelString = channel.getRemoteAddress().toString(); + } + String dpidString; + if (featuresReply == null) { + dpidString = "?"; + } else { + dpidString = HexString.toHexString(featuresReply.getDatapathId()); + } + return String.format("[%s DPID[%s]]", channelString, dpidString); + } + + /** + * Update the channels state. Only called from the state machine. + * TODO: enforce restricted state transitions + * @param state + */ + private void setState(ChannelState state) { + this.state = state; + } + + /** + * Send a message to the switch using the handshake transactions ids. + * @throws IOException + */ + private void sendHandShakeMessage(OFType type) throws IOException { + // Send initial Features Request + OFMessage m = BasicFactory.getInstance().getMessage(type); + m.setXid(handshakeTransactionIds--); + 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)); + } + + + private void gotoWaitInitialRoleState() { + // We need to set the new state /before/ we call addSwitchChannel + // because addSwitchChannel will eventually call setRole + // which can in turn decide that the switch doesn't support + // roles and transition the state straight to MASTER. + setState(ChannelState.WAIT_INITIAL_ROLE); + controller.addSwitchChannelAndSendInitialRole(this); + } + + /** + * Send the configuration requests to tell the switch we want full + * packets + * @throws IOException + */ + private void sendHandshakeSetConfig() throws IOException { + List<OFMessage> msglist = new ArrayList<OFMessage>(3); + + // Ensure we receive the full packet via PacketIn + // FIXME: We don't set the reassembly flags. + OFSetConfig configSet = (OFSetConfig) BasicFactory.getInstance() + .getMessage(OFType.SET_CONFIG); + configSet.setMissSendLength((short) 0xffff) + .setLengthU(OFSwitchConfig.MINIMUM_LENGTH); + configSet.setXid(handshakeTransactionIds--); + msglist.add(configSet); + + // Barrier + OFBarrierRequest barrier = (OFBarrierRequest) BasicFactory.getInstance() + .getMessage(OFType.BARRIER_REQUEST); + barrier.setXid(handshakeTransactionIds--); + msglist.add(barrier); + + // Verify (need barrier?) + OFGetConfigRequest configReq = (OFGetConfigRequest) + BasicFactory.getInstance().getMessage(OFType.GET_CONFIG_REQUEST); + configReq.setXid(handshakeTransactionIds--); + msglist.add(configReq); + channel.write(msglist); + } + + /** + * send a description state request + * @throws IOException + */ + private void sendHandshakeDescriptionStatsRequest() throws IOException { + // Get Description to set switch-specific flags + OFStatisticsRequest req = new OFStatisticsRequest(); + req.setStatisticType(OFStatisticsType.DESC); + req.setXid(handshakeTransactionIds--); + + channel.write(Collections.singletonList(req)); + } + + + /** + * Read switch properties from storage and set switch attributes accordingly + */ + private void readPropertyFromStorage() { + // At this time, also set other switch properties from storage + boolean is_core_switch = false; + IResultSet resultSet = null; + try { + String swid = sw.getStringId(); + resultSet = this.controller.getStorageSourceService() + .getRow(Controller.SWITCH_CONFIG_TABLE_NAME, swid); + for (Iterator<IResultSet> it = + resultSet.iterator(); it.hasNext();) { + is_core_switch = it.next() + .getBoolean(Controller.SWITCH_CONFIG_CORE_SWITCH); + if (log.isDebugEnabled()) { + log.debug("Reading SWITCH_IS_CORE_SWITCH " + + "config for switch={}, is-core={}", + sw, is_core_switch); + } + } + } + finally { + if (resultSet != null) + resultSet.close(); + } + if (is_core_switch) { + sw.setAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH, + Boolean.valueOf(true)); + } + } + + ChannelState getStateForTesting() { + return state; + } + + void useRoleChangerWithOtherTimeoutForTesting(long roleTimeoutMs) { + roleChanger = new RoleChanger(roleTimeoutMs); + } +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFFeaturesReplyFuture.java b/src/main/java/net/floodlightcontroller/core/internal/OFFeaturesReplyFuture.java new file mode 100644 index 0000000000000000000000000000000000000000..eca67bd97ca289fa5246edbb4196d9f0216e98c5 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/internal/OFFeaturesReplyFuture.java @@ -0,0 +1,72 @@ +/** + * Copyright 2012, Big Switch Networks, Inc. + * + * 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 net.floodlightcontroller.core.internal; + +import java.util.concurrent.TimeUnit; + +import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.threadpool.IThreadPoolService; + +import org.openflow.protocol.OFFeaturesReply; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFType; + +/** + * A concrete implementation that handles asynchronously receiving + * OFFeaturesReply + * + * @author Shudong Zhou + */ +public class OFFeaturesReplyFuture extends + OFMessageFuture<OFFeaturesReply> { + + protected volatile boolean finished; + + public OFFeaturesReplyFuture(IThreadPoolService tp, + IOFSwitch sw, int transactionId) { + super(tp, sw, OFType.FEATURES_REPLY, transactionId); + init(); + } + + public OFFeaturesReplyFuture(IThreadPoolService tp, + IOFSwitch sw, int transactionId, long timeout, TimeUnit unit) { + super(tp, sw, OFType.FEATURES_REPLY, transactionId, timeout, unit); + init(); + } + + private void init() { + this.finished = false; + this.result = null; + } + + @Override + protected void handleReply(IOFSwitch sw, OFMessage msg) { + this.result = (OFFeaturesReply) msg; + this.finished = true; + } + + @Override + protected boolean isFinished() { + return finished; + } + + @Override + protected void unRegister() { + super.unRegister(); + sw.cancelFeaturesReply(transactionId); + } +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFMessageDecoder.java b/src/main/java/net/floodlightcontroller/core/internal/OFMessageDecoder.java new file mode 100644 index 0000000000000000000000000000000000000000..25edf396c943dfce728f79610910162ebd608a12 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/internal/OFMessageDecoder.java @@ -0,0 +1,60 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* 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 net.floodlightcontroller.core.internal; + +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.frame.FrameDecoder; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.factory.OFMessageFactory; + +/** + * Decode an openflow message from a Channel, for use in a netty + * pipeline + * @author readams + */ +public class OFMessageDecoder extends FrameDecoder { + + OFMessageFactory factory = BasicFactory.getInstance(); + + @Override + protected Object decode(ChannelHandlerContext ctx, Channel channel, + ChannelBuffer buffer) throws Exception { + if (!channel.isConnected()) { + // In testing, I see decode being called AFTER decode last. + // This check avoids that from reading curroupted frames + return null; + } + + List<OFMessage> message = factory.parseMessage(buffer); + return message; + } + + @Override + protected Object decodeLast(ChannelHandlerContext ctx, Channel channel, + ChannelBuffer buffer) throws Exception { + // This is not strictly needed atthis time. It is used to detect + // connection reset detection from netty (for debug) + return null; + } + +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFMessageEncoder.java b/src/main/java/net/floodlightcontroller/core/internal/OFMessageEncoder.java new file mode 100644 index 0000000000000000000000000000000000000000..6be5f9a1d524b51e85af216f70d9a4800aed0ad6 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/internal/OFMessageEncoder.java @@ -0,0 +1,56 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* 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 net.floodlightcontroller.core.internal; + +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.oneone.OneToOneEncoder; +import org.openflow.protocol.OFMessage; + +/** + * Encode an openflow message for output into a ChannelBuffer, for use in a + * netty pipeline + * @author readams + */ +public class OFMessageEncoder extends OneToOneEncoder { + + @Override + protected Object encode(ChannelHandlerContext ctx, Channel channel, + Object msg) throws Exception { + if (!( msg instanceof List)) + return msg; + + @SuppressWarnings("unchecked") + List<OFMessage> msglist = (List<OFMessage>)msg; + int size = 0; + for (OFMessage ofm : msglist) { + size += ofm.getLengthU(); + } + + ChannelBuffer buf = ChannelBuffers.buffer(size);; + for (OFMessage ofm : msglist) { + ofm.writeTo(buf); + } + return buf; + } + +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFMessageFuture.java b/src/main/java/net/floodlightcontroller/core/internal/OFMessageFuture.java new file mode 100644 index 0000000000000000000000000000000000000000..1fc9c135330d6189652fca4b37aac903bce5bf0d --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/internal/OFMessageFuture.java @@ -0,0 +1,171 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* 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 net.floodlightcontroller.core.internal; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFType; + +import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.threadpool.IThreadPoolService; + +/** + * A Future object used to retrieve asynchronous OFMessage replies. Unregisters + * and cancels itself by default after 60 seconds. This class is meant to be + * sub-classed and proper behavior added to the handleReply method, and + * termination of the Future to be handled in the isFinished method. + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public abstract class OFMessageFuture<V> implements Future<V> { + + protected IThreadPoolService threadPool; + protected volatile boolean canceled; + protected CountDownLatch latch; + protected OFType responseType; + protected volatile V result; + protected IOFSwitch sw; + protected Runnable timeoutTimer; + protected int transactionId; + protected static final long DEFAULT_TIMEOUT = 60; + protected static final TimeUnit DEFAULT_TIMEOUT_UNIT = TimeUnit.SECONDS; + + public OFMessageFuture(IThreadPoolService tp, + IOFSwitch sw, OFType responseType, int transactionId) { + this(tp, sw, responseType, transactionId, + DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT); + } + + public OFMessageFuture(IThreadPoolService tp, + IOFSwitch sw, OFType responseType, int transactionId, long timeout, TimeUnit unit) { + this.threadPool = tp; + this.canceled = false; + this.latch = new CountDownLatch(1); + this.responseType = responseType; + this.sw = sw; + this.transactionId = transactionId; + + final OFMessageFuture<V> future = this; + timeoutTimer = new Runnable() { + @Override + public void run() { + if (timeoutTimer == this) + future.cancel(true); + } + }; + threadPool.getScheduledExecutor().schedule(timeoutTimer, timeout, unit); + } + + protected void unRegister() { + this.timeoutTimer = null; + } + + + // TODO: msg should be generic! + public void deliverFuture(IOFSwitch sw, OFMessage msg) { + if (transactionId == msg.getXid()) { + handleReply(sw, msg); + if (isFinished()) { + unRegister(); + this.latch.countDown(); + } + } + } + + /** + * Used to handle the specific expected message this Future was reigstered + * for, the specified msg parameter is guaranteed to match the type and + * transaction id specified. + * @param sw + * @param msg + * @return + */ + protected abstract void handleReply(IOFSwitch sw, OFMessage msg); + + /** + * Called directly after handleReply, subclasses implement this method to + * indicate when the future can deregister itself from receiving future + * messages, and when it is safe to return the results to any waiting + * threads. + * @return when this Future has completed its work + */ + protected abstract boolean isFinished(); + + /* (non-Javadoc) + * @see java.util.concurrent.Future#cancel(boolean) + */ + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + if (isDone()) { + return false; + } else { + unRegister(); + canceled = true; + this.latch.countDown(); + return !isDone(); + } + } + + /* (non-Javadoc) + * @see java.util.concurrent.Future#isCancelled() + */ + @Override + public boolean isCancelled() { + return canceled; + } + + /* (non-Javadoc) + * @see java.util.concurrent.Future#isDone() + */ + @Override + public boolean isDone() { + return this.latch.getCount() == 0; + } + + /* (non-Javadoc) + * @see java.util.concurrent.Future#get() + */ + @Override + public V get() throws InterruptedException, ExecutionException { + this.latch.await(); + return result; + } + + /* (non-Javadoc) + * @see java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit) + */ + @Override + public V get(long timeout, TimeUnit unit) throws InterruptedException, + ExecutionException, TimeoutException { + this.latch.await(timeout, unit); + return result; + } + + public int getTransactionId() { + return transactionId; + } + + public void setTransactionId(int transactionId) { + this.transactionId = transactionId; + } +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFStatisticsFuture.java b/src/main/java/net/floodlightcontroller/core/internal/OFStatisticsFuture.java new file mode 100644 index 0000000000000000000000000000000000000000..4d3f733a6ed1d3e6b7dbba272643680877f9e982 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/internal/OFStatisticsFuture.java @@ -0,0 +1,80 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* 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 net.floodlightcontroller.core.internal; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; + +import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.threadpool.IThreadPoolService; + +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFStatisticsReply; +import org.openflow.protocol.OFType; +import org.openflow.protocol.statistics.OFStatistics; + +/** + * A concrete implementation that handles asynchronously receiving OFStatistics + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFStatisticsFuture extends + OFMessageFuture<List<OFStatistics>> { + + protected volatile boolean finished; + + public OFStatisticsFuture(IThreadPoolService tp, + IOFSwitch sw, int transactionId) { + super(tp, sw, OFType.STATS_REPLY, transactionId); + init(); + } + + public OFStatisticsFuture(IThreadPoolService tp, + IOFSwitch sw, int transactionId, long timeout, TimeUnit unit) { + super(tp, sw, OFType.STATS_REPLY, transactionId, timeout, unit); + init(); + } + + private void init() { + this.finished = false; + this.result = new CopyOnWriteArrayList<OFStatistics>(); + } + + @Override + protected void handleReply(IOFSwitch sw, OFMessage msg) { + OFStatisticsReply sr = (OFStatisticsReply) msg; + synchronized (this.result) { + this.result.addAll(sr.getStatistics()); + if ((sr.getFlags() & 0x1) == 0) { + this.finished = true; + } + } + } + + @Override + protected boolean isFinished() { + return finished; + } + + @Override + protected void unRegister() { + super.unRegister(); + sw.cancelStatisticsReply(transactionId); + } +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchAppHandshakePlugin.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchAppHandshakePlugin.java deleted file mode 100644 index ddfd008720a6853262905afeb7072cf91c99c3f5..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchAppHandshakePlugin.java +++ /dev/null @@ -1,116 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import java.util.concurrent.TimeUnit; - -import org.jboss.netty.util.Timeout; -import org.jboss.netty.util.Timer; -import org.jboss.netty.util.TimerTask; -import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler.WaitAppHandshakeState; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Preconditions; - -/** - * This class is a plugin that can be used by applications to tap into the - * switch handshake. It operates much like the switch handshake. Messages should - * be sent upon entering the plugin and OF response messages should be handled - * just like any part of the switch handshake. - * - * @author Jason Parraga <jason.parraga@bigswitch.com> - */ -public abstract class OFSwitchAppHandshakePlugin { - - private static final Logger log = LoggerFactory.getLogger(OFSwitchAppHandshakePlugin.class); - - private WaitAppHandshakeState state; - @edu.umd.cs.findbugs.annotations.SuppressWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") - private IOFSwitch sw; - private volatile Timeout timeout; - - private final PluginResult defaultResult; - private final int timeoutS; - - /** - * Constructor for OFSwitchAppHandshakePlugin - * @param defaultResult the default result in the event of a timeout - * @param defaultTimeoutS the timeout length in seconds - */ - protected OFSwitchAppHandshakePlugin(PluginResult defaultResult, int timeoutS){ - Preconditions.checkNotNull(defaultResult, "defaultResult"); - Preconditions.checkNotNull(timeoutS, "timeoutS"); - - this.defaultResult = defaultResult; - this.timeoutS = timeoutS; - } - - /** - * Process incoming OF Messages - * - * @param m The OF Message received - */ - protected abstract void processOFMessage(OFMessage m); - - /** - * Enter this plugin. Should be used to send messages. - */ - protected abstract void enterPlugin(); - - /** - * Gets the switch associated with the handshake for use by the plugin - * writer. - * - * @return the switch associated with the handshake. - */ - protected IOFSwitch getSwitch() { - return this.sw; - } - - /** - * Initialization for plugin called by the OFSwitchHandshakeHandler - * - * @param state the current state of the OFSwitchHandshakeHandler - * @param sw the current switch of the OFSwitchHandshakeHandler - */ - final void init(WaitAppHandshakeState state, IOFSwitch sw, Timer timer) { - this.state = state; - this.sw = sw; - this.timeout = timer.newTimeout(new PluginTimeoutTask(), timeoutS, TimeUnit.SECONDS); - } - - /** - * Called to denote that the plugin has finished - * - * @param result - * the result of the plugin in regards to handshaking - */ - protected final void exitPlugin(PluginResult result) { - timeout.cancel(); - state.exitPlugin(result); - } - - /** - * Plugin timeout task that will exit the plugin - * with the default result value when timed out. - * - */ - private final class PluginTimeoutTask implements TimerTask { - - @Override - public void run(Timeout timeout) throws Exception { - if (!timeout.isCancelled()) { - log.warn("App handshake plugin for {} timed out. Returning result {}.", - sw, defaultResult); - exitPlugin(defaultResult); - } - } - } - - public enum PluginResultType { - CONTINUE(), - DISCONNECT(), - QUARANTINE(); - } -} diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java deleted file mode 100644 index 90c7d4654a6d6d8b1fd670d65d456b68053fde3d..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java +++ /dev/null @@ -1,1886 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nonnull; - -import org.jboss.netty.util.Timer; -import net.floodlightcontroller.core.GenTableMap; -import net.floodlightcontroller.core.HARole; -import net.floodlightcontroller.core.IOFConnection; -import net.floodlightcontroller.core.IOFConnectionBackend; -import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.IOFSwitch.SwitchStatus; -import net.floodlightcontroller.core.IOFSwitchBackend; -import net.floodlightcontroller.core.PortChangeEvent; -import net.floodlightcontroller.core.SwitchDescription; -import net.floodlightcontroller.core.annotations.LogMessageDoc; -import net.floodlightcontroller.core.annotations.LogMessageDocs; -import net.floodlightcontroller.core.internal.OFSwitchAppHandshakePlugin.PluginResultType; -import org.projectfloodlight.openflow.protocol.OFBadRequestCode; -import org.projectfloodlight.openflow.protocol.OFBarrierReply; -import org.projectfloodlight.openflow.protocol.OFBarrierRequest; -import org.projectfloodlight.openflow.protocol.OFBsnControllerConnectionsReply; -import org.projectfloodlight.openflow.protocol.OFBsnControllerConnectionsRequest; -import org.projectfloodlight.openflow.protocol.OFBsnGentableDescStatsReply; -import org.projectfloodlight.openflow.protocol.OFBsnGentableDescStatsRequest; -import org.projectfloodlight.openflow.protocol.OFBsnSetAuxCxnsReply; -import org.projectfloodlight.openflow.protocol.OFBsnSetAuxCxnsRequest; -import org.projectfloodlight.openflow.protocol.OFBsnSetL2TableRequest; -import org.projectfloodlight.openflow.protocol.OFControllerRole; -import org.projectfloodlight.openflow.protocol.OFDescStatsReply; -import org.projectfloodlight.openflow.protocol.OFDescStatsRequest; -import org.projectfloodlight.openflow.protocol.OFErrorMsg; -import org.projectfloodlight.openflow.protocol.OFErrorType; -import org.projectfloodlight.openflow.protocol.OFExperimenter; -import org.projectfloodlight.openflow.protocol.OFFactories; -import org.projectfloodlight.openflow.protocol.OFFactory; -import org.projectfloodlight.openflow.protocol.OFFeaturesReply; -import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode; -import org.projectfloodlight.openflow.protocol.OFFlowRemoved; -import org.projectfloodlight.openflow.protocol.OFGetConfigReply; -import org.projectfloodlight.openflow.protocol.OFGetConfigRequest; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole; -import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply; -import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleRequest; -import org.projectfloodlight.openflow.protocol.OFPacketIn; -import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; -import org.projectfloodlight.openflow.protocol.OFPortStatus; -import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply; -import org.projectfloodlight.openflow.protocol.OFRoleReply; -import org.projectfloodlight.openflow.protocol.OFRoleRequest; -import org.projectfloodlight.openflow.protocol.OFSetConfig; -import org.projectfloodlight.openflow.protocol.OFStatsReply; -import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags; -import org.projectfloodlight.openflow.protocol.OFStatsRequestFlags; -import org.projectfloodlight.openflow.protocol.OFStatsType; -import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.protocol.OFVersion; -import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg; -import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFAuxId; -import org.projectfloodlight.openflow.types.U64; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; - -/** - * Switch handler deals with the switch connection and dispatches - * switch messages to the appropriate locations. These messages - * are typically received by the channel handler first and piped here. - * - * @author Jason Parraga <jason.parraga@bigswitch.com> - */ -public class OFSwitchHandshakeHandler implements IOFConnectionListener { - private static final Logger log = LoggerFactory.getLogger(OFSwitchHandshakeHandler.class); - - private final IOFSwitchManager switchManager; - private final RoleManager roleManager; - private final IOFConnectionBackend mainConnection; - private final SwitchManagerCounters switchManagerCounters; - private IOFSwitchBackend sw; - private final Map<OFAuxId, IOFConnectionBackend> auxConnections; - private volatile OFSwitchHandshakeState state; - private RoleChanger roleChanger; - // Default to 1.3 - This is overwritten by the features reply - private OFFactory factory = OFFactories.getFactory(OFVersion.OF_13); - private final OFFeaturesReply featuresReply; - private final Timer timer; - - private final ArrayList<OFPortStatus> pendingPortStatusMsg; - - /** transaction Ids to use during handshake. Since only one thread - * calls into the OFChannelHandler we don't need atomic. - * We will count down - */ - private long handshakeTransactionIds = 0x00FFFFFFFFL; - - /* Exponential backoff of master role assertion */ - private final long MAX_ASSERT_TIME_INTERVAL_NS = TimeUnit.SECONDS.toNanos(120); - private final long DEFAULT_ROLE_TIMEOUT_NS = TimeUnit.SECONDS.toNanos(10); - - protected OFPortDescStatsReply portDescStats; - - /** - * When we remove a pending role request and set the role on the switch - * we use this enum to indicate how we arrived at the decision. - * @author gregor - */ - private enum RoleRecvStatus { - /** We received a role reply message from the switch */ - RECEIVED_REPLY, - /** The switch returned an error indicated that roles are not - * supported*/ - UNSUPPORTED, - /** The request timed out */ - NO_REPLY; - } - /** - * A utility class to handle role requests and replies for this channel. - * After a role request is submitted the role changer keeps track of the - * pending request, collects the reply (if any) and times out the request - * if necessary. - * - * To simplify role handling we only keep track of the /last/ pending - * role reply send to the switch. If multiple requests are pending and - * we receive replies for earlier requests we ignore them. However, this - * way of handling pending requests implies that we could wait forever if - * a new request is submitted before the timeout triggers. If necessary - * we could work around that though. - * @author gregor - */ - private class RoleChanger { - // indicates that a request is currently pending - // needs to be volatile to allow correct double-check idiom - private volatile boolean requestPending; - // the transaction Id of the pending request - private long pendingXid; - // the role that's pending - private OFControllerRole pendingRole; - // system time in NS when we send the request - private long roleSubmitTimeNs; - // the timeout to use - private final long roleTimeoutNs; - private long lastAssertTimeNs; - private long assertTimeIntervalNs = TimeUnit.SECONDS.toNanos(1); - - public RoleChanger(long roleTimeoutNs) { - this.roleTimeoutNs = roleTimeoutNs; - // System.nanoTime() may be negative -- prime the roleSubmitTime as - // "long ago in the past" to be robust against it. - this.roleSubmitTimeNs = System.nanoTime() - (2 * roleTimeoutNs); - this.lastAssertTimeNs = System.nanoTime() - (2 * assertTimeIntervalNs); - this.requestPending = false; - this.pendingXid = -1; - this.pendingRole = null; - } - - /** - * Send Nicira role request message to the switch requesting the - * specified role. - * - * @param role role to request - */ - private long sendNiciraRoleRequest(OFControllerRole role){ - - long xid; - // Construct the role request message - if(factory.getVersion().compareTo(OFVersion.OF_12) < 0) { - OFNiciraControllerRoleRequest.Builder builder = - factory.buildNiciraControllerRoleRequest(); - xid = factory.nextXid(); - builder.setXid(xid); - - OFNiciraControllerRole niciraRole = NiciraRoleUtils.ofRoleToNiciraRole(role); - builder.setRole(niciraRole); - OFNiciraControllerRoleRequest roleRequest = builder.build(); - // Send it to the switch - mainConnection.write(roleRequest); - } else { - // send an OF 1.2+ role request - OFRoleRequest roleRequest = factory.buildRoleRequest() - // we don't use the generation id scheme for now, - // switch initializes to 0, we keep it at 0 - .setGenerationId(U64.of(0)) - .setRole(role) - .build(); - xid = roleRequest.getXid(); - mainConnection.write(roleRequest); - } - return xid; - } - - /** - * Send a role request for the given role only if no other role - * request is currently pending. - * @param role The role to send to the switch. - * @throws IOException - */ - @LogMessageDoc(level="WARN", - message="Reasserting master role on switch {SWITCH}, " + - "likely a configruation error with multiple masters", - explanation="The controller keeps getting permission error " + - "from switch, likely due to switch connected to another " + - "controller also in master mode", - recommendation=LogMessageDoc.CHECK_SWITCH) - synchronized void sendRoleRequestIfNotPending(OFControllerRole role) - throws IOException { - long now = System.nanoTime(); - if (now - lastAssertTimeNs < assertTimeIntervalNs) { - return; - } - - lastAssertTimeNs = now; - if (assertTimeIntervalNs < MAX_ASSERT_TIME_INTERVAL_NS) { // 2 minutes max - assertTimeIntervalNs <<= 1; - } else if (role == OFControllerRole.ROLE_MASTER){ - log.warn("Reasserting master role on switch {}, " + - "likely a switch config error with multiple masters", - role, sw); - } - if (!requestPending) - sendRoleRequest(role); - else - switchManagerCounters.roleNotResentBecauseRolePending.increment(); - } - - /** - * Send a role request with the given role to the switch. - * - * Send a role request with the given role to the switch and update - * the pending request and timestamp. - * - * @param role - * @throws IOException - */ - synchronized void sendRoleRequest(OFControllerRole role) throws IOException { - /* - * There are three cases to consider for SUPPORTS_NX_ROLE: - * - * 1) unset. We have neither received a role reply from the - * switch nor has a request timed out. Send a request. - * 2) TRUE: We've already send a request earlier and received - * a reply. The switch supports role and we should send one. - * 3) FALSE: We have already send a role and received an error. - * The switch does not support roles. Don't send a role request, - * set the switch's role directly. - */ - Boolean supportsNxRole = (Boolean) - sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE); - if ((supportsNxRole != null) && !supportsNxRole) { - setSwitchRole(role, RoleRecvStatus.UNSUPPORTED); - } else { - pendingXid = sendNiciraRoleRequest(role); - pendingRole = role; - this.roleSubmitTimeNs = System.nanoTime(); - requestPending = true; - } - } - - /** - * Deliver a received role reply and set SWITCH_SUPPORTS_NX_ROLE. - * - * Check if a request is pending and if the received reply matches the - * the expected pending reply (we check both role and xid) we set - * the role for the switch/channel. - * - * If a request is pending but doesn't match the reply we ignore it. - * - * If no request is pending we disconnect. - * - * @param xid - * @param role - * @throws SwitchStateException if no request is pending - */ - synchronized void deliverRoleReply(long xid, OFControllerRole role) { - if (!requestPending) { - // Maybe don't disconnect if the role reply we received is - // for the same role we are already in. - String msg = String.format("Switch: [%s], State: [%s], " - + "received unexpected RoleReply[%s]. " - + "No roles are pending", - OFSwitchHandshakeHandler.this.getSwitchInfoString(), - OFSwitchHandshakeHandler.this.state.toString(), - role); - throw new SwitchStateException(msg); - } - - if (pendingXid == xid && pendingRole == role) { - log.debug("[{}] Received role reply message setting role to {}", - getDpid(), role); - switchManagerCounters.roleReplyReceived.increment(); - setSwitchRole(role, RoleRecvStatus.RECEIVED_REPLY); - } else { - log.debug("[{}] Received stale or unexpected role reply " + - "{}, xid={}. Ignoring. " + - "Waiting for {}, xid={}", - new Object[] { getDpid(), role, xid, - pendingRole, pendingXid }); - } - } - - /** - * Called if we receive an error message. If the xid matches the - * pending request we handle it otherwise we ignore it. We also - * set SWITCH_SUPPORTS_NX_ROLE to false. - * - * Note: since we only keep the last pending request we might get - * error messages for earlier role requests that we won't be able - * to handle - * @param xid - * @return true if the error was handled by us, false otherwise - * @throws SwitchStateException if the error was for the pending - * role request but was unexpected - */ - synchronized boolean deliverError(OFErrorMsg error) { - if (!requestPending) - return false; - - if (pendingXid == error.getXid()) { - if (error.getErrType() == OFErrorType.BAD_REQUEST) { - switchManagerCounters.roleReplyErrorUnsupported.increment(); - setSwitchRole(pendingRole, RoleRecvStatus.UNSUPPORTED); - } else { - // TODO: Is this the right thing to do if we receive - // some other error besides a bad request error? - // Presumably that means the switch did actually - // understand the role request message, but there - // was some other error from processing the message. - // OF 1.2 specifies a ROLE_REQUEST_FAILED - // error code, but it doesn't look like the Nicira - // role request has that. Should check OVS source - // code to see if it's possible for any other errors - // to be returned. - // If we received an error the switch is not - // in the correct role, so we need to disconnect it. - // We could also resend the request but then we need to - // check if there are other pending request in which - // case we shouldn't resend. If we do resend we need - // to make sure that the switch eventually accepts one - // of our requests or disconnect the switch. This feels - // cumbersome. - String msg = String.format("Switch: [%s], State: [%s], " - + "Unexpected error %s in respone to our " - + "role request for %s.", - OFSwitchHandshakeHandler.this.getSwitchInfoString(), - OFSwitchHandshakeHandler.this.state.toString(), - error.toString(), - pendingRole); - throw new SwitchStateException(msg); - } - return true; - } - return false; - } - - /** - * Check if a pending role request has timed out. - */ - void checkTimeout() { - if (!requestPending) - return; - synchronized(this) { - if (!requestPending) - return; - long now = System.nanoTime(); - if (now - this.roleSubmitTimeNs > roleTimeoutNs) { - // timeout triggered. - switchManagerCounters.roleReplyTimeout.increment(); - setSwitchRole(pendingRole, RoleRecvStatus.NO_REPLY); - } - } - } - - /** - * Set the role for this switch / channel. - * - * If the status indicates that we received a reply we set the role. - * If the status indicates otherwise we disconnect the switch if - * the role is SLAVE. - * - * "Setting a role" means setting the appropriate ChannelState, - * setting the flags on the switch and - * notifying Controller.java about new role of the switch - * - * @param role The role to set. - * @param status How we derived at the decision to set this status. - */ - synchronized private void setSwitchRole(OFControllerRole role, RoleRecvStatus status) { - requestPending = false; - if (status == RoleRecvStatus.RECEIVED_REPLY) - sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true); - else - sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); - sw.setControllerRole(role); - - if (role != OFControllerRole.ROLE_SLAVE) { - OFSwitchHandshakeHandler.this.setState(new MasterState()); - } else { - if (status != RoleRecvStatus.RECEIVED_REPLY) { - if (log.isDebugEnabled()) { - log.debug("Disconnecting switch {}. Doesn't support role" - + "({}) request and controller is now SLAVE", - getSwitchInfoString(), status); - } - // the disconnect will trigger a switch removed to - // controller so no need to signal anything else - sw.disconnect(); - } else { - OFSwitchHandshakeHandler.this.setState(new SlaveState()); - } - } - } - } - - /** - * Default implementation for message handlers in any state. - * - * Individual states must override these if they want a behavior - * that differs from the default. - * - * In general, these handlers simply ignore the message and do - * nothing. - * - * There are some exceptions though, since some messages really - * are handled the same way in every state (e.g., ECHO_REQUST) or - * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY - */ - public abstract class OFSwitchHandshakeState { - - void processOFBarrierReply(OFBarrierReply m) { - // do nothing - } - - void processOFError(OFErrorMsg m) { - logErrorDisconnect(m); - } - - void processOFFlowRemoved(OFFlowRemoved m) { - unhandledMessageReceived(m); - } - - void processOFGetConfigReply(OFGetConfigReply m) { - // we only expect config replies in the WAIT_CONFIG_REPLY state - // TODO: might use two different strategies depending on whether - // we got a miss length of 64k or not. - illegalMessageReceived(m); - } - - void processOFPacketIn(OFPacketIn m) { - unhandledMessageReceived(m); - } - - // By default add port status messages to a pending list - void processOFPortStatus(OFPortStatus m) { - pendingPortStatusMsg.add(m); - } - - void processOFQueueGetConfigReply(OFQueueGetConfigReply m) { - unhandledMessageReceived(m); - } - - void processOFStatsReply(OFStatsReply m) { - switch(m.getStatsType()) { - case PORT_DESC: - processPortDescStatsReply((OFPortDescStatsReply) m); - break; - default: - unhandledMessageReceived(m); - } - } - - void processOFExperimenter(OFExperimenter m) { - if (m instanceof OFBsnControllerConnectionsReply) { - OFBsnControllerConnectionsReply reply = (OFBsnControllerConnectionsReply) m; - handleControllerConnectionMessage(reply); - } else { - unhandledMessageReceived(m); - } - } - - void processPortDescStatsReply(OFPortDescStatsReply m) { - unhandledMessageReceived(m); - } - - void processOFRoleReply(OFRoleReply m) { - unhandledMessageReceived(m); - } - - private final boolean handshakeComplete; - OFSwitchHandshakeState(boolean handshakeComplete) { - this.handshakeComplete = handshakeComplete; - } - - void logState() { - if(log.isDebugEnabled()) - log.debug("[{}] - Switch Handshake - enter state {}", mainConnection.getDatapathId(), this.getClass().getSimpleName()); - } - - /** enter this state. Can initialize the handler, send - * the necessary messages, etc. - */ - void enterState(){ - } - - /** - * Is this a state in which the handshake has completed? - * @return true if the handshake is complete - */ - public boolean isHandshakeComplete() { - return handshakeComplete; - } - - /** - * Used to notify the WAIT OF AUX state that - * a new connection has been added - * @param connection - */ - public void auxConnectionOpened(IOFConnectionBackend connection) { - // Should only be handled in wait of aux - log.debug("[{}] - Switch Handshake - unhandled aux connection event", - getDpid()); - } - /** - * Get a string specifying the switch connection, state, and - * message received. To be used as message for SwitchStateException - * or log messages - * @param h The channel handler (to get switch information_ - * @param m The OFMessage that has just been received - * @param details A string giving more details about the exact nature - * of the problem. - * @return - */ - // needs to be protected because enum members are acutally subclasses - protected String getSwitchStateMessage(OFMessage m, - String details) { - return String.format("Switch: [%s], State: [%s], received: [%s]" - + ", details: %s", - getSwitchInfoString(), - this.toString(), - m.getType().toString(), - details); - } - - /** - * We have an OFMessage we didn't expect given the current state and - * we want to treat this as an error. - * We currently throw an exception that will terminate the connection - * However, we could be more forgiving - * @param h the channel handler that received the message - * @param m the message - * @throws SwitchStateExeption we always through the execption - */ - // needs to be protected because enum members are acutally subclasses - protected void illegalMessageReceived(OFMessage m) { - String msg = getSwitchStateMessage(m, - "Switch should never send this message in the current state"); - throw new SwitchStateException(msg); - - } - - /** - * We have an OFMessage we didn't expect given the current state and - * we want to ignore the message - * @param h the channel handler the received the message - * @param m the message - */ - protected void unhandledMessageReceived(OFMessage m) { - switchManagerCounters.unhandledMessage.increment(); - if (log.isDebugEnabled()) { - String msg = getSwitchStateMessage(m, - "Ignoring unexpected message"); - log.debug(msg); - } - } - - /** - * Log an OpenFlow error message from a switch - * @param error The error message - */ - @LogMessageDoc(level="ERROR", - message="Error {error type} {error code} from {switch} " + - "in state {state}", - explanation="The switch responded with an unexpected error" + - "to an OpenFlow message from the controller", - recommendation="This could indicate improper network operation. " + - "If the problem persists restarting the switch and " + - "controller may help." - ) - protected void logError(OFErrorMsg error) { - log.error("{} from switch {} in state {}", - new Object[] { - error.toString(), - getSwitchInfoString(), - this.toString()}); - } - - /** - * Log an OpenFlow error message from a switch and disconnect the - * channel - * @param error The error message - */ - protected void logErrorDisconnect(OFErrorMsg error) { - logError(error); - mainConnection.disconnect(); - } - - /** - * Extract the role from an OFVendor message. - * - * Extract the role from an OFVendor message if the message is a - * Nicira role reply. Otherwise return null. - * - * @param h The channel handler receiving the message - * @param vendorMessage The vendor message to parse. - * @return The role in the message if the message is a Nicira role - * reply, null otherwise. - */ - protected OFControllerRole extractNiciraRoleReply(OFMessage vendorMessage) { - if (!(vendorMessage instanceof OFNiciraControllerRoleReply)) - return null; - OFNiciraControllerRoleReply roleReply = - (OFNiciraControllerRoleReply) vendorMessage; - return NiciraRoleUtils.niciraToOFRole(roleReply); - } - - /** - * Handle a port status message. - * - * Handle a port status message by updating the port maps in the - * IOFSwitch instance and notifying Controller about the change so - * it can dispatch a switch update. - * - * @param h The OFChannelHhandler that received the message - * @param m The PortStatus message we received - * @param doNotify if true switch port changed events will be - * dispatched - */ - protected void handlePortStatusMessage(OFPortStatus m, - boolean doNotify) { - if (sw == null) { - String msg = getSwitchStateMessage(m, - "State machine error: switch is null. Should never " + - "happen"); - throw new SwitchStateException(msg); - } - Collection<PortChangeEvent> changes = sw.processOFPortStatus(m); - if (doNotify) { - for (PortChangeEvent ev: changes) - switchManager.notifyPortChanged(sw, ev.port, ev.type); - } - } - - protected void handleControllerConnectionMessage(OFBsnControllerConnectionsReply m) { - if (sw == null) { - String msg = getSwitchStateMessage(m, - "State machine error: switch is null. Should never " + - "happen"); - throw new SwitchStateException(msg); - } - // Update switches - sw.updateControllerConnections(m); - // Notify role manager - roleManager.notifyControllerConnectionUpdate(); - } - - - /** - * Process an OF message received on the channel and - * update state accordingly. - * - * The main "event" of the state machine. Process the received message, - * send follow up message if required and update state if required. - * - * Switches on the message type and calls more specific event handlers - * for each individual OF message type. If we receive a message that - * is supposed to be sent from a controller to a switch we throw - * a SwitchStateExeption. - * - * The more specific handlers can also throw SwitchStateExceptions - * - * @param h The OFChannelHandler that received the message - * @param m The message we received. - * @throws SwitchStateException - * @throws IOException - */ - void processOFMessage(OFMessage m) { - roleChanger.checkTimeout(); - switch(m.getType()) { - case BARRIER_REPLY: - processOFBarrierReply((OFBarrierReply)m); - break; - case ERROR: - processOFError((OFErrorMsg)m); - break; - case FLOW_REMOVED: - processOFFlowRemoved((OFFlowRemoved)m); - break; - case GET_CONFIG_REPLY: - processOFGetConfigReply((OFGetConfigReply)m); - break; - case PACKET_IN: - processOFPacketIn((OFPacketIn)m); - break; - case PORT_STATUS: - processOFPortStatus((OFPortStatus)m); - break; - case QUEUE_GET_CONFIG_REPLY: - processOFQueueGetConfigReply((OFQueueGetConfigReply)m); - break; - case STATS_REPLY: - processOFStatsReply((OFStatsReply)m); - break; - case ROLE_REPLY: - processOFRoleReply((OFRoleReply)m); - break; - case EXPERIMENTER: - processOFExperimenter((OFExperimenter)m); - break; - default: - illegalMessageReceived(m); - break; - } - } - } - - /** - * Initial state before channel is connected. Should not handle any messages. - */ - public class InitState extends OFSwitchHandshakeState { - - InitState() { - super(false); - } - - @Override - public void logState() { - log.debug("[{}] - Switch Handshake - Initiating from {}", - getDpid(), mainConnection.getRemoteInetAddress()); - } - } - - /** - * 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 or WAIT_SET_L2_TABLE_REPLY - */ - public class WaitPortDescStatsReplyState extends OFSwitchHandshakeState { - WaitPortDescStatsReplyState() { - super(false); - } - - @Override - void enterState(){ - sendPortDescRequest(); - } - - @Override - void processPortDescStatsReply(OFPortDescStatsReply m) { - portDescStats = m; - - setState(new WaitConfigReplyState()); - } - - @Override - void processOFExperimenter(OFExperimenter m) { - unhandledMessageReceived(m); - } - } - - public class WaitSetL2TableReplyState extends OFSwitchHandshakeState { - - WaitSetL2TableReplyState() { - super(false); - } - - @Override - void processOFExperimenter(OFExperimenter m) { - // TODO: actually parse the response - setState(new WaitConfigReplyState()); - }; - - @Override - void processOFStatsReply(OFStatsReply m) { - illegalMessageReceived(m); - } - } - - /** - * We are waiting for a config reply message. Once we receive it - * we send a DescriptionStatsRequest to the switch. - * Next state: WAIT_DESCRIPTION_STAT_REPLY - */ - public class WaitConfigReplyState extends OFSwitchHandshakeState { - - WaitConfigReplyState() { - super(false); - } - - @Override - @LogMessageDocs({ - @LogMessageDoc(level="WARN", - message="Config Reply from {switch} has " + - "miss length set to {length}", - explanation="The controller requires that the switch " + - "use a miss length of 0xffff for correct " + - "function", - recommendation="Use a different switch to ensure " + - "correct function") - }) - void processOFGetConfigReply(OFGetConfigReply m) { - if (m.getMissSendLen() == 0xffff) { - log.trace("Config Reply from switch {} confirms " - + "miss length set to 0xffff", - getSwitchInfoString()); - } else { - // FIXME: we can't really deal with switches that don't send - // full packets. Shouldn't we drop the connection here? - // FIXME: count?? - log.warn("Config Reply from switch {} has" - + "miss length set to {}", - getSwitchInfoString(), - m.getMissSendLen()); - } - setState(new WaitDescriptionStatReplyState()); - } - - @Override - void processOFStatsReply(OFStatsReply m) { - illegalMessageReceived(m); - } - - @Override - void processOFError(OFErrorMsg m) { - if ((m.getErrType() == OFErrorType.BAD_REQUEST) && - (((OFBadRequestErrorMsg)m).getCode() == OFBadRequestCode.BAD_EXPERIMENTER)) { - log.debug("Switch {} has multiple tables but does not " + - "support L2 table extension", - getSwitchInfoString()); - return; - } - logErrorDisconnect(m); - } - - @Override - void enterState() { - sendHandshakeSetConfig(); - } - } - - /** - * We are waiting for a OFDescriptionStat message from the switch. - * Once we receive any stat message we try to parse it. If it's not - * a description stats message we disconnect. If its the expected - * description stats message, we: - * - use the switch driver to bind the switch and get an IOFSwitch - * instance, setup the switch instance - * - setup the IOFSwitch instance - * - add switch to FloodlightProvider and send the intial role - * request to the switch. - * - * Next state: WaitOFAuxCxnsReplyState (if OF1.3), else - * WaitInitialRoleState or WaitSwitchDriverSubHandshake - * - * All following states will have a h.sw instance! - */ - public class WaitDescriptionStatReplyState extends OFSwitchHandshakeState{ - - WaitDescriptionStatReplyState() { - super(false); - } - - @LogMessageDoc(message="Switch {switch info} bound to class " + - "{switch driver}, description {switch description}", - explanation="The specified switch has been bound to " + - "a switch driver based on the switch description" + - "received from the switch") - @Override - void processOFStatsReply(OFStatsReply m) { - // Read description, if it has been updated - if (m.getStatsType() != OFStatsType.DESC) { - illegalMessageReceived(m); - return; - } - - OFDescStatsReply descStatsReply = (OFDescStatsReply) m; - SwitchDescription description = new SwitchDescription(descStatsReply); - sw = switchManager.getOFSwitchInstance(mainConnection, description, factory, featuresReply.getDatapathId()); - switchManager.switchAdded(sw); - // set switch information - // set features reply and channel first so we a DPID and - // channel info. - sw.setFeaturesReply(featuresReply); - if(portDescStats != null) { - sw.setPortDescStats(portDescStats); - } - - // Handle pending messages now that we have a sw object - handlePendingPortStatusMessages(description); - - // Handle non OF 13 connections - if(featuresReply.getVersion().compareTo(OFVersion.OF_13) < 0){ - sw.startDriverHandshake(); - if (sw.isDriverHandshakeComplete()) { - setState(new WaitAppHandshakeState()); - } else { - setState(new WaitSwitchDriverSubHandshakeState()); - } - } - // Handle OF 1.3 MAIN Connection - else{ - setState(new WaitOFAuxCxnsReplyState()); - } - } - - void handlePendingPortStatusMessages(SwitchDescription description){ - for (OFPortStatus ps: pendingPortStatusMsg) - handlePortStatusMessage(ps, false); - pendingPortStatusMsg.clear(); - log.info("Switch {} bound to class {}," + - " description {}", - new Object[] { sw, sw.getClass(), - description }); - } - - @Override - void enterState() { - sendHandshakeDescriptionStatsRequest(); - } - } - - /** - * We are waiting for an OFAuxCxnsReply message from the switch. - * If the switch replies with a status of 0 it has approved of our request. - * If the auxiliary connections haven't already connected we will - * wait for them. - * - * Next state: WAIT_GENTABLE_DESC_STATS_REPLY (if OF1.3), else - * WAIT_INITIAL_ROLE or WAIT_SWITCH_DRIVER_SUB_HANDSHAKE - * - * All following states will have a h.sw instance! - */ - public class WaitOFAuxCxnsReplyState extends OFSwitchHandshakeState { - - // Default number of aux connections - int numAux = 0; - - WaitOFAuxCxnsReplyState() { - super(false); - } - - @Override - void processOFExperimenter(OFExperimenter m) { - if(m instanceof OFBsnSetAuxCxnsReply){ - OFBsnSetAuxCxnsReply auxReply = (OFBsnSetAuxCxnsReply) m; - - if(auxReply.getStatus() == 0){ - if(this.numAux != 0){ - // All aux connections havent arrived yet - if(auxConnections.size() != this.numAux){ - // We do nothing and wait for connections... - } else { - // Looks like all the aux connections have already arrived - gotoNextState(); - } - } - else { - // We don't need any OF Aux Connections - gotoNextState(); - } - } else { - String msg = String.format("Switch: [%s], HandshakeState: [%s], " - + "received unexpected OF Aux Status [%s]. ", - OFSwitchHandshakeHandler.this.getSwitchInfoString(), - OFSwitchHandshakeHandler.this.state.toString(), - String.valueOf(auxReply.getStatus())); - throw new OFAuxException(msg); - } - } else if(m instanceof OFBsnControllerConnectionsReply) { - OFBsnControllerConnectionsReply reply = (OFBsnControllerConnectionsReply) m; - handleControllerConnectionMessage(reply); - } - } - - @Override - public void auxConnectionOpened(IOFConnectionBackend auxConnection){ - auxConnections.put(auxConnection.getAuxId(), auxConnection); - auxConnection.setListener(OFSwitchHandshakeHandler.this); - - // Only handle success case - if(auxConnections.size() == this.numAux){ - if(log.isDebugEnabled()) { - log.debug("[{}] - all {} required aux connections open. Proceeding", - getDpid(), numAux); - } - gotoNextState(); - } else { - log.debug("[" + getDpid() + "] - {} / {} required aux connections open. Waiting for more connections. ", - auxConnections.size(), numAux); - } - } - - @Override - void processOFError(OFErrorMsg m) { - log.info("Received error on OF Aux handshake - switch does not support OF Aux: {}", m); - gotoNextState(); - } - - void gotoNextState(){ - // Register any aux connections that have been receieved. - for (IOFConnectionBackend conn : auxConnections.values()) { - sw.registerConnection(conn); - } - setState(new WaitGentableDescStatsReplyState()); - } - - @Override - public void enterState() { - this.numAux = switchManager.getNumRequiredConnections(); - sendOFAuxSetCxnsRequest(this.numAux); - } - - @Override - void processOFPortStatus(OFPortStatus m) { - handlePortStatusMessage(m, false); - } - } - - /** - * We have requested the GenTable mapping (OFBsnGenTableDescStatsRequest) - * and are waiting on the reply / replies (note: it is a multipart message). - * Once we're received all parts, we create a GenTableMapping object and - * set it in the switch. - * - * If we receive an error messsage corresponding to our request, we conclude - * that the switch doesn't support GenTables and leave the empty default - * mapping in the switch. - * - * Next state: WaitSwitchDriverSubHandshakeState() if a driver handshake is required - * WaitAppHandshake else - */ - public class WaitGentableDescStatsReplyState extends OFSwitchHandshakeState { - - private final List<OFBsnGentableDescStatsReply> pendingGenTableDescStatsReplies; - private final long pendingRequestXid; - - WaitGentableDescStatsReplyState() { - super(false); - pendingGenTableDescStatsReplies = new CopyOnWriteArrayList<>(); - pendingRequestXid = sendHandshakeGenTableDescStatsRequest(); - } - - @Override - void processOFStatsReply(OFStatsReply m) { - // Read description, if it has been updated - if (!(m instanceof OFBsnGentableDescStatsReply)) { - illegalMessageReceived(m); - return; - } - - OFBsnGentableDescStatsReply reply = (OFBsnGentableDescStatsReply) m; - - pendingGenTableDescStatsReplies.add(reply); - if(reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) { - // more replies to follow, stay in state - return; - } - - if(sw == null) { - throw new IllegalArgumentException("Switch instance unknown at time of GenTableDescStatsReply"); - } - - GenTableMap genTableMap = GenTableMap.of(pendingGenTableDescStatsReplies); - if(log.isDebugEnabled()) - log.debug("[{}] Gen Tables: {}", sw.getId(), genTableMap); - sw.setGenTableMap(genTableMap); - - gotoNextState(); - } - - public void gotoNextState() { - sw.startDriverHandshake(); - if (sw.isDriverHandshakeComplete()) - setState(new WaitAppHandshakeState()); - else - setState(new WaitSwitchDriverSubHandshakeState()); - } - - @Override - void processOFError(OFErrorMsg m) { - if(m.getXid() == pendingRequestXid) { - // let switch continue with an empty set of gentables - log.info("Received error on GenTable handshake - switch does not support gentables: {}", m); - gotoNextState(); - } else { - illegalMessageReceived(m); - } - } - - @Override - void processOFPortStatus(OFPortStatus m) { - handlePortStatusMessage(m, false); - } - } - - public class WaitSwitchDriverSubHandshakeState extends OFSwitchHandshakeState { - - WaitSwitchDriverSubHandshakeState() { - super(false); - } - - @Override - void processOFMessage(OFMessage m) { - // FIXME: other message to handle here? - sw.processDriverHandshakeMessage(m); - if (sw.isDriverHandshakeComplete()) { - setState(new WaitAppHandshakeState()); - } - } - - @Override - void processOFPortStatus(OFPortStatus m) { - handlePortStatusMessage(m, false); - } - } - - public class WaitAppHandshakeState extends OFSwitchHandshakeState { - - private final Iterator<IAppHandshakePluginFactory> pluginIterator; - private OFSwitchAppHandshakePlugin plugin; - - WaitAppHandshakeState() { - super(false); - this.pluginIterator = switchManager.getHandshakePlugins().iterator(); - } - - @Override - void processOFMessage(OFMessage m) { - if(m.getType() == OFType.PORT_STATUS){ - OFPortStatus status = (OFPortStatus) m; - handlePortStatusMessage(status, false); - } - else if(plugin != null){ - this.plugin.processOFMessage(m); - } - else{ - super.processOFMessage(m); - } - } - - /** - * Called by handshake plugins to signify that they have finished their - * sub handshake. - * - * @param result - * the result of the sub handshake - */ - void exitPlugin(PluginResult result) { - - // Proceed - if (result.getResultType() == PluginResultType.CONTINUE) { - if (log.isDebugEnabled()) { - log.debug("Switch " + getSwitchInfoString() + " app handshake plugin {} returned {}." - + " Proceeding normally..", - this.plugin.getClass().getSimpleName(), result); - } - - enterNextPlugin(); - - // Stop - } else if (result.getResultType() == PluginResultType.DISCONNECT) { - log.error("Switch " + getSwitchInfoString() + " app handshake plugin {} returned {}. " - + "Disconnecting switch.", - this.plugin.getClass().getSimpleName(), result); - mainConnection.disconnect(); - } else if (result.getResultType() == PluginResultType.QUARANTINE) { - log.warn("Switch " + getSwitchInfoString() + " app handshake plugin {} returned {}. " - + "Putting switch into quarantine state.", - this.plugin.getClass().getSimpleName(), - result); - setState(new QuarantineState(result.getReason())); - } - } - - @Override - public void enterState() { - enterNextPlugin(); - } - - /** - * Initialize the plugin and begin. - * - * @param plugin the of switch app handshake plugin - */ - public void enterNextPlugin() { - if(this.pluginIterator.hasNext()){ - this.plugin = pluginIterator.next().createPlugin(); - this.plugin.init(this, sw, timer); - this.plugin.enterPlugin(); - } - // No more plugins left... - else{ - // Non OF 1.3 - if(featuresReply.getVersion().compareTo(OFVersion.OF_13) < 0){ - setState(new WaitInitialRoleState()); - } else { - setState(new WaitControllerCxnsReplyState()); - } - } - } - - @Override - void processOFPortStatus(OFPortStatus m) { - handlePortStatusMessage(m, false); - } - - OFSwitchAppHandshakePlugin getCurrentPlugin() { - return plugin; - } - - } - - /** - * Switch is in a quarantine state. Essentially the handshake is complete. - */ - public class QuarantineState extends OFSwitchHandshakeState { - - private final String quarantineReason; - - QuarantineState(String reason) { - super(true); - this.quarantineReason = reason; - } - - @Override - public void enterState() { - setSwitchStatus(SwitchStatus.QUARANTINED); - } - - @Override - void processOFPortStatus(OFPortStatus m) { - handlePortStatusMessage(m, false); - } - - public String getQuarantineReason() { - return this.quarantineReason; - } - } - - public class WaitControllerCxnsReplyState extends OFSwitchHandshakeState { - - WaitControllerCxnsReplyState() { - super(false); - } - - @Override - void processOFExperimenter(OFExperimenter m) { - if(m instanceof OFBsnControllerConnectionsReply) { - OFBsnControllerConnectionsReply reply = (OFBsnControllerConnectionsReply) m; - handleControllerConnectionMessage(reply); - - setState(new WaitInitialRoleState()); - } else { - unhandledMessageReceived(m); - } - } - - @Override - void processOFPortStatus(OFPortStatus m) { - handlePortStatusMessage(m, false); - } - - @Override - public void enterState() { - // Write out request down - OFBsnControllerConnectionsRequest request = factory.buildBsnControllerConnectionsRequest() - .build(); - mainConnection.write(request); - - } - - } - - /** - * We are waiting for the initial role reply message (or error indication) - * from the switch. Next State: MASTER or SLAVE - */ - public class WaitInitialRoleState extends OFSwitchHandshakeState { - - WaitInitialRoleState() { - super(false); - } - - @Override - void processOFError(OFErrorMsg m) { - // role changer will ignore the error if it isn't for it - boolean didHandle = roleChanger.deliverError(m); - if (!didHandle) { - logError(m); - } - } - - @Override - void processOFExperimenter(OFExperimenter m) { - OFControllerRole role = extractNiciraRoleReply(m); - // If role == null it measn the message wasn't really a - // Nicira role reply. We ignore this case. - if (role != null) { - roleChanger.deliverRoleReply(m.getXid(), role); - } else if(m instanceof OFBsnControllerConnectionsReply) { - OFBsnControllerConnectionsReply reply = (OFBsnControllerConnectionsReply) m; - handleControllerConnectionMessage(reply); - } else { - unhandledMessageReceived(m); - } - } - - @Override - void processOFRoleReply(OFRoleReply m) { - roleChanger.deliverRoleReply(m.getXid(), m.getRole()); - } - - @Override - void processOFStatsReply(OFStatsReply m) { - illegalMessageReceived(m); - } - - @Override - void processOFPortStatus(OFPortStatus m) { - handlePortStatusMessage(m, false); - } - - @Override - void enterState(){ - sendRoleRequest(roleManager.getOFControllerRole()); - } - } - - /** - * The switch is in MASTER role. We enter this state after a role - * reply from the switch is received (or the controller is MASTER - * and the switch doesn't support roles). The handshake is complete at - * this point. We only leave this state if the switch disconnects or - * if we send a role request for SLAVE /and/ receive the role reply for - * SLAVE. - */ - public class MasterState extends OFSwitchHandshakeState { - - MasterState() { - super(true); - } - - @Override - void enterState() { - setSwitchStatus(SwitchStatus.MASTER); - } - - @LogMessageDoc(level="WARN", - message="Received permission error from switch {} while" + - "being master. Reasserting master role.", - explanation="The switch has denied an operation likely " + - "indicating inconsistent controller roles", - recommendation="This situation can occurs transiently during role" + - " changes. If, however, the condition persists or happens" + - " frequently this indicates a role inconsistency. " + - LogMessageDoc.CHECK_CONTROLLER ) - @Override - void processOFError(OFErrorMsg m) { - // role changer will ignore the error if it isn't for it - boolean didHandle = roleChanger.deliverError(m); - if (didHandle) - return; - if ((m.getErrType() == OFErrorType.BAD_REQUEST) && - (((OFBadRequestErrorMsg)m).getCode() == OFBadRequestCode.EPERM)) { - // We are the master controller and the switch returned - // a permission error. This is a likely indicator that - // the switch thinks we are slave. Reassert our - // role - // FIXME: this could be really bad during role transitions - // if two controllers are master (even if its only for - // a brief period). We might need to see if these errors - // persist before we reassert - switchManagerCounters.epermErrorWhileSwitchIsMaster.increment(); - log.warn("Received permission error from switch {} while" + - "being master. Reasserting master role.", - getSwitchInfoString()); - reassertRole(OFControllerRole.ROLE_MASTER); - } - else if ((m.getErrType() == OFErrorType.FLOW_MOD_FAILED) && - (((OFFlowModFailedErrorMsg)m).getCode() == OFFlowModFailedCode.ALL_TABLES_FULL)) { - sw.setTableFull(true); - } - else { - logError(m); - } - dispatchMessage(m); - } - - @Override - void processOFExperimenter(OFExperimenter m) { - OFControllerRole role = extractNiciraRoleReply(m); - // If role == null it means the message wasn't really a - // Nicira role reply. We ignore just dispatch it to the - // OFMessage listenersa in this case. - if (role != null) { - roleChanger.deliverRoleReply(m.getXid(), role); - } else if (m instanceof OFBsnControllerConnectionsReply){ - OFBsnControllerConnectionsReply reply = (OFBsnControllerConnectionsReply) m; - handleControllerConnectionMessage(reply); - } else { - dispatchMessage(m); - } - } - - - @Override - void processOFRoleReply(OFRoleReply m) { - roleChanger.deliverRoleReply(m.getXid(), m.getRole()); - } - - @Override - void processOFPortStatus(OFPortStatus m) { - handlePortStatusMessage(m, true); - } - - @Override - void processOFPacketIn(OFPacketIn m) { - dispatchMessage(m); - } - - @Override - void processOFFlowRemoved(OFFlowRemoved m) { - dispatchMessage(m); - } - } - - /** - * The switch is in SLAVE role. We enter this state after a role - * reply from the switch is received. The handshake is complete at - * this point. We only leave this state if the switch disconnects or - * if we send a role request for MASTER /and/ receive the role reply for - * MASTER. - * TODO: CURRENTLY, WE DO NOT DISPATCH ANY MESSAGE IN SLAVE. - */ - public class SlaveState extends OFSwitchHandshakeState { - - SlaveState() { - super(true); - } - - @Override - void enterState() { - setSwitchStatus(SwitchStatus.SLAVE); - } - - @Override - void processOFError(OFErrorMsg m) { - // role changer will ignore the error if it isn't for it - boolean didHandle = roleChanger.deliverError(m); - if (!didHandle) { - logError(m); - } - } - - @Override - void processOFStatsReply(OFStatsReply m) { - } - - @Override - void processOFPortStatus(OFPortStatus m) { - handlePortStatusMessage(m, true); - } - - @Override - void processOFExperimenter(OFExperimenter m) { - OFControllerRole role = extractNiciraRoleReply(m); - // If role == null it means the message wasn't really a - // Nicira role reply. We ignore it. - if (role != null) { - roleChanger.deliverRoleReply(m.getXid(), role); - } else if (m instanceof OFBsnControllerConnectionsReply){ - OFBsnControllerConnectionsReply reply = (OFBsnControllerConnectionsReply) m; - handleControllerConnectionMessage(reply); - } else { - unhandledMessageReceived(m); - } - } - - @Override - void processOFRoleReply(OFRoleReply m) { - roleChanger.deliverRoleReply(m.getXid(), m.getRole()); - } - - @Override - @LogMessageDoc(level="WARN", - message="Received PacketIn from switch {} while" + - "being slave. Reasserting slave role.", - explanation="The switch has receive a PacketIn despite being " + - "in slave role indicating inconsistent controller roles", - recommendation="This situation can occurs transiently during role" + - " changes. If, however, the condition persists or happens" + - " frequently this indicates a role inconsistency. " + - LogMessageDoc.CHECK_CONTROLLER ) - void processOFPacketIn(OFPacketIn m) { - // we don't expect packetIn while slave, reassert we are slave - switchManagerCounters.packetInWhileSwitchIsSlave.increment(); - log.warn("Received PacketIn from switch {} while" + - "being slave. Reasserting slave role.", sw); - reassertRole(OFControllerRole.ROLE_SLAVE); - } - }; - - - /** - * Create a new unconnected OFChannelHandler. - * @param controller - * @param broker - * @throws SwitchHandshakeHandlerException - */ - OFSwitchHandshakeHandler(@Nonnull IOFConnectionBackend connection, - @Nonnull OFFeaturesReply featuresReply, - @Nonnull IOFSwitchManager switchManager, - @Nonnull RoleManager roleManager, - @Nonnull Timer timer) { - Preconditions.checkNotNull(connection, "connection"); - Preconditions.checkNotNull(featuresReply, "featuresReply"); - Preconditions.checkNotNull(switchManager, "switchManager"); - Preconditions.checkNotNull(roleManager, "roleManager"); - Preconditions.checkNotNull(timer, "timer"); - Preconditions.checkArgument(connection.getAuxId().equals(OFAuxId.MAIN), - "connection must be MAIN connection but is %s", connection); - - this.switchManager = switchManager; - this.roleManager = roleManager; - this.mainConnection = connection; - this.auxConnections = new ConcurrentHashMap<OFAuxId, IOFConnectionBackend>(); - this.featuresReply = featuresReply; - this.timer = timer; - this.switchManagerCounters = switchManager.getCounters(); - this.factory = OFFactories.getFactory(featuresReply.getVersion()); - this.roleChanger = new RoleChanger(DEFAULT_ROLE_TIMEOUT_NS); - setState(new InitState()); - this.pendingPortStatusMsg = new ArrayList<OFPortStatus>(); - - connection.setListener(this); - } - - /** - * This begins the switch handshake. We start where the OFChannelHandler - * left off, right after receiving the OFFeaturesReply. - */ - public void beginHandshake() { - Preconditions.checkState(state instanceof InitState, "must be in InitState"); - - if (this.featuresReply.getNTables() > 1) { - log.debug("Have {} table for switch {}", this.featuresReply.getNTables(), - getSwitchInfoString()); - // likely supports L2 table extensions. Send set - sendHandshakeL2TableSet(); - // TODO: no L2 SET reply yet, so fire and forget the set - } - - if(this.featuresReply.getVersion().compareTo(OFVersion.OF_13) < 0) { - setState(new WaitConfigReplyState()); - } else { - // OF 1.3. Ask for Port Descriptions - setState(new WaitPortDescStatsReplyState()); - } - } - - public DatapathId getDpid(){ - return this.featuresReply.getDatapathId(); - } - - public OFAuxId getOFAuxId(){ - return this.featuresReply.getAuxiliaryId(); - } - - /** - * Is this a state in which the handshake has completed? - * @return true if the handshake is complete - */ - public boolean isHandshakeComplete() { - return this.state.isHandshakeComplete(); - } - - /** - * Forwards to RoleChanger. See there. - * @param role - */ - void sendRoleRequestIfNotPending(OFControllerRole role) { - try { - roleChanger.sendRoleRequestIfNotPending(role); - } catch (IOException e) { - log.error("Disconnecting switch {} due to IO Error: {}", - getSwitchInfoString(), e.getMessage()); - mainConnection.disconnect(); - } - } - - - /** - * Forwards to RoleChanger. See there. - * @param role - */ - void sendRoleRequest(OFControllerRole role) { - try { - roleChanger.sendRoleRequest(role); - } catch (IOException e) { - log.error("Disconnecting switch {} due to IO Error: {}", - getSwitchInfoString(), e.getMessage()); - mainConnection.disconnect(); - } - } - - /** - * Dispatches the message to the controller packet pipeline - */ - private void dispatchMessage(OFMessage m) { - this.switchManager.handleMessage(this.sw, m, null); - } - - /** - * Return a string describing this switch based on the already available - * information (DPID and/or remote socket) - * @return - */ - private String getSwitchInfoString() { - if (sw != null) - return sw.toString(); - String channelString; - if (mainConnection == null || mainConnection.getRemoteInetAddress() == null) { - channelString = "?"; - } else { - channelString = mainConnection.getRemoteInetAddress().toString(); - } - String dpidString; - if (featuresReply == null) { - dpidString = "?"; - } else { - dpidString = featuresReply.getDatapathId().toString(); - } - return String.format("[%s DPID[%s]]", channelString, dpidString); - } - - /** - * Update the channels state. Only called from the state machine. - * TODO: enforce restricted state transitions - * @param state - */ - private void setState(OFSwitchHandshakeState state) { - this.state = state; - state.logState(); - state.enterState(); - } - - private void sendOFAuxSetCxnsRequest(int numAux) { - OFBsnSetAuxCxnsRequest request = factory.buildBsnSetAuxCxnsRequest() - .setNumAux(numAux) - .build(); - mainConnection.write(request); - } - - public void processOFMessage(OFMessage m) { - state.processOFMessage(m); - } - - /** - * Send the configuration requests to tell the switch we want full - * packets - * @throws IOException - */ - private void sendHandshakeSetConfig() { - // Ensure we receive the full packet via PacketIn - // FIXME: We don't set the reassembly flags. - OFSetConfig configSet = factory.buildSetConfig() - .setXid(handshakeTransactionIds--) - .setMissSendLen(0xffff) - .build(); - - // Barrier - OFBarrierRequest barrier = factory.buildBarrierRequest() - .setXid(handshakeTransactionIds--) - .build(); - - // Verify (need barrier?) - OFGetConfigRequest configReq = factory.buildGetConfigRequest() - .setXid(handshakeTransactionIds--) - .build(); - List<OFMessage> msgList = ImmutableList.<OFMessage>of(configSet, barrier, configReq); - mainConnection.write(msgList); - } - - protected void sendPortDescRequest() { - mainConnection.write(factory.portDescStatsRequest(ImmutableSet.<OFStatsRequestFlags>of())); - } - - /** - * send a description state request - */ - private void sendHandshakeDescriptionStatsRequest() { - // Send description stats request to set switch-specific flags - OFDescStatsRequest descStatsRequest = factory.buildDescStatsRequest() - .setXid(handshakeTransactionIds--) - .build(); - mainConnection.write(descStatsRequest); - } - - /** send a BSN GenTable DescStats Request, querying for the tables - * available at the switch. - * @return - */ - private long sendHandshakeGenTableDescStatsRequest() { - long xid = handshakeTransactionIds--; - OFBsnGentableDescStatsRequest descStatsRequest = factory.buildBsnGentableDescStatsRequest() - .setXid(xid) - .build(); - - mainConnection.write(descStatsRequest); - return xid; - } - - //TODO: Can we nuke this? - jason - /** - * Send an setL2TableSet message to the switch. - */ - private void sendHandshakeL2TableSet() { - // The BsnSetL2Table message is only supported in OF 1.0, so skip if - // we're using a later version - if (factory.getVersion() != OFVersion.OF_10) - return; - - OFBsnSetL2TableRequest m = factory.buildBsnSetL2TableRequest() - .setXid(handshakeTransactionIds--) - .setL2TableEnable(true) - .setL2TablePriority(1000) - .build(); - mainConnection.write(m); - } - - OFSwitchHandshakeState getStateForTesting() { - return state; - } - - void reassertRole(OFControllerRole role){ - this.roleManager.reassertRole(this, HARole.ofOFRole(role)); - } - - void useRoleChangerWithOtherTimeoutForTesting(long roleTimeoutMs) { - roleChanger = new RoleChanger(TimeUnit.MILLISECONDS.toNanos(roleTimeoutMs)); - } - - /** - * Called by the switch manager when new aux connections have connected. - * This alerts the state machine of an aux connection. - * - * @param connection - * the aux connection - */ - public synchronized void auxConnectionOpened(IOFConnectionBackend connection) { - if(log.isDebugEnabled()) - log.debug("[{}] - Switch Handshake - new aux connection {}", - this.getDpid(), connection.getAuxId()); - this.state.auxConnectionOpened(connection); - } - - /** - * Gets the main connection - * - * @return the main connection - */ - public IOFConnectionBackend getMainConnection() { - return this.mainConnection; - } - - /** - * Determines if this handshake handler is responsible for the supplied - * connection. - * - * @param connection - * an OF connection - * @return true if the handler has the connection - */ - public boolean hasConnection(IOFConnectionBackend connection) { - if (this.mainConnection.equals(connection) - || this.auxConnections.get(connection.getAuxId()) == connection) { - return true; - } else { - return false; - } - } - - void cleanup() { - for (IOFConnectionBackend conn : this.auxConnections.values()) { - conn.disconnect(); - } - - this.mainConnection.disconnect(); - } - - public String getState() { - return this.state.getClass().getSimpleName(); - } - - public String getQuarantineReason() { - if(this.state instanceof QuarantineState) { - QuarantineState qs = (QuarantineState) this.state; - return qs.getQuarantineReason(); - } - return null; - } - - /** - * Gets the current connections that this switch handshake handler is - * responsible for. Used primarily by the REST API. - * @return an immutable list of IOFConnections - */ - public ImmutableList<IOFConnection> getConnections() { - ImmutableList.Builder<IOFConnection> builder = ImmutableList.builder(); - - builder.add(mainConnection); - builder.addAll(auxConnections.values()); - - return builder.build(); - } - - - /** IOFConnectionListener */ - @Override - public void connectionClosed(IOFConnectionBackend connection) { - // Disconnect handler's remaining connections - cleanup(); - - // Only remove the switch handler when the main connection is - // closed - if (connection == this.mainConnection) { - switchManager.handshakeDisconnected(connection.getDatapathId()); - if(sw != null) { - log.debug("[{}] - main connection {} closed - disconnecting switch", - connection); - - setSwitchStatus(SwitchStatus.DISCONNECTED); - switchManager.switchDisconnected(sw); - } - } - } - - @Override - public void messageReceived(IOFConnectionBackend connection, OFMessage m) { - processOFMessage(m); - } - - @Override - public boolean isSwitchHandshakeComplete(IOFConnectionBackend connection) { - return state.isHandshakeComplete(); - } - - public void setSwitchStatus(SwitchStatus status) { - if(sw != null) { - SwitchStatus oldStatus = sw.getStatus(); - if(oldStatus != status) { - sw.setStatus(status); - switchManager.switchStatusChanged(sw, oldStatus, status); - } else { - log.warn("[{}] SwitchStatus change to {} requested, switch is already in status", - mainConnection.getDatapathId()); - } - } else { - log.warn("[{}] SwitchStatus change to {} requested, but switch is not allocated yet", - mainConnection.getDatapathId()); - } - } - -} diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchImpl.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..51dfddc1a1625f530610f7ab00d407d57c9a9e70 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchImpl.java @@ -0,0 +1,46 @@ +/** +* Copyright 2012, Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* 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 net.floodlightcontroller.core.internal; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.openflow.protocol.statistics.OFDescriptionStatistics; + +import net.floodlightcontroller.core.OFSwitchBase; + +/** + * This is the internal representation of an openflow switch. + */ +public class OFSwitchImpl extends OFSwitchBase { + + @Override + @JsonIgnore + public void setSwitchProperties(OFDescriptionStatistics description) { + this.description = new OFDescriptionStatistics(description); + } + + @Override + public OFPortType getPortType(short port_num) { + return OFPortType.NORMAL; + } + + @Override + @JsonIgnore + public boolean isFastPort(short port_num) { + return false; + } +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java deleted file mode 100644 index dd1cc86b7566655479c0f87234da9c33f6ed370e..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java +++ /dev/null @@ -1,756 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.Executors; - -import org.jboss.netty.bootstrap.ServerBootstrap; -import org.jboss.netty.channel.ChannelPipelineFactory; -import org.jboss.netty.channel.group.ChannelGroup; -import org.jboss.netty.channel.group.DefaultChannelGroup; -import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; - -import net.floodlightcontroller.core.FloodlightContext; -import net.floodlightcontroller.core.HAListenerTypeMarker; -import net.floodlightcontroller.core.HARole; -import net.floodlightcontroller.core.IFloodlightProviderService; -import net.floodlightcontroller.core.IHAListener; -import net.floodlightcontroller.core.IOFConnectionBackend; -import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.IOFSwitch.SwitchStatus; -import net.floodlightcontroller.core.IOFSwitchBackend; -import net.floodlightcontroller.core.IOFSwitchDriver; -import net.floodlightcontroller.core.IOFSwitchListener; -import net.floodlightcontroller.core.LogicalOFMessageCategory; -import net.floodlightcontroller.core.PortChangeType; -import net.floodlightcontroller.core.SwitchDescription; -import net.floodlightcontroller.core.annotations.LogMessageDoc; -import net.floodlightcontroller.core.annotations.LogMessageDocs; -import net.floodlightcontroller.core.internal.Controller.IUpdate; -import net.floodlightcontroller.core.internal.Controller.ModuleLoaderState; -import net.floodlightcontroller.core.module.FloodlightModuleContext; -import net.floodlightcontroller.core.module.FloodlightModuleException; -import net.floodlightcontroller.core.module.IFloodlightModule; -import net.floodlightcontroller.core.module.IFloodlightService; -import net.floodlightcontroller.core.rest.SwitchRepresentation; -import net.floodlightcontroller.debugcounter.IDebugCounterService; -import net.floodlightcontroller.debugevent.IDebugEventService; -import net.floodlightcontroller.debugevent.IDebugEventService.EventType; -import net.floodlightcontroller.debugevent.IEventCategory; -import net.floodlightcontroller.debugevent.MockDebugEventService; - -import org.projectfloodlight.openflow.protocol.OFControllerRole; -import org.projectfloodlight.openflow.protocol.OFFactory; -import org.projectfloodlight.openflow.protocol.OFFeaturesReply; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFAuxId; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; - -/** - * The Switch Manager class contains most of the code involved with dealing - * with switches. The Switch manager keeps track of the switches known to the controller, - * their status, and any important information about the switch lifecycle. The - * Switch Manager also provides the switch service, which allows other modules - * to hook in switch listeners and get basic access to switch information. - * - * @author gregor, capveg, sovietaced - * - */ -public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListener, IHAListener, IFloodlightModule, IOFSwitchService { - private static final Logger log = LoggerFactory.getLogger(OFSwitchManager.class); - - private volatile OFControllerRole role; - private SwitchManagerCounters counters; - - private ConcurrentHashMap<DatapathId, OFSwitchHandshakeHandler> switchHandlers; - private ConcurrentHashMap<DatapathId, IOFSwitchBackend> switches; - - private ISwitchDriverRegistry driverRegistry; - - private Set<LogicalOFMessageCategory> logicalOFMessageCategories = new CopyOnWriteArraySet<LogicalOFMessageCategory>(); - private final List<IAppHandshakePluginFactory> handshakePlugins = new CopyOnWriteArrayList<IAppHandshakePluginFactory>(); - private int numRequiredConnections = -1; - // Event IDs for debug events - protected IEventCategory<SwitchEvent> evSwitch; - - // ISwitchService - protected Set<IOFSwitchListener> switchListeners; - - // Module Dependencies - IFloodlightProviderService floodlightProvider; - IDebugEventService debugEventService; - IDebugCounterService debugCounterService; - - /** IHAListener Implementation **/ - @Override - public void transitionToActive() { - this.role = HARole.ACTIVE.getOFRole(); - } - - @Override - public void transitionToStandby() { - this.role = HARole.STANDBY.getOFRole(); - } - - /** IOFSwitchManager Implementation **/ - - @Override public SwitchManagerCounters getCounters() { - return this.counters; - } - - private void addUpdateToQueue(IUpdate iUpdate) { - floodlightProvider.addUpdateToQueue(iUpdate); - } - - @Override - public synchronized void switchAdded(IOFSwitchBackend sw) { - DatapathId dpid = sw.getId(); - IOFSwitchBackend oldSw = this.switches.put(dpid, sw); - // Update event history - evSwitch.newEventWithFlush(new SwitchEvent(dpid, "connected")); - - if (oldSw == sw) { - // Note == for object equality, not .equals for value - counters.errorActivatedSwitchNotPresent - .increment(); - log.error("Switch {} added twice?", sw); - return; - } else if (oldSw != null) { - // This happens either when we have switches with duplicate - // DPIDs or when a switch reconnects before we saw the - // disconnect - counters.switchWithSameDpidActivated - .increment(); - log.warn("New switch added {} for already-added switch {}", sw, oldSw); - // We need to disconnect and remove the old switch - // TODO: we notify switch listeners that the switch has been - // removed and then we notify them that the new one has been - // added. One could argue that a switchChanged notification - // might be more appropriate in this case.... - oldSw.cancelAllPendingRequests(); - addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.REMOVED)); - oldSw.disconnect(); - } - - } - - @LogMessageDocs({ - @LogMessageDoc(level="ERROR", - message="Switch {switch} activated but was already active", - explanation="A switch that was already activated was " + - "activated again. This should not happen.", - recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG - ), - @LogMessageDoc(level="WARN", - message="New switch added {switch} for already-added switch {switch}", - explanation="A switch with the same DPID as another switch " + - "connected to the controller. This can be caused by " + - "multiple switches configured with the same DPID, or " + - "by a switch reconnected very quickly after " + - "disconnecting.", - recommendation="If this happens repeatedly, it is likely there " + - "are switches with duplicate DPIDs on the network. " + - "Reconfigure the appropriate switches. If it happens " + - "very rarely, then it is likely this is a transient " + - "network problem that can be ignored." - ) - }) - @Override - public synchronized void switchStatusChanged(IOFSwitchBackend sw, SwitchStatus oldStatus, SwitchStatus newStatus) { - DatapathId dpid = sw.getId(); - IOFSwitchBackend presentSw = this.switches.get(dpid); - - if (presentSw != sw) { - // Note == for object equality, not .equals for value - counters.errorActivatedSwitchNotPresent - .increment(); - log.warn("Switch {} status change but not present in sync manager", sw); - return; - } - evSwitch.newEventWithFlush(new SwitchEvent(dpid, - String.format("%s -> %s", - oldStatus, - newStatus))); - - if(newStatus == SwitchStatus.MASTER && role != OFControllerRole.ROLE_MASTER) { - counters.invalidSwitchActivatedWhileSlave.increment(); - log.error("Switch {} activated but controller not MASTER", sw); - sw.disconnect(); - return; // only react to switch connections when master - } - - if(!oldStatus.isVisible() && newStatus.isVisible()) { - // the switch has just become visible. Send 'add' notification to our - // listeners - addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.ADDED)); - } else if((oldStatus.isVisible() && !newStatus.isVisible())) { - addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.REMOVED)); - } - - // note: no else if - both may be true - if(oldStatus != SwitchStatus.MASTER && newStatus == SwitchStatus.MASTER ) { - counters.switchActivated.increment(); - addUpdateToQueue(new SwitchUpdate(dpid, - SwitchUpdateType.ACTIVATED)); - } else if(oldStatus == SwitchStatus.MASTER && newStatus != SwitchStatus.MASTER ) { - counters.switchDeactivated.increment(); - addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.DEACTIVATED)); - } - } - - @Override - public synchronized void switchDisconnected(IOFSwitchBackend sw) { - DatapathId dpid = sw.getId(); - IOFSwitchBackend presentSw = this.switches.get(dpid); - - if (presentSw != sw) { - // Note == for object equality, not .equals for value - counters.errorActivatedSwitchNotPresent - .increment(); - log.warn("Switch {} disconnect but not present in sync manager", sw); - return; - } - - counters.switchDisconnected.increment(); - this.switches.remove(dpid); - } - - @Override public void handshakeDisconnected(DatapathId dpid) { - this.switchHandlers.remove(dpid); - } - - public Iterable<IOFSwitch> getActiveSwitches() { - ImmutableList.Builder<IOFSwitch> builder = ImmutableList.builder(); - for(IOFSwitch sw: switches.values()) { - if(sw.getStatus().isControllable()) - builder.add(sw); - } - return builder.build(); - } - - public Map<DatapathId, IOFSwitch> getAllSwitchMap(boolean showInvisible) { - if(showInvisible) { - return ImmutableMap.<DatapathId, IOFSwitch>copyOf(switches); - } else { - ImmutableMap.Builder<DatapathId, IOFSwitch> builder = ImmutableMap.builder(); - for(IOFSwitch sw: switches.values()) { - if(sw.getStatus().isVisible()) - builder.put(sw.getId(), sw); - } - return builder.build(); - } - } - - @Override - public Map<DatapathId, IOFSwitch> getAllSwitchMap() { - return getAllSwitchMap(true); - } - - @Override - public Set<DatapathId> getAllSwitchDpids() { - return getAllSwitchMap().keySet(); - } - - public Set<DatapathId> getAllSwitchDpids(boolean showInvisible) { - return getAllSwitchMap(showInvisible).keySet(); - } - - @Override - public IOFSwitch getSwitch(DatapathId dpid) { - return this.switches.get(dpid); - } - - @Override - public IOFSwitch getActiveSwitch(DatapathId dpid) { - IOFSwitchBackend sw = this.switches.get(dpid); - if(sw != null && sw.getStatus().isVisible()) - return sw; - else - return null; - } - - enum SwitchUpdateType { - ADDED, - REMOVED, - ACTIVATED, - DEACTIVATED, - PORTCHANGED, - OTHERCHANGE - } - - /** - * Update message indicating a switch was added or removed - */ - class SwitchUpdate implements IUpdate { - private final DatapathId swId; - private final SwitchUpdateType switchUpdateType; - private final OFPortDesc port; - private final PortChangeType changeType; - - public SwitchUpdate(DatapathId swId, SwitchUpdateType switchUpdateType) { - this(swId, switchUpdateType, null, null); - } - - public SwitchUpdate(DatapathId swId, - SwitchUpdateType switchUpdateType, - OFPortDesc port, - PortChangeType changeType) { - if (switchUpdateType == SwitchUpdateType.PORTCHANGED) { - if (port == null) { - throw new NullPointerException("Port must not be null " + - "for PORTCHANGED updates"); - } - if (changeType == null) { - throw new NullPointerException("ChangeType must not be " + - "null for PORTCHANGED updates"); - } - } else { - if (port != null || changeType != null) { - throw new IllegalArgumentException("port and changeType " + - "must be null for " + switchUpdateType + - " updates"); - } - } - this.swId = swId; - this.switchUpdateType = switchUpdateType; - this.port = port; - this.changeType = changeType; - } - - @Override - public void dispatch() { - if (log.isTraceEnabled()) { - log.trace("Dispatching switch update {} {}", swId, switchUpdateType); - } - if (switchListeners != null) { - for (IOFSwitchListener listener : switchListeners) { - switch(switchUpdateType) { - case ADDED: - // don't count here. We have more specific - // counters before the update is created - listener.switchAdded(swId); - break; - case REMOVED: - // don't count here. We have more specific - // counters before the update is created - listener.switchRemoved(swId); - break; - case PORTCHANGED: - counters.switchPortChanged - .increment(); - listener.switchPortChanged(swId, port, changeType); - break; - case ACTIVATED: - // don't count here. We have more specific - // counters before the update is created - listener.switchActivated(swId); - break; - case DEACTIVATED: - // ignore - break; - case OTHERCHANGE: - counters.switchOtherChange - .increment(); - listener.switchChanged(swId); - break; - } - } - } - } - } - - /** - * Handles a new OF Connection - * @param IOFConnectionBackend connection an opened OF Connection - * @param OFFeaturesReply featuresReply the features reply received for the opened connection. - * It is needed for the rest of the switch handshake. - */ - @Override - public void connectionOpened(IOFConnectionBackend connection, - OFFeaturesReply featuresReply) { - DatapathId dpid = connection.getDatapathId(); - OFAuxId auxId = connection.getAuxId(); - - log.debug("{} opened", connection); - - if(auxId.equals(OFAuxId.MAIN)) { - - // Create a new switch handshake handler - OFSwitchHandshakeHandler handler = - new OFSwitchHandshakeHandler(connection, featuresReply, this, - floodlightProvider.getRoleManager(), floodlightProvider.getTimer()); - - OFSwitchHandshakeHandler oldHandler = switchHandlers.put(dpid, handler); - - // Disconnect all the handler's connections - if(oldHandler != null){ - log.debug("{} is a new main connection, killing old handler connections", - connection); - oldHandler.cleanup(); - } - - handler.beginHandshake(); - - } else { - OFSwitchHandshakeHandler handler = switchHandlers.get(dpid); - - if(handler != null) { - handler.auxConnectionOpened(connection); - } - // Connections have arrived before the switchhandler is ready - else { - log.warn("{} arrived before main connection, closing connection", connection); - connection.disconnect(); - } - } - } - - @Override - public void addSwitchEvent(DatapathId dpid, String reason, boolean flushNow) { - if (flushNow) - evSwitch.newEventWithFlush(new SwitchEvent(dpid, reason)); - else - evSwitch.newEventNoFlush(new SwitchEvent(dpid, reason)); - } - - @Override - public synchronized void notifyPortChanged(IOFSwitchBackend sw, - OFPortDesc port, - PortChangeType changeType) { - Preconditions.checkNotNull(sw, "switch must not be null"); - Preconditions.checkNotNull(port, "port must not be null"); - Preconditions.checkNotNull(changeType, "changeType must not be null"); - - if (role != OFControllerRole.ROLE_MASTER) { - counters.invalidPortsChanged.increment(); - return; - } - if (!this.switches.containsKey(sw.getId())) { - counters.invalidPortsChanged.increment(); - return; - } - - if(sw.getStatus().isVisible()) { - // no need to count here. SwitchUpdate.dispatch will count - // the portchanged - SwitchUpdate update = new SwitchUpdate(sw.getId(), - SwitchUpdateType.PORTCHANGED, - port, changeType); - addUpdateToQueue(update); - } - } - - @Override - public IOFSwitchBackend getOFSwitchInstance(IOFConnectionBackend connection, - SwitchDescription description, - OFFactory factory, DatapathId datapathId) { - return this.driverRegistry.getOFSwitchInstance(connection, description, factory, datapathId); - } - - @Override - public void handleMessage(IOFSwitchBackend sw, OFMessage m, FloodlightContext bContext) { - floodlightProvider.handleMessage(sw, m, bContext); - } - - @Override - public void addOFSwitchDriver(String manufacturerDescriptionPrefix, - IOFSwitchDriver driver) { - this.driverRegistry.addSwitchDriver(manufacturerDescriptionPrefix, driver); - } - - @Override - public ImmutableList<OFSwitchHandshakeHandler> getSwitchHandshakeHandlers() { - return ImmutableList.copyOf(this.switchHandlers.values()); - } - - @Override - public int getNumRequiredConnections() { - Preconditions.checkState(numRequiredConnections >= 0, "numRequiredConnections not calculated"); - return numRequiredConnections; - } - - public Set<LogicalOFMessageCategory> getLogicalOFMessageCategories() { - return logicalOFMessageCategories; - } - - private int calcNumRequiredConnections() { - if(!this.logicalOFMessageCategories.isEmpty()){ - // We use tree set here to maintain ordering - TreeSet<OFAuxId> auxConnections = new TreeSet<OFAuxId>(); - - for(LogicalOFMessageCategory category : this.logicalOFMessageCategories){ - auxConnections.add(category.getAuxId()); - } - - OFAuxId first = auxConnections.first(); - OFAuxId last = auxConnections.last(); - - // Check for contiguous set (1....size()) - if(first.equals(OFAuxId.MAIN)) { - if(last.getValue() != auxConnections.size() - 1){ - throw new IllegalStateException("Logical OF message categories must maintain contiguous OF Aux Ids! i.e. (0,1,2,3,4,5)"); - } - return auxConnections.size() - 1; - } else if(first.equals(OFAuxId.of(1))) { - if(last.getValue() != auxConnections.size()){ - throw new IllegalStateException("Logical OF message categories must maintain contiguous OF Aux Ids! i.e. (1,2,3,4,5)"); - } - return auxConnections.size(); - } else { - throw new IllegalStateException("Logical OF message categories must start at 0 (MAIN) or 1"); - } - } else { - return 0; - } - } - - /** ISwitchService Implementation **/ - @Override - public void addOFSwitchListener(IOFSwitchListener listener) { - this.switchListeners.add(listener); - } - - @Override - public void removeOFSwitchListener(IOFSwitchListener listener) { - this.switchListeners.remove(listener); - } - - @Override - public void registerLogicalOFMessageCategory(LogicalOFMessageCategory category) { - logicalOFMessageCategories.add(category); - } - - @Override - public boolean isCategoryRegistered(LogicalOFMessageCategory category) { - return logicalOFMessageCategories.contains(category); - } - - @Override - public SwitchRepresentation getSwitchRepresentation(DatapathId dpid) { - IOFSwitch sw = this.switches.get(dpid); - OFSwitchHandshakeHandler handler = this.switchHandlers.get(dpid); - - if(sw != null && handler != null) { - return new SwitchRepresentation(sw, handler); - } - return null; - } - - @Override - public List<SwitchRepresentation> getSwitchRepresentations() { - - List<SwitchRepresentation> representations = new ArrayList<SwitchRepresentation>(); - - for(DatapathId dpid : this.switches.keySet()) { - SwitchRepresentation representation = getSwitchRepresentation(dpid); - if(representation != null) { - representations.add(representation); - } - } - return representations; - } - - @Override - public void registerHandshakePlugin(IAppHandshakePluginFactory factory) { - Preconditions.checkState(floodlightProvider.getModuleLoaderState() == ModuleLoaderState.INIT, - "handshakeplugins can only be registered when the module loader is in state INIT!"); - handshakePlugins.add(factory); - } - - @Override - public List<IAppHandshakePluginFactory> getHandshakePlugins() { - return handshakePlugins; - } - - /* IFloodlightModule Implementation */ - @Override - public Collection<Class<? extends IFloodlightService>> - getModuleServices() { - Collection<Class<? extends IFloodlightService>> l = - new ArrayList<Class<? extends IFloodlightService>>(); - l.add(IOFSwitchService.class); - return l; - } - - @Override - public Map<Class<? extends IFloodlightService>, IFloodlightService> - getServiceImpls() { - Map<Class<? extends IFloodlightService>, IFloodlightService> m = - new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); - m.put(IOFSwitchService.class, this); - return m; - } - - @Override - public Collection<Class<? extends IFloodlightService>> - getModuleDependencies() { - Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); - - l.add(IFloodlightProviderService.class); - l.add(IDebugEventService.class); - l.add(IDebugCounterService.class); - - return l; - } - - @Override - public void init(FloodlightModuleContext context) throws FloodlightModuleException { - // Module dependencies - floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class); - debugEventService = context.getServiceImpl(IDebugEventService.class); - debugCounterService = context.getServiceImpl(IDebugCounterService.class); - - // Module variables - switchHandlers = new ConcurrentHashMap<DatapathId, OFSwitchHandshakeHandler>(); - switches = new ConcurrentHashMap<DatapathId, IOFSwitchBackend>(); - floodlightProvider.getTimer(); - counters = new SwitchManagerCounters(debugCounterService); - driverRegistry = new NaiveSwitchDriverRegistry(this); - - this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>(); - - this.counters = new SwitchManagerCounters(debugCounterService); - - } - - @Override - public void startUp(FloodlightModuleContext context) throws FloodlightModuleException { - startUpBase(context); - bootstrapNetty(); - } - - /** - * Startup method that includes everything besides the netty boostrap. - * This has been isolated for testing. - * @param context floodlight module context - * @throws FloodlightModuleException - */ - public void startUpBase(FloodlightModuleContext context) throws FloodlightModuleException { - // Initial Role - role = floodlightProvider.getRole().getOFRole(); - - // IRoleListener - floodlightProvider.addHAListener(this); - - loadLogicalCategories(); - - registerDebugEvents(); - } - - /** - * Bootstraps netty, the server that handles all openflow connections - */ - public void bootstrapNetty() { - try { - final ServerBootstrap bootstrap = createServerBootStrap(); - - bootstrap.setOption("reuseAddr", true); - bootstrap.setOption("child.keepAlive", true); - bootstrap.setOption("child.tcpNoDelay", true); - bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE); - - ChannelPipelineFactory pfact = - new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService); - bootstrap.setPipelineFactory(pfact); - InetSocketAddress sa = new InetSocketAddress(floodlightProvider.getOFPort()); - final ChannelGroup cg = new DefaultChannelGroup(); - cg.add(bootstrap.bind(sa)); - - log.info("Listening for switch connections on {}", sa); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Helper that bootstrapNetty. - * @return - */ - private ServerBootstrap createServerBootStrap() { - if (floodlightProvider.getWorkerThreads() == 0) { - return new ServerBootstrap( - new NioServerSocketChannelFactory( - Executors.newCachedThreadPool(), - Executors.newCachedThreadPool())); - } else { - return new ServerBootstrap( - new NioServerSocketChannelFactory( - Executors.newCachedThreadPool(), - Executors.newCachedThreadPool(), floodlightProvider.getWorkerThreads())); - } - } - - /** - * Performs startup related actions for logical OF message categories. - * Setting the categories list to immutable ensures that unsupported operation - * exceptions will be activated if modifications are attempted. - */ - public void loadLogicalCategories() { - logicalOFMessageCategories = ImmutableSet.copyOf(logicalOFMessageCategories); - numRequiredConnections = calcNumRequiredConnections(); - } - - /** - * Registers an event handler with the debug event service - * for switch events. - * @throws FloodlightModuleException - */ - private void registerDebugEvents() throws FloodlightModuleException { - if (debugEventService == null) { - debugEventService = new MockDebugEventService(); - } - evSwitch = debugEventService.buildEvent(SwitchEvent.class) - .setModuleName(this.counters.getPrefix()) - .setEventName("switch-event") - .setEventDescription("Switch connected, disconnected or port changed") - .setEventType(EventType.ALWAYS_LOG) - .setBufferCapacity(100) - .register(); - } - - @Override - public String getName() { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean isCallbackOrderingPrereq(HAListenerTypeMarker type, - String name) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean isCallbackOrderingPostreq(HAListenerTypeMarker type, - String name) { - // TODO Auto-generated method stub - return false; - } - - @Override - public void controllerNodeIPsChanged( - Map<String, String> curControllerNodeIPs, - Map<String, String> addedControllerNodeIPs, - Map<String, String> removedControllerNodeIPs) { - // TODO Auto-generated method stub - - } -} diff --git a/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java b/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..82a3c67d658ddee3b20f702f2019117a044ccb4b --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java @@ -0,0 +1,77 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* 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 net.floodlightcontroller.core.internal; + +import java.util.concurrent.ThreadPoolExecutor; + +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.handler.execution.ExecutionHandler; +import org.jboss.netty.handler.timeout.IdleStateHandler; +import org.jboss.netty.handler.timeout.ReadTimeoutHandler; +import org.jboss.netty.util.ExternalResourceReleasable; +import org.jboss.netty.util.HashedWheelTimer; +import org.jboss.netty.util.Timer; + +/** + * Creates a ChannelPipeline for a server-side openflow channel + * @author readams + */ +public class OpenflowPipelineFactory + implements ChannelPipelineFactory, ExternalResourceReleasable { + + protected Controller controller; + protected ThreadPoolExecutor pipelineExecutor; + protected Timer timer; + protected IdleStateHandler idleHandler; + protected ReadTimeoutHandler readTimeoutHandler; + + public OpenflowPipelineFactory(Controller controller, + ThreadPoolExecutor pipelineExecutor) { + super(); + this.controller = controller; + this.pipelineExecutor = pipelineExecutor; + this.timer = new HashedWheelTimer(); + this.idleHandler = new IdleStateHandler(timer, 20, 25, 0); + this.readTimeoutHandler = new ReadTimeoutHandler(timer, 30); + } + + @Override + public ChannelPipeline getPipeline() throws Exception { + OFChannelHandler handler = new OFChannelHandler(controller); + + ChannelPipeline pipeline = Channels.pipeline(); + pipeline.addLast("ofmessagedecoder", new OFMessageDecoder()); + pipeline.addLast("ofmessageencoder", new OFMessageEncoder()); + pipeline.addLast("idle", idleHandler); + pipeline.addLast("timeout", readTimeoutHandler); + pipeline.addLast("handshaketimeout", + new HandshakeTimeoutHandler(handler, timer, 15)); + if (pipelineExecutor != null) + pipeline.addLast("pipelineExecutor", + new ExecutionHandler(pipelineExecutor)); + pipeline.addLast("handler", handler); + return pipeline; + } + + @Override + public void releaseExternalResources() { + timer.stop(); + } +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/PluginResult.java b/src/main/java/net/floodlightcontroller/core/internal/PluginResult.java deleted file mode 100644 index e11a527de0a4df91c500bfd5e421fae6c9dd94c9..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/PluginResult.java +++ /dev/null @@ -1,40 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import javax.annotation.Nonnull; - -import net.floodlightcontroller.core.internal.OFSwitchAppHandshakePlugin.PluginResultType; - -import com.google.common.base.Preconditions; - -/** - * Class that represents the result of an app handshake plugin. - * - */ -public class PluginResult { - - private final PluginResultType result; - private final String reason; - - public PluginResult(@Nonnull PluginResultType result) { - this.result = result; - this.reason = null; - } - - public PluginResult(@Nonnull PluginResultType result, String reason) { - Preconditions.checkNotNull(result, "result must not be null"); - - if(result != PluginResultType.QUARANTINE && reason != null) - throw new IllegalStateException("Reason can only be set for Quarantine PluginResult"); - - this.result = result; - this.reason = reason; - } - - public PluginResultType getResultType() { - return this.result; - } - - public String getReason() { - return this.reason; - } -} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/internal/RoleManager.java b/src/main/java/net/floodlightcontroller/core/internal/RoleManager.java deleted file mode 100644 index f37da6707e7e24db99315f16ba02e935a898b00f..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/RoleManager.java +++ /dev/null @@ -1,258 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import java.util.Date; -import java.util.Map.Entry; - -import javax.annotation.Nonnull; - -import net.floodlightcontroller.core.HARole; -import net.floodlightcontroller.core.internal.Controller; -import net.floodlightcontroller.core.internal.RoleManagerCounters; -import net.floodlightcontroller.core.IHAListener; -import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.IOFSwitchBackend; -import net.floodlightcontroller.core.internal.IOFSwitchService; -import net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler; -import net.floodlightcontroller.core.internal.Controller.IUpdate; - -import net.floodlightcontroller.core.internal.RoleManager; -import net.floodlightcontroller.core.internal.RoleManager.SwitchRoleUpdate; -import org.projectfloodlight.openflow.protocol.OFControllerRole; -import org.projectfloodlight.openflow.types.DatapathId; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Preconditions; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import net.floodlightcontroller.core.RoleInfo; - -/** - * A utility class to manage the <i>controller roles</i>. - * - * A utility class to manage the <i>controller roles</i> as opposed - * to the switch roles. The class manages the controllers current role, - * handles role change requests, and maintains the list of connected - * switch(-channel) so it can notify the switches of role changes. - * - * We need to ensure that every connected switch is always send the - * correct role. Therefore, switch add, sending of the intial role, and - * changing role need to use mutexes to ensure this. This has the ugly - * side-effect of requiring calls between controller and OFChannelHandler - * - * This class is fully thread safe. Its method can safely be called from - * any thread. - * - * @author gregor - * - */ -public class RoleManager { - private volatile RoleInfo currentRoleInfo; - private final Controller controller; - private final RoleManagerCounters counters; - private volatile boolean notifiedLeader; - - private static final Logger log = - LoggerFactory.getLogger(RoleManager.class); - - /** - * @param role initial role - * @param roleChangeDescription initial value of the change description - * @throws NullPointerException if role or roleChangeDescription is null - */ - public RoleManager(@Nonnull Controller controller, - @Nonnull HARole role, - @Nonnull String roleChangeDescription) { - Preconditions.checkNotNull(controller, "controller must not be null"); - Preconditions.checkNotNull(role, "role must not be null"); - Preconditions.checkNotNull(roleChangeDescription, "roleChangeDescription must not be null"); - - this.currentRoleInfo = new RoleInfo(role, - roleChangeDescription, - new Date()); - this.controller = controller; - this.counters = new RoleManagerCounters(controller.getDebugCounter()); - } - - /** - * Re-assert a role for the given channel handler. - * - * The caller specifies the role that should be reasserted. We only - * reassert the role if the controller's current role matches the - * reasserted role and there is no role request for the reasserted role - * pending. - * @param ofSwitchHandshakeHandler The OFChannelHandler on which we should reassert. - * @param role The role to reassert - */ - public synchronized void reassertRole(OFSwitchHandshakeHandler ofSwitchHandshakeHandler, HARole role) { - // check if the requested reassertion actually makes sense - if (this.getRole() != role) - return; - ofSwitchHandshakeHandler.sendRoleRequestIfNotPending(this.getRole().getOFRole()); - } - - /** - * Set the controller's new role and notify switches. - * - * This method updates the controllers current role and notifies all - * connected switches of the new role is different from the current - * role. We dampen calls to this method. See class description for - * details. - * - * @param role The new role. - * @param roleChangeDescription A textual description of why the role - * was changed. For information purposes only. - * @throws NullPointerException if role or roleChangeDescription is null - */ - public synchronized void setRole(@Nonnull HARole role, @Nonnull String roleChangeDescription) { - Preconditions.checkNotNull(role, "role must not be null"); - Preconditions.checkNotNull(roleChangeDescription, "roleChangeDescription must not be null"); - - if (role == getRole()) { - counters.setSameRole.increment(); - log.debug("Received role request for {} but controller is " - + "already {}. Ignoring it.", role, this.getRole()); - return; - } - - if (this.getRole() == HARole.STANDBY && role == HARole.ACTIVE) { - // At this point we are guaranteed that we will execute the code - // below exactly once during the lifetime of this process! And - // it will be a to MASTER transition - counters.setRoleMaster.increment(); - } - - log.info("Received role request for {} (reason: {})." - + " Initiating transition", role, roleChangeDescription); - - currentRoleInfo = - new RoleInfo(role, roleChangeDescription, new Date()); - - // NOTE: HARoleUpdate will terminate floodlight on transition to STANDBY - controller.addUpdateToQueue(new HARoleUpdate(role)); - controller.addUpdateToQueue(new SwitchRoleUpdate(role)); - - } - - @SuppressFBWarnings(value="UG_SYNC_SET_UNSYNC_GET", - justification = "setter is synchronized for mutual exclusion, " - + "currentRoleInfo is volatile, so no sync on getter needed") - public synchronized HARole getRole() { - return currentRoleInfo.getRole(); - } - - public synchronized OFControllerRole getOFControllerRole() { - return getRole().getOFRole(); - } - - /** - * Return the RoleInfo object describing the current role. - * - * Return the RoleInfo object describing the current role. The - * RoleInfo object is used by REST API users. - * @return the current RoleInfo object - */ - public RoleInfo getRoleInfo() { - return currentRoleInfo; - } - - private void attemptActiveTransition() { - if(!switchesHaveAnotherMaster()){ - // No valid cluster controller connections found, become ACTIVE! - setRole(HARole.ACTIVE, "Leader election assigned ACTIVE role"); - notifiedLeader = false; - } - } - - /** - * Iterates over all the switches and checks to see if they have controller - * connections that points towards another master controller. - * @return - */ - private boolean switchesHaveAnotherMaster() { - IOFSwitchService switchService = controller.getSwitchService(); - - for(Entry<DatapathId, IOFSwitch> switchMap : switchService.getAllSwitchMap().entrySet()){ - IOFSwitchBackend sw = (IOFSwitchBackend) switchMap.getValue(); - if(sw.hasAnotherMaster()){ - return true; - } - } - return false; - } - - public void notifyControllerConnectionUpdate() { - if(notifiedLeader && currentRoleInfo.getRole() != HARole.ACTIVE) { - attemptActiveTransition(); - } - } - - /** - * Update message indicating controller's role has changed. - * RoleManager, which enqueues these updates guarantees that we will - * only have a single transition from SLAVE to MASTER. - * - * When the role update from master to slave is complete, the HARoleUpdate - * will terminate floodlight. - */ - private class HARoleUpdate implements IUpdate { - private final HARole newRole; - public HARoleUpdate(HARole newRole) { - this.newRole = newRole; - } - - @Override - public void dispatch() { - if (log.isDebugEnabled()) { - log.debug("Dispatching HA Role update newRole = {}", - newRole); - } - for (IHAListener listener : controller.haListeners.getOrderedListeners()) { - if (log.isTraceEnabled()) { - log.trace("Calling HAListener {} with transitionTo{}", - listener.getName(), newRole); - } - switch(newRole) { - case ACTIVE: - listener.transitionToActive(); - break; - case STANDBY: - listener.transitionToStandby(); - break; - } - } - - controller.setNotifiedRole(newRole); - - if(newRole == HARole.STANDBY) { - String reason = String.format("Received role request to " - + "transition from ACTIVE to STANDBY (reason: %s)", - getRoleInfo().getRoleChangeDescription()); - //TODO @Ryan shutdownService.terminate(reason, 0); - } - } - } - - public class SwitchRoleUpdate implements IUpdate { - private final HARole role; - - public SwitchRoleUpdate(HARole role) { - this.role = role; - } - - @Override - public void dispatch() { - if (log.isDebugEnabled()) { - log.debug("Dispatching switch role update newRole = {}, switch role = {}", - this.role, this.role.getOFRole()); - } - - for (OFSwitchHandshakeHandler h: controller.getSwitchService().getSwitchHandshakeHandlers()) - h.sendRoleRequest(this.role.getOFRole()); - } - } - - public RoleManagerCounters getCounters() { - return this.counters; - } -} diff --git a/src/main/java/net/floodlightcontroller/core/internal/RoleManagerCounters.java b/src/main/java/net/floodlightcontroller/core/internal/RoleManagerCounters.java deleted file mode 100644 index 04d948339ecedc0c487a57384481f155591eb328..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/RoleManagerCounters.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import net.floodlightcontroller.debugcounter.IDebugCounter; -import net.floodlightcontroller.debugcounter.IDebugCounterService; -import net.floodlightcontroller.debugcounter.IDebugCounterService.MetaData; - - public class RoleManagerCounters { - - public final String prefix = RoleManager.class.getSimpleName(); - public final IDebugCounter setSameRole; - public final IDebugCounter setRoleMaster; - - public RoleManagerCounters(IDebugCounterService debugCounters) { - debugCounters.registerModule(prefix); - setSameRole = - debugCounters.registerCounter( - prefix, "set-same-role", - "Controller received a role request for the same " + - "role the controller already had", - MetaData.WARN); - - setRoleMaster = - debugCounters.registerCounter( - prefix, "set-role-master", - "Controller received a role request with role of " + - "MASTER. This counter can be at most 1."); - } - - public String getPrefix(){ - return this.prefix; - } - } \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/internal/SwitchEvent.java b/src/main/java/net/floodlightcontroller/core/internal/SwitchEvent.java deleted file mode 100644 index 7a4f36b37cc54e5c9c5d7698ffc04b9ac7adba2d..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/SwitchEvent.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn; -import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType; -import org.projectfloodlight.openflow.types.DatapathId; - -public class SwitchEvent { - @EventColumn(name = "dpid", description = EventFieldType.DPID) - DatapathId dpid; - - @EventColumn(name = "reason", description = EventFieldType.STRING) - String reason; - - public SwitchEvent(DatapathId dpid, String reason) { - this.dpid = dpid; - this.reason = reason; - } -} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/internal/SwitchManagerCounters.java b/src/main/java/net/floodlightcontroller/core/internal/SwitchManagerCounters.java deleted file mode 100644 index 942809cd45d4a060c865a9355442830e713ae318..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/internal/SwitchManagerCounters.java +++ /dev/null @@ -1,208 +0,0 @@ -package net.floodlightcontroller.core.internal; - -import net.floodlightcontroller.debugcounter.IDebugCounter; -import net.floodlightcontroller.debugcounter.IDebugCounterService; -import net.floodlightcontroller.debugcounter.IDebugCounterService.MetaData; - -public class SwitchManagerCounters { - - public final String prefix = OFSwitchManager.class.getSimpleName(); - public final IDebugCounter invalidPortsChanged; - public final IDebugCounter switchConnected; - public final IDebugCounter invalidSwitchActivatedWhileSlave; - public final IDebugCounter switchActivated; - public final IDebugCounter switchDeactivated; - public final IDebugCounter errorActivatedSwitchNotPresent; - public final IDebugCounter switchWithSameDpidActivated; - public final IDebugCounter switchDisconnected; - public final IDebugCounter switchPortChanged; - public final IDebugCounter switchOtherChange; - public final IDebugCounter switchDisconnectReadTimeout; - public final IDebugCounter switchDisconnectHandshakeTimeout; - public final IDebugCounter switchDisconnectIOError; - public final IDebugCounter switchDisconnectParseError; - public final IDebugCounter switchDisconnectSwitchStateException; - public final IDebugCounter rejectedExecutionException; - public final IDebugCounter switchDisconnectOtherException; - public final IDebugCounter unhandledMessage; - public final IDebugCounter packetInWhileSwitchIsSlave; - public final IDebugCounter roleNotResentBecauseRolePending; - public final IDebugCounter epermErrorWhileSwitchIsMaster; - public final IDebugCounter roleReplyTimeout; - public final IDebugCounter roleReplyReceived; - public final IDebugCounter roleReplyErrorUnsupported; - - public SwitchManagerCounters(IDebugCounterService debugCounters) { - debugCounters.registerModule(prefix); - invalidPortsChanged = - debugCounters.registerCounter( - prefix, "invalid-ports-changed", - "Received an unexpected ports changed " + - "notification while the controller was in " + - "SLAVE role.", - MetaData.WARN); - - invalidSwitchActivatedWhileSlave = - debugCounters.registerCounter( - prefix, "invalid-switch-activated-while-slave", - "Received an unexpected switchActivated " + - "notification while the controller was in " + - "SLAVE role.", - MetaData.WARN); - - switchActivated = - debugCounters.registerCounter( - prefix, "switch-activated", - "A switch connected to this controller is now " + - "in MASTER role"); - - switchDeactivated = - debugCounters.registerCounter( - prefix, "switch-activated", - "A switch connected to this controller is now " + - "in SLAVE role"); - - errorActivatedSwitchNotPresent = // err - debugCounters.registerCounter( - prefix, "error-same-switch-reactivated", - "A switch that was already in active state " + - "was activated again. This indicates a " + - "controller defect", - MetaData.ERROR); - - switchWithSameDpidActivated = // warn - debugCounters.registerCounter( - prefix, "switch-with-same-dpid-activated", - "A switch with the same DPID as another switch " + - "connected to the controller. This can be " + - "caused by multiple switches configured with " + - "the same DPID or by a switch reconnecting very " + - "quickly.", - MetaData.WARN); - - switchDisconnected = - debugCounters.registerCounter( - prefix, "switch-disconnected", - "FIXME: switch has disconnected"); - - switchPortChanged = - debugCounters.registerCounter( - prefix, "switch-port-changed", - "Number of times switch ports have changed"); - switchOtherChange = - debugCounters.registerCounter( - prefix, "switch-other-change", - "Number of times other information of a switch " + - "has changed."); - - switchDisconnectReadTimeout = - debugCounters.registerCounter( - prefix, "switch-disconnect-read-timeout", - "Number of times a switch was disconnected due " + - "due the switch failing to send OpenFlow " + - "messages or responding to OpenFlow ECHOs", - MetaData.ERROR); - switchDisconnectHandshakeTimeout = - debugCounters.registerCounter( - prefix, "switch-disconnect-handshake-timeout", - "Number of times a switch was disconnected " + - "because it failed to complete the handshake " + - "in time.", - MetaData.ERROR); - switchDisconnectIOError = - debugCounters.registerCounter( - prefix, "switch-disconnect-io-error", - "Number of times a switch was disconnected " + - "due to IO errors on the switch connection.", - MetaData.ERROR); - switchDisconnectParseError = - debugCounters.registerCounter( - prefix, "switch-disconnect-parse-error", - "Number of times a switch was disconnected " + - "because it sent an invalid packet that could " + - "not be parsed", - MetaData.ERROR); - - switchDisconnectSwitchStateException = - debugCounters.registerCounter( - prefix, "switch-disconnect-switch-state-exception", - "Number of times a switch was disconnected " + - "because it sent messages that were invalid " + - "given the switch connection's state.", - MetaData.ERROR); - rejectedExecutionException = - debugCounters.registerCounter( - prefix, "rejected-execution-exception", - "TODO", - MetaData.ERROR); - - switchDisconnectOtherException = - debugCounters.registerCounter( - prefix, "switch-disconnect-other-exception", - "Number of times a switch was disconnected " + - "due to an exceptional situation not covered " + - "by other counters", - MetaData.ERROR); - - switchConnected = - debugCounters.registerCounter( - prefix, "switch-connected", - "Number of times a new switch connection was " + - "established"); - - unhandledMessage = - debugCounters.registerCounter( - prefix, "unhandled-message", - "Number of times an OpenFlow message was " + - "received that the controller ignored because " + - "it was inapproriate given the switch " + - "connection's state.", - MetaData.WARN); - - packetInWhileSwitchIsSlave = - debugCounters.registerCounter( - prefix, "packet-in-while-switch-is-slave", - "Number of times a packet in was received " + - "from a switch that was in SLAVE role. " + - "Possibly inidicates inconsistent roles."); - epermErrorWhileSwitchIsMaster = - debugCounters.registerCounter( - prefix, "eperm-error-while-switch-is-master", - "Number of times a permission error was " + - "received while the switch was in MASTER role. " + - "Possibly inidicates inconsistent roles.", - MetaData.WARN); - - roleNotResentBecauseRolePending = - debugCounters.registerCounter( - prefix, "role-not-resent-because-role-pending", - "The controller tried to reestablish a role " + - "with a switch but did not do so because a " + - "previous role request was still pending"); - roleReplyTimeout = - debugCounters.registerCounter( - prefix, "role-reply-timeout", - "Number of times a role request message did not " + - "receive the expected reply from a switch", - MetaData.WARN); - - roleReplyReceived = // expected RoleReply received - debugCounters.registerCounter( - prefix, "role-reply-received", - "Number of times the controller received the " + - "expected role reply message from a switch"); - - roleReplyErrorUnsupported = - debugCounters.registerCounter( - prefix, "role-reply-error-unsupported", - "Number of times the controller received an " + - "error from a switch in response to a role " + - "request indicating that the switch does not " + - "support roles."); - } - - public String getPrefix(){ - return this.prefix; - } - -} diff --git a/src/main/java/net/floodlightcontroller/core/module/ApplicationLoader.java b/src/main/java/net/floodlightcontroller/core/module/ApplicationLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..f56dfa996e90f9550a09fb3ae817ed4685431046 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/module/ApplicationLoader.java @@ -0,0 +1,179 @@ +package net.floodlightcontroller.core.module; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; + +/** + * Load an application from a configuration directory + * @author readams + */ +public class ApplicationLoader + implements IFloodlightModule, IApplicationService { + + /** + * Representation for the application configuration + * @author readams + */ + public static class Application { + private String name; + private String[] modules; + private Map<String,String> config; + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public String[] getModules() { + return modules; + } + public void setModules(String[] modules) { + this.modules = modules; + } + public Map<String, String> getConfig() { + return config; + } + public void setConfig(Map<String, String> config) { + this.config = config; + } + } + + + protected static Logger logger = + LoggerFactory.getLogger(ApplicationLoader.class); + protected static ObjectMapper mapper = new ObjectMapper(); + protected static ObjectReader reader = mapper.reader(Application.class); + + IModuleService moduleService; + + private static String APP_RESOURCE_PATH = "apps/"; + + /** + * Path containing application description files + */ + protected String applicationPath; + + /** + * Application to load + */ + protected String application; + + // ***************** + // IFloodlightModule + // ***************** + + @Override + public Collection<Class<? extends IFloodlightService>> + getModuleServices() { + Collection<Class<? extends IFloodlightService>> l = + new ArrayList<Class<? extends IFloodlightService>>(); + l.add(IApplicationService.class); + return l; + } + + @Override + public Map<Class<? extends IFloodlightService>, IFloodlightService> + getServiceImpls() { + + Map<Class<? extends IFloodlightService>, + IFloodlightService> m = + new HashMap<Class<? extends IFloodlightService>, + IFloodlightService>(); + // We are the class that implements the service + m.put(IApplicationService.class, this); + return m; + } + + @Override + public Collection<Class<? extends IFloodlightService>> + getModuleDependencies() { + return null; + } + + @Override + public void init(FloodlightModuleContext context) + throws FloodlightModuleException { + moduleService = context.getServiceImpl(IModuleService.class); + + Map<String,String> config = context.getConfigParams(this); + if (config.containsKey("appsd")) + applicationPath = config.get("appsd"); + if (config.containsKey("application")) + application = config.get("application"); + } + + @Override + public void startUp(FloodlightModuleContext context) + throws FloodlightModuleException { + if (application == null) { + throw new FloodlightModuleException("No application to load"); + } + + // attempt to load from application path + File appPath; + if (applicationPath != null && + (appPath = new File(applicationPath)).exists() && + appPath.isDirectory()) { + File[] files = appPath.listFiles(); + Arrays.sort(files); + for (File f : files) { + if (f.isFile() && f.getName().matches(".*\\.json$")); + try { + if (loadApplication(new FileInputStream(f), f.getPath())) + return; + } catch (FileNotFoundException e) { + throw new FloodlightModuleException(e); + } + } + } + + // attempt to load from classpath. Note here that the file needs + // to be named after the application to be successful + try { + String r = APP_RESOURCE_PATH + application + ".json"; + InputStream is = getClass().getClassLoader().getResourceAsStream(r); + loadApplication(is, "resource: " + r); + } catch (Exception e) { + throw new FloodlightModuleException(e); + } + + } + + private boolean loadApplication(InputStream is, String path) + throws FloodlightModuleException { + Application a; + try { + a = reader.readValue(is); + } catch (Exception e) { + throw new FloodlightModuleException("Could not read application " + + path, e); + } + if (application.equals(a.getName())) { + Properties p = new Properties(); + if (a.getConfig() != null) + p.putAll(a.getConfig()); + if (a.getModules() != null) { + logger.info("Loading application {}", a.getName()); + moduleService.loadModulesFromList(Arrays.asList(a.getModules()), + p); + return true; + } + } + return false; + } +} diff --git a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleConfigFileNotFoundException.java b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleConfigFileNotFoundException.java deleted file mode 100644 index e20d1e06f74d572590ca63d4660c799b4278a2fd..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleConfigFileNotFoundException.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.floodlightcontroller.core.module; - -public class FloodlightModuleConfigFileNotFoundException extends - FloodlightModuleException { - private static final long serialVersionUID = 1L; - - public FloodlightModuleConfigFileNotFoundException(String fileName) { - super("No such file or directory: " + fileName); - } -} diff --git a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleContext.java b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleContext.java new file mode 100644 index 0000000000000000000000000000000000000000..27c1f968ac0534ecf1bfcd293ce471669016719e --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleContext.java @@ -0,0 +1,124 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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 net.floodlightcontroller.core.module; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * The service registry for an IFloodlightProvider. + * @author alexreimers + */ +public class FloodlightModuleContext implements IFloodlightModuleContext { + protected Map<Class<? extends IFloodlightService>, IFloodlightService> serviceMap; + protected Map<Class<? extends IFloodlightModule>, Map<String, String>> configParams; + protected Collection<IFloodlightModule> moduleSet; + + /** + * Creates the ModuleContext for use with this IFloodlightProvider. + * This will be used as a module registry for all IFloodlightModule(s). + */ + public FloodlightModuleContext() { + serviceMap = + new HashMap<Class<? extends IFloodlightService>, + IFloodlightService>(); + configParams = + new HashMap<Class<? extends IFloodlightModule>, + Map<String, String>>(); + } + + /** + * Adds a IFloodlightModule for this Context. + * @param clazz the service class + * @param service The IFloodlightService to add to the registry + */ + public void addService(Class<? extends IFloodlightService> clazz, + IFloodlightService service) { + serviceMap.put(clazz, service); + } + + @SuppressWarnings("unchecked") + @Override + public <T extends IFloodlightService> T getServiceImpl(Class<T> service) { + IFloodlightService s = serviceMap.get(service); + return (T)s; + } + + @Override + public Collection<Class<? extends IFloodlightService>> getAllServices() { + return serviceMap.keySet(); + } + + @Override + public Collection<IFloodlightModule> getAllModules() { + return moduleSet; + } + + public void addModules(Collection<IFloodlightModule> modSet) { + if (this.moduleSet == null) + this.moduleSet = new ArrayList<IFloodlightModule>(); + this.moduleSet.addAll(modSet); + } + + @Override + public Map<String, String> getConfigParams(IFloodlightModule module) { + Class<? extends IFloodlightModule> clazz = module.getClass(); + return getConfigParams(clazz); + } + + @Override + public Map<String, String> getConfigParams(Class<? extends IFloodlightModule> clazz) { + Map<String, String> retMap = configParams.get(clazz); + if (retMap == null) { + // Return an empty map if none exists so the module does not + // need to null check the map + retMap = new HashMap<String, String>(); + configParams.put(clazz, retMap); + } + + // also add any configuration parameters for superclasses, but + // only if more specific configuration does not override it + for (Class<? extends IFloodlightModule> c : configParams.keySet()) { + if (c.isAssignableFrom(clazz)) { + for (Map.Entry<String, String> ent : configParams.get(c).entrySet()) { + if (!retMap.containsKey(ent.getKey())) { + retMap.put(ent.getKey(), ent.getValue()); + } + } + } + } + + return retMap; + } + + /** + * Adds a configuration parameter for a module + * @param mod The fully qualified module name to add the parameter to + * @param key The configuration parameter key + * @param value The configuration parameter value + */ + public void addConfigParam(IFloodlightModule mod, String key, String value) { + Map<String, String> moduleParams = configParams.get(mod.getClass()); + if (moduleParams == null) { + moduleParams = new HashMap<String, String>(); + configParams.put(mod.getClass(), moduleParams); + } + moduleParams.put(key, value); + } + } diff --git a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleException.java b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleException.java new file mode 100644 index 0000000000000000000000000000000000000000..5cddd37fccb8564ae7e31b6f2418a2339ce354c7 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleException.java @@ -0,0 +1,37 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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 net.floodlightcontroller.core.module; + +public class FloodlightModuleException extends Exception { + private static final long serialVersionUID = 1L; + + public FloodlightModuleException(String error) { + super(error); + } + + public FloodlightModuleException() { + super(); + } + + public FloodlightModuleException(String message, Throwable cause) { + super(message, cause); + } + + public FloodlightModuleException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleLoader.java b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..32e4fc031e26eafee38bd282e0a1b21e0b73ebfb --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/module/FloodlightModuleLoader.java @@ -0,0 +1,501 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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 net.floodlightcontroller.core.module; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Queue; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.Set; + +import net.floodlightcontroller.core.annotations.LogMessageDoc; +import net.floodlightcontroller.core.annotations.LogMessageDocs; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Finds all Floodlight modules in the class path and loads/starts them. + * @author alexreimers + * + */ +public class FloodlightModuleLoader implements IModuleService { + protected static Logger logger = + LoggerFactory.getLogger(FloodlightModuleLoader.class); + + protected static Map<Class<? extends IFloodlightService>, + Collection<IFloodlightModule>> serviceMap; + protected static Map<IFloodlightModule, + Collection<Class<? extends + IFloodlightService>>> moduleServiceMap; + protected static Map<String, IFloodlightModule> moduleNameMap; + protected static Object lock = new Object(); + + protected static Set<String> initedSet = new HashSet<String>(); + protected static Set<String> startedSet = new HashSet<String>();; + + protected FloodlightModuleContext floodlightModuleContext; + + public static final String COMPILED_CONF_FILE = + "floodlightdefault.properties"; + public static final String FLOODLIGHT_MODULES_KEY = + "floodlight.modules"; + public static final String FLOODLIGHT_CONFD = + "floodlight.confd"; + + public FloodlightModuleLoader() { + floodlightModuleContext = new FloodlightModuleContext(); + floodlightModuleContext.addService(IModuleService.class, this); + } + + /** + * Finds all IFloodlightModule(s) in the classpath. It creates 3 Maps. + * serviceMap -> Maps a service to a module + * moduleServiceMap -> Maps a module to all the services it provides + * moduleNameMap -> Maps the string name to the module + * @throws FloodlightModuleException If two modules are specified in the configuration + * that provide the same service. + */ + protected static void findAllModules(Collection<String> mList) throws FloodlightModuleException { + synchronized (lock) { + if (serviceMap != null) return; + serviceMap = + new HashMap<Class<? extends IFloodlightService>, + Collection<IFloodlightModule>>(); + moduleServiceMap = + new HashMap<IFloodlightModule, + Collection<Class<? extends + IFloodlightService>>>(); + moduleNameMap = new HashMap<String, IFloodlightModule>(); + + // Get all the current modules in the classpath + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + ServiceLoader<IFloodlightModule> moduleLoader + = ServiceLoader.load(IFloodlightModule.class, cl); + // Iterate for each module, iterate through and add it's services + Iterator<IFloodlightModule> moduleIter = moduleLoader.iterator(); + while (moduleIter.hasNext()) { + IFloodlightModule m = null; + try { + m = moduleIter.next(); + } catch (ServiceConfigurationError sce) { + logger.error("Could not find module: {}", sce.getMessage()); + continue; + } + //} + //for (IFloodlightModule m : moduleLoader) { + if (logger.isDebugEnabled()) { + logger.debug("Found module " + m.getClass().getName()); + } + + // Set up moduleNameMap + moduleNameMap.put(m.getClass().getCanonicalName(), m); + + // Set up serviceMap + Collection<Class<? extends IFloodlightService>> servs = + m.getModuleServices(); + if (servs != null) { + moduleServiceMap.put(m, servs); + for (Class<? extends IFloodlightService> s : servs) { + Collection<IFloodlightModule> mods = + serviceMap.get(s); + if (mods == null) { + mods = new ArrayList<IFloodlightModule>(); + serviceMap.put(s, mods); + } + mods.add(m); + // Make sure they haven't specified duplicate modules in the config + int dupInConf = 0; + for (IFloodlightModule cMod : mods) { + if (mList.contains(cMod.getClass().getCanonicalName())) + dupInConf += 1; + } + + if (dupInConf > 1) { + String duplicateMods = ""; + for (IFloodlightModule mod : mods) { + duplicateMods += mod.getClass().getCanonicalName() + ", "; + } + throw new FloodlightModuleException("ERROR! The configuraiton" + + " file specifies more than one module that provides the service " + + s.getCanonicalName() +". Please specify only ONE of the " + + "following modules in the config file: " + duplicateMods); + } + } + } + } + } + } + + /** + * Loads the modules from a specified configuration file. + * @param fName The configuration file path + * @return An IFloodlightModuleContext with all the modules to be started + * @throws FloodlightModuleException + */ + @LogMessageDocs({ + @LogMessageDoc(level="INFO", + message="Loading modules from file {file name}", + explanation="The controller is initializing its module " + + "configuration from the specified properties file"), + @LogMessageDoc(level="INFO", + message="Loading default modules", + explanation="The controller is initializing its module " + + "configuration to the default configuration"), + @LogMessageDoc(level="ERROR", + message="Could not load module configuration file", + explanation="The controller failed to read the " + + "module configuration file", + recommendation="Verify that the module configuration is " + + "present. " + LogMessageDoc.CHECK_CONTROLLER), + @LogMessageDoc(level="ERROR", + message="Could not load default modules", + explanation="The controller failed to read the default " + + "module configuration", + recommendation=LogMessageDoc.CHECK_CONTROLLER) + }) + public IFloodlightModuleContext loadModulesFromConfig(String fName) + throws FloodlightModuleException { + Properties prop = new Properties(); + Collection<String> configMods; + + File f = new File(fName); + if (f.isFile()) { + logger.info("Loading modules from file {}", f.getPath()); + configMods = loadProperties(null, f, prop); + } else { + logger.info("Loading default modules"); + InputStream is = this.getClass().getClassLoader(). + getResourceAsStream(COMPILED_CONF_FILE); + configMods = loadProperties(is, null, prop); + } + + return loadModulesFromList(configMods, prop); + } + + private Collection<String> loadProperties(InputStream is, + File confFile, + Properties prop) { + try { + Properties fprop = new Properties(); + if (is != null) { + fprop.load(is); + } else { + fprop.load(new FileInputStream(confFile)); + } + prop.putAll(fprop); + } catch (Exception e) { + logger.error("Could not load module configuration file", e); + System.exit(1); + } + + Collection<String> configMods = new ArrayList<String>(); + String moduleList = prop.getProperty(FLOODLIGHT_MODULES_KEY); + if (moduleList != null) { + moduleList = moduleList.replaceAll("\\s", ""); + configMods.addAll(Arrays.asList(moduleList.split(","))); + prop.remove(FLOODLIGHT_MODULES_KEY); + } + + String confdStr = prop.getProperty(FLOODLIGHT_CONFD); + prop.remove(FLOODLIGHT_CONFD); + if (confdStr != null) { + File confd = new File(confdStr); + if (confd.exists() && confd.isDirectory()) { + File[] files = confd.listFiles(); + Arrays.sort(files); + for (File f : files) { + if (f.isFile() && + f.getName().matches(".*\\.properties$")) + configMods.addAll(loadProperties(null, f, prop)); + } + } + } + + return configMods; + } + + /** + * Loads modules (and their dependencies) specified in the list + * @param mList The array of fully qualified module names + * @param ignoreList The list of Floodlight services NOT to + * load modules for. Used for unit testing. + * @return The ModuleContext containing all the loaded modules + * @throws FloodlightModuleException + */ + protected IFloodlightModuleContext loadModulesFromList(Collection<String> configMods, Properties prop, + Collection<IFloodlightService> ignoreList) throws FloodlightModuleException { + logger.debug("Starting module loader"); + if (logger.isDebugEnabled() && ignoreList != null) + logger.debug("Not loading module services " + ignoreList.toString()); + + findAllModules(configMods); + + Collection<IFloodlightModule> moduleSet = new ArrayList<IFloodlightModule>(); + Map<Class<? extends IFloodlightService>, IFloodlightModule> moduleMap = + new HashMap<Class<? extends IFloodlightService>, + IFloodlightModule>(); + + Queue<String> moduleQ = new LinkedList<String>(); + // Add the explicitly configured modules to the q + moduleQ.addAll(configMods); + Set<String> modsVisited = new HashSet<String>(); + + while (!moduleQ.isEmpty()) { + String moduleName = moduleQ.remove(); + if (modsVisited.contains(moduleName)) + continue; + modsVisited.add(moduleName); + IFloodlightModule module = moduleNameMap.get(moduleName); + if (module == null) { + throw new FloodlightModuleException("Module " + + moduleName + " not found"); + } + // If the module provides a service that is in the + // services ignorelist don't load it. + if ((ignoreList != null) && (module.getModuleServices() != null)) { + for (IFloodlightService ifs : ignoreList) { + for (Class<?> intsIgnore : ifs.getClass().getInterfaces()) { + //System.out.println(intsIgnore.getName()); + // Check that the interface extends IFloodlightService + //if (intsIgnore.isAssignableFrom(IFloodlightService.class)) { + //System.out.println(module.getClass().getName()); + if (intsIgnore.isAssignableFrom(module.getClass())) { + // We now ignore loading this module. + logger.debug("Not loading module " + + module.getClass().getCanonicalName() + + " because interface " + + intsIgnore.getCanonicalName() + + " is in the ignore list."); + + continue; + } + //} + } + } + } + + // Add the module to be loaded + addModule(moduleMap, moduleSet, module); + // Add it's dep's to the queue + Collection<Class<? extends IFloodlightService>> deps = + module.getModuleDependencies(); + if (deps != null) { + for (Class<? extends IFloodlightService> c : deps) { + IFloodlightModule m = moduleMap.get(c); + if (m == null) { + Collection<IFloodlightModule> mods = serviceMap.get(c); + // Make sure only one module is loaded + if ((mods == null) || (mods.size() == 0)) { + throw new FloodlightModuleException("ERROR! Could not " + + "find an IFloodlightModule that provides service " + + c.toString()); + } else if (mods.size() == 1) { + IFloodlightModule mod = mods.iterator().next(); + if (!modsVisited.contains(mod.getClass().getCanonicalName())) + moduleQ.add(mod.getClass().getCanonicalName()); + } else { + boolean found = false; + for (IFloodlightModule moduleDep : mods) { + if (configMods.contains(moduleDep.getClass().getCanonicalName())) { + // Module will be loaded, we can continue + found = true; + break; + } + } + if (!found) { + String duplicateMods = ""; + for (IFloodlightModule mod : mods) { + duplicateMods += mod.getClass().getCanonicalName() + ", "; + } + throw new FloodlightModuleException("ERROR! Found more " + + "than one (" + mods.size() + ") IFloodlightModules that provides " + + "service " + c.toString() + + ". This service is required for " + moduleName + + ". Please specify one of the following modules in the config: " + + duplicateMods); + } + } + } + } + } + } + + floodlightModuleContext.addModules(moduleSet); + parseConfigParameters(prop); + initModules(moduleSet); + startupModules(moduleSet); + + return floodlightModuleContext; + } + + @Override + public IFloodlightModuleContext loadModulesFromList(Collection<String> configMods, Properties prop) + throws FloodlightModuleException { + return loadModulesFromList(configMods, prop, null); + } + + /** + * Add a module to the set of modules to load and register its services + * @param moduleMap the module map + * @param moduleSet the module set + * @param module the module to add + */ + protected void addModule(Map<Class<? extends IFloodlightService>, + IFloodlightModule> moduleMap, + Collection<IFloodlightModule> moduleSet, + IFloodlightModule module) { + if (!moduleSet.contains(module)) { + Collection<Class<? extends IFloodlightService>> servs = + moduleServiceMap.get(module); + if (servs != null) { + for (Class<? extends IFloodlightService> c : servs) + moduleMap.put(c, module); + } + moduleSet.add(module); + } + } + + /** + * Allocate service implementations and then init all the modules + * @param moduleSet The set of modules to call their init function on + * @throws FloodlightModuleException If a module can not properly be loaded + */ + protected void initModules(Collection<IFloodlightModule> moduleSet) + throws FloodlightModuleException { + for (IFloodlightModule module : moduleSet) { + if (initedSet.contains(module.getClass().getCanonicalName())) + continue; + + // Get the module's service instance(s) + Map<Class<? extends IFloodlightService>, + IFloodlightService> simpls = module.getServiceImpls(); + + // add its services to the context + if (simpls != null) { + for (Entry<Class<? extends IFloodlightService>, + IFloodlightService> s : simpls.entrySet()) { + if (logger.isDebugEnabled()) { + logger.debug("Setting " + s.getValue() + + " as provider for " + + s.getKey().getCanonicalName()); + } + if (floodlightModuleContext.getServiceImpl(s.getKey()) == null) { + floodlightModuleContext.addService(s.getKey(), + s.getValue()); + } else { + throw new FloodlightModuleException("Cannot set " + + s.getValue() + + " as the provider for " + + s.getKey().getCanonicalName() + + " because " + + floodlightModuleContext.getServiceImpl(s.getKey()) + + " already provides it"); + } + } + } + } + + for (IFloodlightModule module : moduleSet) { + if (initedSet.contains(module.getClass().getCanonicalName())) + continue; + initedSet.add(module.getClass().getCanonicalName()); + + // init the module + if (logger.isDebugEnabled()) { + logger.debug("Initializing " + + module.getClass().getCanonicalName()); + } + module.init(floodlightModuleContext); + } + } + + /** + * Call each loaded module's startup method + * @param moduleSet the module set to start up + * @throws FloodlightModuleException + */ + protected void startupModules(Collection<IFloodlightModule> moduleSet) + throws FloodlightModuleException { + for (IFloodlightModule m : moduleSet) { + if (startedSet.contains(m.getClass().getCanonicalName())) + continue; + startedSet.add(m.getClass().getCanonicalName()); + + if (logger.isDebugEnabled()) { + logger.debug("Starting " + m.getClass().getCanonicalName()); + } + m.startUp(floodlightModuleContext); + } + } + + /** + * Parses configuration parameters for each module + * @param prop The properties file to use + */ + @LogMessageDoc(level="WARN", + message="Module {module} not found or loaded. " + + "Not adding configuration option {key} = {value}", + explanation="Ignoring a configuration parameter for a " + + "module that is not loaded.") + protected void parseConfigParameters(Properties prop) { + if (prop == null) return; + + Enumeration<?> e = prop.propertyNames(); + while (e.hasMoreElements()) { + String key = (String) e.nextElement(); + // Ignore module list key + if (key.equals(FLOODLIGHT_MODULES_KEY)) { + continue; + } + + String configValue = null; + int lastPeriod = key.lastIndexOf("."); + String moduleName = key.substring(0, lastPeriod); + String configKey = key.substring(lastPeriod + 1); + // Check to see if it's overridden on the command line + String systemKey = System.getProperty(key); + if (systemKey != null) { + configValue = systemKey; + } else { + configValue = prop.getProperty(key); + } + + IFloodlightModule mod = moduleNameMap.get(moduleName); + if (mod == null) { + logger.warn("Module {} not found or loaded. " + + "Not adding configuration option {} = {}", + new Object[]{moduleName, configKey, configValue}); + } else { + floodlightModuleContext.addConfigParam(mod, configKey, configValue); + } + } + } +} diff --git a/src/main/java/net/floodlightcontroller/core/module/FloodlightModulePriority.java b/src/main/java/net/floodlightcontroller/core/module/FloodlightModulePriority.java deleted file mode 100644 index d2e561fbf435049f16fe80c885941c3e50deae40..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/module/FloodlightModulePriority.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2013, Big Switch Networks, Inc. - * - * 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 net.floodlightcontroller.core.module; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Set a module priority value so if there are multiple modules that provide - * a given service, and there is a unique module with maximum priority, - * we will use that module in preference to lower-priority modules rather - * than requiring users to manually specify a module to load. This makes it - * possible to define a default provider that uses the DEFAULT_PROVIDER priority - * while the normal modules omit the annotation or specify NORMAL priority. - * @author readams - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface FloodlightModulePriority { - public enum Priority { - MINIMUM(0), - TEST(10), - EXTRA_LOW(20), - LOW(30), - NORMAL(40), - DEFAULT_PROVIDER(50), - HIGH(60), - EXTRA_HIGH(70); - - private final int value; - - private Priority(int value) { - this.value = value; - } - - public int value() { - return value; - } - } - - public Priority value() default Priority.NORMAL; -} diff --git a/src/main/java/net/floodlightcontroller/core/module/FloodlightServices.java b/src/main/java/net/floodlightcontroller/core/module/FloodlightServices.java deleted file mode 100644 index c5d24b72313ee1234ca5beea1a7c9cafef229f38..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/module/FloodlightServices.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.floodlightcontroller.core.module; - -public @interface FloodlightServices { - -} diff --git a/src/main/java/net/floodlightcontroller/core/module/IApplicationService.java b/src/main/java/net/floodlightcontroller/core/module/IApplicationService.java new file mode 100644 index 0000000000000000000000000000000000000000..60a916930a06185f5a11199bd1cd839d9ebf8f2b --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/module/IApplicationService.java @@ -0,0 +1,5 @@ +package net.floodlightcontroller.core.module; + +public interface IApplicationService extends IFloodlightService { + +} diff --git a/src/main/java/net/floodlightcontroller/core/module/IFloodlightModule.java b/src/main/java/net/floodlightcontroller/core/module/IFloodlightModule.java new file mode 100644 index 0000000000000000000000000000000000000000..982e4be26a7e073dd700599a96a4393697088ed3 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/module/IFloodlightModule.java @@ -0,0 +1,92 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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 net.floodlightcontroller.core.module; + +import java.util.Collection; +import java.util.Map; + + +/** + * Defines an interface for loadable Floodlight modules. + * + * At a high level, these functions are called in the following order: + * <ol> + * <li> getServices() : what services does this module provide + * <li> getDependencies() : list the dependencies + * <li> init() : internal initializations (don't touch other modules) + * <li> startUp() : external initializations (<em>do</em> touch other modules) + * </ol> + * + * @author alexreimers + */ +public interface IFloodlightModule { + + /** + * Return the list of interfaces that this module implements. + * All interfaces must inherit IFloodlightService + * @return + */ + + public Collection<Class<? extends IFloodlightService>> getModuleServices(); + + /** + * Instantiate (as needed) and return objects that implement each + * of the services exported by this module. The map returned maps + * the implemented service to the object. The object could be the + * same object or different objects for different exported services. + * @return The map from service interface class to service implementation + */ + public Map<Class<? extends IFloodlightService>, + IFloodlightService> getServiceImpls(); + + /** + * Get a list of Modules that this module depends on. The module system + * will ensure that each these dependencies is resolved before the + * subsequent calls to init(). + * @return The Collection of IFloodlightServices that this module depends + * on. + */ + + public Collection<Class<? extends IFloodlightService>> getModuleDependencies(); + + /** + * This is a hook for each module to do its <em>internal</em> initialization, + * e.g., call setService(context.getService("Service")) + * + * All module dependencies are resolved when this is called, but not every module + * is initialized. + * + * @param context + * @throws FloodlightModuleException + */ + + void init(FloodlightModuleContext context) throws FloodlightModuleException; + + /** + * This is a hook for each module to do its <em>external</em> initializations, + * e.g., register for callbacks or query for state in other modules + * + * It is expected that this function will not block and that modules that want + * non-event driven CPU will spawn their own threads. + * + * @param context + * @throws FloodlightModuleException + */ + + void startUp(FloodlightModuleContext context) + throws FloodlightModuleException; +} diff --git a/src/main/java/net/floodlightcontroller/core/module/IFloodlightModuleContext.java b/src/main/java/net/floodlightcontroller/core/module/IFloodlightModuleContext.java new file mode 100644 index 0000000000000000000000000000000000000000..46dea092c2eaa20b180b2176ddd3babbb2d24b26 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/module/IFloodlightModuleContext.java @@ -0,0 +1,59 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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 net.floodlightcontroller.core.module; + +import java.util.Collection; +import java.util.Map; + + +public interface IFloodlightModuleContext { + /** + * Retrieves a casted version of a module from the registry. + * @param name The IFloodlightService object type + * @return The IFloodlightService + * @throws FloodlightModuleException If the module was not found + * or a ClassCastException was encountered. + */ + public <T extends IFloodlightService> T getServiceImpl(Class<T> service); + + /** + * Returns all loaded services + * @return A collection of service classes that have been loaded + */ + public Collection<Class<? extends IFloodlightService>> getAllServices(); + + /** + * Returns all loaded modules + * @return All Floodlight modules that are going to be loaded + */ + public Collection<IFloodlightModule> getAllModules(); + + /** + * Gets module specific configuration parameters. + * @param module The module to get the configuration parameters for + * @return A key, value map of the configuration options + */ + public Map<String, String> getConfigParams(IFloodlightModule module); + + /** + * Gets module specific configuration parameters. + * @param clazz The class of the module to get configuration parameters for + * @return A key, value map of the configuration options + */ + public Map<String, String> getConfigParams(Class<? extends + IFloodlightModule> clazz); +} diff --git a/src/main/java/net/floodlightcontroller/core/module/IFloodlightService.java b/src/main/java/net/floodlightcontroller/core/module/IFloodlightService.java new file mode 100644 index 0000000000000000000000000000000000000000..4a5f4ca653a3fb5e09b651e87d3f302d8506ce14 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/module/IFloodlightService.java @@ -0,0 +1,27 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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 net.floodlightcontroller.core.module; + +/** + * This is the base interface for any IFloodlightModule package that provides + * a service. + * @author alexreimers + * + */ +public abstract interface IFloodlightService { + // This space is intentionally left blank....don't touch it +} diff --git a/src/main/java/net/floodlightcontroller/core/module/IModuleService.java b/src/main/java/net/floodlightcontroller/core/module/IModuleService.java new file mode 100644 index 0000000000000000000000000000000000000000..440ad2de6240709ef9e8a068e4b557f6b1444d82 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/module/IModuleService.java @@ -0,0 +1,18 @@ +package net.floodlightcontroller.core.module; + +import java.util.Collection; +import java.util.Properties; + +public interface IModuleService extends IFloodlightService { + /** + * Loads modules (and their dependencies) specified in the list. + * @param configMods The collection of fully qualified module names to load. + * @param prop The list of properties that are configuration options. + * @return The ModuleContext containing all the loaded modules. + * @throws FloodlightModuleException + */ + public IFloodlightModuleContext + loadModulesFromList(Collection<String> configMods, + Properties prop) throws FloodlightModuleException; + +} diff --git a/src/main/java/net/floodlightcontroller/core/module/ModuleLoaderResource.java b/src/main/java/net/floodlightcontroller/core/module/ModuleLoaderResource.java new file mode 100644 index 0000000000000000000000000000000000000000..b4406117effee39794485dcedac4885fbea0a3ad --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/module/ModuleLoaderResource.java @@ -0,0 +1,120 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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 net.floodlightcontroller.core.module; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + + +import org.restlet.resource.Get; +import org.restlet.resource.ServerResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Returns list of modules loaded by Floodlight. + * @author Rob Sherwood + */ +public class ModuleLoaderResource extends ServerResource { + protected static Logger log = + LoggerFactory.getLogger(ModuleLoaderResource.class); + + /** + * Retrieves information about loaded modules. + * @return Information about loaded modules. + */ + @Get("json") + public Map<String, Object> retrieve() { + return retrieveInternal(false); + } + + /** + * Retrieves all modules and their dependencies available + * to Floodlight. + * @param loadedOnly Whether to return all modules available or only the ones loaded. + * @return Information about modules available or loaded. + */ + public Map<String, Object> retrieveInternal(boolean loadedOnly) { + Map<String, Object> model = new HashMap<String, Object>(); + + Set<String> loadedModules = new HashSet<String>(); + for (Object val : getContext().getAttributes().values()) { + if ((val instanceof IFloodlightModule) || (val instanceof IFloodlightService)) { + String serviceImpl = val.getClass().getCanonicalName(); + loadedModules.add(serviceImpl); + // log.debug("Tracking serviceImpl " + serviceImpl); + } + } + + for (String moduleName : + FloodlightModuleLoader.moduleNameMap.keySet() ) { + Map<String,Object> moduleInfo = new HashMap<String, Object>(); + + IFloodlightModule module = + FloodlightModuleLoader.moduleNameMap.get( + moduleName); + + Collection<Class<? extends IFloodlightService>> deps = + module.getModuleDependencies(); + if ( deps == null) + deps = new HashSet<Class<? extends IFloodlightService>>(); + Map<String,Object> depsMap = new HashMap<String, Object> (); + for (Class<? extends IFloodlightService> service : deps) { + Object serviceImpl = getContext().getAttributes().get(service.getCanonicalName()); + if (serviceImpl != null) + depsMap.put(service.getCanonicalName(), serviceImpl.getClass().getCanonicalName()); + else + depsMap.put(service.getCanonicalName(), "<unresolved>"); + + } + moduleInfo.put("depends", depsMap); + + Collection<Class<? extends IFloodlightService>> provides = + module.getModuleServices(); + if ( provides == null) + provides = new HashSet<Class<? extends IFloodlightService>>(); + Map<String,Object> providesMap = new HashMap<String,Object>(); + for (Class<? extends IFloodlightService> service : provides) { + providesMap.put(service.getCanonicalName(), module.getServiceImpls().get(service).getClass().getCanonicalName()); + } + moduleInfo.put("provides", providesMap); + + moduleInfo.put("loaded", false); // not loaded, by default + + // check if this module is loaded directly + if (loadedModules.contains(module.getClass().getCanonicalName())) { + moduleInfo.put("loaded", true); + } else { + // if not, then maybe one of the services it exports is loaded + for (Class<? extends IFloodlightService> service : provides) { + String modString = module.getServiceImpls().get(service).getClass().getCanonicalName(); + if (loadedModules.contains(modString)) + moduleInfo.put("loaded", true); + /* else + log.debug("ServiceImpl not loaded " + modString); */ + } + } + + if ((Boolean)moduleInfo.get("loaded")|| !loadedOnly ) + model.put(moduleName, moduleInfo); + } + return model; + } +} diff --git a/src/main/java/net/floodlightcontroller/core/module/Run.java b/src/main/java/net/floodlightcontroller/core/module/Run.java deleted file mode 100644 index 4bf8a2ed41d270282b5dbda582a8ac7c32ec448f..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/module/Run.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.floodlightcontroller.core.module; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Indicate the run() method of a floodlight module - * @author readams - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface Run { - /** declares this run method as the application main method. Will be called last and is not expected to - * return. It is a configuration error to have more than one module declaring a main method. - * @return - */ - boolean mainLoop() default false; -} diff --git a/src/main/java/net/floodlightcontroller/core/rest/AggregateStatistics.java b/src/main/java/net/floodlightcontroller/core/rest/AggregateStatistics.java deleted file mode 100644 index 3d0fe680e544ffa5a7701ad42f0037fe8de9037c..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/rest/AggregateStatistics.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.floodlightcontroller.core.rest; - -import org.projectfloodlight.openflow.protocol.OFAggregateStatsReply; -import org.projectfloodlight.openflow.types.U64; - -public class AggregateStatistics { - - private final U64 packetCount; - private final U64 byteCount; - private final long flowCount; - - private AggregateStatistics(U64 packetCount, U64 byteCount, long flowCount) { - this.packetCount = packetCount; - this.byteCount = byteCount; - this.flowCount = flowCount; - } - - public static AggregateStatistics of(U64 packetCount, U64 byteCount, - long flowCount) { - return new AggregateStatistics(packetCount, byteCount, flowCount); - } - - public static AggregateStatistics of(OFAggregateStatsReply statsReply) { - return new AggregateStatistics(statsReply.getPacketCount(), - statsReply.getByteCount(), statsReply.getFlowCount()); - } - - public U64 getPacketCount() { - return packetCount; - } - - public U64 getByteCount() { - return byteCount; - } - - public long getFlowCount() { - return flowCount; - } -} diff --git a/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java b/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java deleted file mode 100644 index 03a46f462b68300c61639052b03a7931bfcc4861..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java +++ /dev/null @@ -1,116 +0,0 @@ -package net.floodlightcontroller.core.rest; - -import java.net.SocketAddress; -import java.util.Collection; -import java.util.Date; -import java.util.Map; -import java.util.Set; - -import javax.annotation.Nonnull; - -import net.floodlightcontroller.core.IOFConnection; -import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler; -import org.projectfloodlight.openflow.protocol.OFCapabilities; -import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.types.DatapathId; - -import com.google.common.base.Preconditions; - -/** - * REST representation of an OF Switch. Stitches together data from different - * areas of the platform to provide a complete, centralized representation. - * @author Jason Parraga <Jason.Parraga@bigswitch.com> - * - */ -public class SwitchRepresentation { - - private final long buffers; - private final Set<OFCapabilities> capabilities; - private final Short tables; - private final SocketAddress inetAddress; - private final Collection<OFPortDesc> sortedPorts; - private final boolean isConnected; - private final Date connectedSince; - private final DatapathId dpid; - private final Map<Object, Object> attributes; - private final boolean isActive; - - private final Collection<IOFConnection> connections; - private final String handshakeState; - private final String quarantineReason; - - public SwitchRepresentation(@Nonnull IOFSwitch sw, @Nonnull OFSwitchHandshakeHandler handshakeHandler) { - Preconditions.checkNotNull(sw, "switch must not be null"); - Preconditions.checkNotNull(handshakeHandler, "handshakeHandler must not be null"); - - // IOFSwitch - this.buffers = sw.getBuffers(); - this.capabilities = sw.getCapabilities(); - this.tables = sw.getTables(); - this.inetAddress = sw.getInetAddress(); - this.sortedPorts = sw.getSortedPorts(); - this.isConnected = sw.isConnected(); - this.connectedSince = sw.getConnectedSince(); - this.dpid = sw.getId(); - this.attributes = sw.getAttributes(); - this.isActive = sw.isActive(); - - // OFSwitchHandshakeHandler - this.connections = handshakeHandler.getConnections(); - this.handshakeState = handshakeHandler.getState(); - this.quarantineReason = handshakeHandler.getQuarantineReason(); - } - - public long getBuffers() { - return this.buffers; - } - - public Short getTables() { - return this.tables; - } - - public Set<OFCapabilities> getCapabilities() { - return this.capabilities; - } - - public SocketAddress getInetAddress() { - return this.inetAddress; - } - - public Collection<OFPortDesc> getSortedPorts() { - return this.sortedPorts; - } - - public boolean isConnected() { - return this.isConnected; - } - - public Date getConnectedSince() { - return this.connectedSince; - } - - public DatapathId getDpid() { - return this.dpid; - } - - public Map<Object, Object> getAttributes() { - return this.attributes; - } - - public boolean isActive() { - return this.isActive; - } - - public Collection<IOFConnection> getConnections() { - return this.connections; - } - - public String getHandshakeState() { - return this.handshakeState; - } - - public String getQuarantineReason() { - return this.quarantineReason; - } -} diff --git a/src/main/java/net/floodlightcontroller/core/rest/TableStatistics.java b/src/main/java/net/floodlightcontroller/core/rest/TableStatistics.java deleted file mode 100644 index 480ff2004e0eae6f133fb79a94adc318b6b57cf4..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/rest/TableStatistics.java +++ /dev/null @@ -1,45 +0,0 @@ -package net.floodlightcontroller.core.rest; - -import org.projectfloodlight.openflow.protocol.OFTableStatsEntry; -import org.projectfloodlight.openflow.types.TableId; -import org.projectfloodlight.openflow.types.U64; - -public class TableStatistics { - - private final TableId tableId; - private final long activeCount; - private final U64 lookupCount; - private final U64 matchedCount; - - private TableStatistics(TableId tableId, long activeCount, U64 lookupCount, U64 matchedCount) { - this.tableId = tableId; - this.activeCount = activeCount; - this.lookupCount = lookupCount; - this.matchedCount = matchedCount; - } - - public static TableStatistics of(TableId tableId, long activeCount, U64 lookupCount, U64 matchedCount) { - return new TableStatistics(tableId, activeCount, lookupCount, matchedCount); - } - - public static TableStatistics of(OFTableStatsEntry entry) { - return new TableStatistics(entry.getTableId(), - entry.getActiveCount(), entry.getLookupCount(), entry.getMatchedCount()); - } - - public TableId getTableId() { - return tableId; - } - - public long getActiveCount() { - return activeCount; - } - - public U64 getLookupCount() { - return lookupCount; - } - - public U64 getMatchedCount() { - return matchedCount; - } -} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/types/SwitchMessagePair.java b/src/main/java/net/floodlightcontroller/core/types/SwitchMessagePair.java index 2f40532961b5912b2981f4ce5fbd75d668b1beb8..0e91bc9b488d6009eaaac15641aba1b0cde5a998 100644 --- a/src/main/java/net/floodlightcontroller/core/types/SwitchMessagePair.java +++ b/src/main/java/net/floodlightcontroller/core/types/SwitchMessagePair.java @@ -17,7 +17,7 @@ package net.floodlightcontroller.core.types; -import org.projectfloodlight.openflow.protocol.OFMessage; +import org.openflow.protocol.OFMessage; import net.floodlightcontroller.core.IOFSwitch; diff --git a/src/main/java/net/floodlightcontroller/core/util/URIUtil.java b/src/main/java/net/floodlightcontroller/core/util/URIUtil.java deleted file mode 100644 index 01a44ccf0cda6782ebbcaef9c5874361c4c272b0..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/util/URIUtil.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.floodlightcontroller.core.util; - -import java.net.URI; - -public class URIUtil { - - public static URI createURI(String hostname, int port) { - return URI.create("tcp://" + hostname + ":" + port); - } -} diff --git a/src/main/java/net/floodlightcontroller/core/web/AllSwitchStatisticsResource.java b/src/main/java/net/floodlightcontroller/core/web/AllSwitchStatisticsResource.java index cea740239ce1bfa20ad0d705135d507beaa188bb..c957e6356ba9833bb2b40e331881cccd528b40a1 100644 --- a/src/main/java/net/floodlightcontroller/core/web/AllSwitchStatisticsResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/AllSwitchStatisticsResource.java @@ -25,12 +25,10 @@ import java.util.Map; import java.util.Set; import net.floodlightcontroller.core.IFloodlightProviderService; - -import org.projectfloodlight.openflow.protocol.OFFeaturesReply; -import org.projectfloodlight.openflow.protocol.OFStatsReply; -import org.projectfloodlight.openflow.protocol.OFStatsType; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.util.HexString; +import org.openflow.protocol.OFFeaturesReply; +import org.openflow.protocol.statistics.OFStatistics; +import org.openflow.protocol.statistics.OFStatisticsType; +import org.openflow.util.HexString; import org.restlet.resource.Get; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,26 +50,26 @@ public class AllSwitchStatisticsResource extends SwitchResourceBase { public Map<String, Object> retrieveInternal(String statType) { HashMap<String, Object> model = new HashMap<String, Object>(); - OFStatsType type = null; + OFStatisticsType type = null; REQUESTTYPE rType = null; if (statType.equals("port")) { - type = OFStatsType.PORT; + type = OFStatisticsType.PORT; rType = REQUESTTYPE.OFSTATS; } else if (statType.equals("queue")) { - type = OFStatsType.QUEUE; + type = OFStatisticsType.QUEUE; rType = REQUESTTYPE.OFSTATS; } else if (statType.equals("flow")) { - type = OFStatsType.FLOW; + type = OFStatisticsType.FLOW; rType = REQUESTTYPE.OFSTATS; } else if (statType.equals("aggregate")) { - type = OFStatsType.AGGREGATE; + type = OFStatisticsType.AGGREGATE; rType = REQUESTTYPE.OFSTATS; } else if (statType.equals("desc")) { - type = OFStatsType.DESC; + type = OFStatisticsType.DESC; rType = REQUESTTYPE.OFSTATS; } else if (statType.equals("table")) { - type = OFStatsType.TABLE; + type = OFStatisticsType.TABLE; rType = REQUESTTYPE.OFSTATS; } else if (statType.equals("features")) { rType = REQUESTTYPE.OFFEATURES; @@ -82,11 +80,11 @@ public class AllSwitchStatisticsResource extends SwitchResourceBase { IFloodlightProviderService floodlightProvider = (IFloodlightProviderService)getContext().getAttributes(). get(IFloodlightProviderService.class.getCanonicalName()); - Set<DatapathId> switchDpids = floodlightProvider.getAllSwitchDpids(); + Set<Long> switchDpids = floodlightProvider.getAllSwitchDpids(); List<GetConcurrentStatsThread> activeThreads = new ArrayList<GetConcurrentStatsThread>(switchDpids.size()); List<GetConcurrentStatsThread> pendingRemovalThreads = new ArrayList<GetConcurrentStatsThread>(); GetConcurrentStatsThread t; - for (DatapathId l : switchDpids) { + for (Long l : switchDpids) { t = new GetConcurrentStatsThread(l, rType, type); activeThreads.add(t); t.start(); @@ -100,9 +98,9 @@ public class AllSwitchStatisticsResource extends SwitchResourceBase { for (GetConcurrentStatsThread curThread : activeThreads) { if (curThread.getState() == State.TERMINATED) { if (rType == REQUESTTYPE.OFSTATS) { - model.put(HexString.toHexString(curThread.getSwitchId().getLong()), curThread.getStatisticsReply()); + model.put(HexString.toHexString(curThread.getSwitchId()), curThread.getStatisticsReply()); } else if (rType == REQUESTTYPE.OFFEATURES) { - model.put(HexString.toHexString(curThread.getSwitchId().getLong()), curThread.getFeaturesReply()); + model.put(HexString.toHexString(curThread.getSwitchId()), curThread.getFeaturesReply()); } pendingRemovalThreads.add(curThread); } @@ -132,13 +130,13 @@ public class AllSwitchStatisticsResource extends SwitchResourceBase { } protected class GetConcurrentStatsThread extends Thread { - private List<OFStatsReply> switchReply; - private DatapathId switchId; - private OFStatsType statType; + private List<OFStatistics> switchReply; + private long switchId; + private OFStatisticsType statType; private REQUESTTYPE requestType; private OFFeaturesReply featuresReply; - public GetConcurrentStatsThread(DatapathId switchId, REQUESTTYPE requestType, OFStatsType statType) { + public GetConcurrentStatsThread(long switchId, REQUESTTYPE requestType, OFStatisticsType statType) { this.switchId = switchId; this.requestType = requestType; this.statType = statType; @@ -146,7 +144,7 @@ public class AllSwitchStatisticsResource extends SwitchResourceBase { this.featuresReply = null; } - public List<OFStatsReply> getStatisticsReply() { + public List<OFStatistics> getStatisticsReply() { return switchReply; } @@ -154,7 +152,7 @@ public class AllSwitchStatisticsResource extends SwitchResourceBase { return featuresReply; } - public DatapathId getSwitchId() { + public long getSwitchId() { return switchId; } diff --git a/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java b/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java index 457cc91c20fc15d854abeb148eccc27daef1c2d1..eeaebe22c38aafb04834f2bac82e51be991e21be 100644 --- a/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java @@ -23,16 +23,14 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Collection; -import java.util.Set; import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.core.ImmutablePort; import net.floodlightcontroller.util.FilterIterator; -import org.projectfloodlight.openflow.protocol.OFCapabilities; -import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.protocol.OFActionType; +import org.openflow.protocol.OFPhysicalPort; +import org.openflow.util.HexString; import org.restlet.data.Form; import org.restlet.data.Status; import org.restlet.resource.Get; @@ -54,7 +52,7 @@ public class ControllerSwitchesResource extends ServerResource { this.sw = sw; } - public Set<OFActionType> getActions() { + public int getActions() { return sw.getActions(); } @@ -85,11 +83,11 @@ public class ControllerSwitchesResource extends ServerResource { return rv; } - public long getBuffers() { + public int getBuffers() { return sw.getBuffers(); } - public Set<OFCapabilities> getCapabilities() { + public int getCapabilities() { return sw.getCapabilities(); } @@ -116,8 +114,8 @@ public class ControllerSwitchesResource extends ServerResource { return addr.toString(); } - public Collection<OFPortDesc> getPorts() { - return sw.getPorts(); + public Collection<OFPhysicalPort> getPorts() { + return ImmutablePort.ofPhysicalPortListOf(sw.getPorts()); } } @@ -160,13 +158,13 @@ public class ControllerSwitchesResource extends ServerResource { (IFloodlightProviderService)getContext().getAttributes(). get(IFloodlightProviderService.class.getCanonicalName()); - DatapathId switchDPID = null; + Long switchDPID = null; Form form = getQuery(); String dpid = form.getFirstValue("dpid", true); if (dpid != null) { try { - switchDPID = DatapathId.of(dpid); + switchDPID = HexString.toLong(dpid); } catch (Exception e) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST, DPID_ERROR); return null; diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchCounterCategoriesResource.java b/src/main/java/net/floodlightcontroller/core/web/SwitchCounterCategoriesResource.java index f8f9ff0e643e369cd3e342ddd933a302b4340cae..34aa2eaa763f2953fa6b03ad95a82c25683add11 100644 --- a/src/main/java/net/floodlightcontroller/core/web/SwitchCounterCategoriesResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/SwitchCounterCategoriesResource.java @@ -23,7 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.projectfloodlight.openflow.types.DatapathId; +import org.openflow.util.HexString; import org.restlet.resource.Get; import net.floodlightcontroller.core.IFloodlightProviderService; @@ -47,8 +47,8 @@ public class SwitchCounterCategoriesResource extends CounterResourceBase { String layer = (String) getRequestAttributes().get("layer"); if (switchID.equalsIgnoreCase("all")) { - for (DatapathId dpid : floodlightProvider.getAllSwitchDpids()) { - switchID = dpid.toString(); + for (Long dpid : floodlightProvider.getAllSwitchDpids()) { + switchID = HexString.toHexString(dpid); getOneSwitchCounterCategoriesJson(model, switchID, counterName, layer); } diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchCounterResource.java b/src/main/java/net/floodlightcontroller/core/web/SwitchCounterResource.java index f078dd8c198687537a1a1365026b40400ab7bc95..34755ea083536a430652842a231fd22944e27552 100644 --- a/src/main/java/net/floodlightcontroller/core/web/SwitchCounterResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/SwitchCounterResource.java @@ -22,7 +22,7 @@ import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; -import org.projectfloodlight.openflow.types.DatapathId; +import org.openflow.util.HexString; import org.restlet.resource.Get; import net.floodlightcontroller.core.IFloodlightProviderService; @@ -46,8 +46,8 @@ public class SwitchCounterResource extends CounterResourceBase { if (switchID.equalsIgnoreCase("all")) { getOneSwitchCounterJson(model, ICounterStoreService.CONTROLLER_NAME, counterName); - for (DatapathId dpid : floodlightProvider.getAllSwitchDpids()) { - switchID = dpid.toString(); + for (Long dpid : floodlightProvider.getAllSwitchDpids()) { + switchID = HexString.toHexString(dpid); getOneSwitchCounterJson(model, switchID, counterName); } diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchResourceBase.java b/src/main/java/net/floodlightcontroller/core/web/SwitchResourceBase.java index aa48eb3c1c63deb2f603f0668ffe5edc1999dd95..6b4a46318fb7dbac07fd628ed23fcc44b8fb146a 100644 --- a/src/main/java/net/floodlightcontroller/core/web/SwitchResourceBase.java +++ b/src/main/java/net/floodlightcontroller/core/web/SwitchResourceBase.java @@ -26,23 +26,17 @@ import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.annotations.LogMessageDoc; -import org.projectfloodlight.openflow.protocol.OFFactories; -import org.projectfloodlight.openflow.protocol.OFFactory; -import org.projectfloodlight.openflow.protocol.OFFeaturesReply; -import org.projectfloodlight.openflow.protocol.OFFlowStatsReply; -import org.projectfloodlight.openflow.protocol.OFMatch; -import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.protocol.OFAggregateStatsRequest; -import org.projectfloodlight.openflow.protocol.OFAggregateStatsReply; -import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest; -import org.projectfloodlight.openflow.protocol.OFPortStatsRequest; -import org.projectfloodlight.openflow.protocol.OFQueueStatsRequest; -import org.projectfloodlight.openflow.protocol.OFStatsType; -import org.projectfloodlight.openflow.protocol.OFVersion; -import org.projectfloodlight.openflow.util.HexString; -import org.projectfloodlight.openflow.protocol.OFStatsReply; -import org.projectfloodlight.openflow.protocol.OFStatsRequest; -import org.projectfloodlight.openflow.types.DatapathId; +import org.openflow.protocol.OFFeaturesReply; +import org.openflow.protocol.OFMatch; +import org.openflow.protocol.OFPort; +import org.openflow.protocol.OFStatisticsRequest; +import org.openflow.protocol.statistics.OFAggregateStatisticsRequest; +import org.openflow.protocol.statistics.OFFlowStatisticsRequest; +import org.openflow.protocol.statistics.OFPortStatisticsRequest; +import org.openflow.protocol.statistics.OFQueueStatisticsRequest; +import org.openflow.protocol.statistics.OFStatistics; +import org.openflow.protocol.statistics.OFStatisticsType; +import org.openflow.util.HexString; import org.restlet.resource.ResourceException; import org.restlet.resource.ServerResource; import org.slf4j.Logger; @@ -73,34 +67,21 @@ public class SwitchResourceBase extends ServerResource { "from the switch", recommendation=LogMessageDoc.CHECK_SWITCH + " " + LogMessageDoc.GENERIC_ACTION) - protected List<OFStatsReply> getSwitchStatistics(DatapathId switchId, - OFStatsType statType) { + protected List<OFStatistics> getSwitchStatistics(long switchId, + OFStatisticsType statType) { IFloodlightProviderService floodlightProvider = (IFloodlightProviderService)getContext().getAttributes(). get(IFloodlightProviderService.class.getCanonicalName()); IOFSwitch sw = floodlightProvider.getSwitch(switchId); - - OFFactory factory = OFFactories.getFactory(OFVersion.OF_13); - OFFlowStatsRequest flowStatsRequest = factory.buildFlowStatsRequest() - .build(); - - Future<List<OFFlowStatsReply>> future = sw.queryStatistics(flowStatsRequest); - return future.get(); - - - sw.get - - Future<List<OFStatsReply>> future; - List<OFStatsReply> values = null; - if (sw != null) - { - OFFactory - OFStatsRequest<> req = new OFFactory. - req.cre(statType); + Future<List<OFStatistics>> future; + List<OFStatistics> values = null; + if (sw != null) { + OFStatisticsRequest req = new OFStatisticsRequest(); + req.setStatisticType(statType); int requestLength = req.getLengthU(); - if (statType == OFStatsType.FLOW) { - OFStatsRequest specificReq = new OFFlowStatisticsRequest(); + if (statType == OFStatisticsType.FLOW) { + OFFlowStatisticsRequest specificReq = new OFFlowStatisticsRequest(); OFMatch match = new OFMatch(); match.setWildcards(0xffffffff); specificReq.setMatch(match); diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java b/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java index d7b4c01290368ce9b89b5a36c05c2aa236219926..e6d66e2c24db29bad055be5d9a0132778e7ffe87 100644 --- a/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java @@ -18,7 +18,7 @@ package net.floodlightcontroller.core.web; import java.util.HashMap; -import org.projectfloodlight.openflow.types.DatapathId; +import org.openflow.util.HexString; import org.restlet.resource.ServerResource; import net.floodlightcontroller.core.IFloodlightProviderService; @@ -53,7 +53,7 @@ public class SwitchRoleResource extends ServerResource { return model; } - DatapathId dpid = DatapathId.of(switchId); + Long dpid = HexString.toLong(switchId); IOFSwitch sw = floodlightProvider.getSwitch(dpid); if (sw == null) return null; diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchStatisticsResource.java b/src/main/java/net/floodlightcontroller/core/web/SwitchStatisticsResource.java index a70d120ae59ff618d8ae57a3b59601cb2e66bbec..57771f718bfa51295220deb2a3d9e0e88c4223e3 100644 --- a/src/main/java/net/floodlightcontroller/core/web/SwitchStatisticsResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/SwitchStatisticsResource.java @@ -20,8 +20,7 @@ package net.floodlightcontroller.core.web; import java.util.HashMap; import java.util.Map; -import org.projectfloodlight.openflow.protocol.OFStatsType; -import org.projectfloodlight.openflow.types.DatapathId; +import org.openflow.protocol.statistics.OFStatisticsType; import org.restlet.resource.Get; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,17 +42,17 @@ public class SwitchStatisticsResource extends SwitchResourceBase { String statType = (String) getRequestAttributes().get("statType"); if (statType.equals("port")) { - values = getSwitchStatistics(DatapathId.of(switchId), OFStatsType.PORT); + values = getSwitchStatistics(switchId, OFStatisticsType.PORT); } else if (statType.equals("queue")) { - values = getSwitchStatistics(DatapathId.of(switchId), OFStatsType.QUEUE); + values = getSwitchStatistics(switchId, OFStatisticsType.QUEUE); } else if (statType.equals("flow")) { - values = getSwitchStatistics(DatapathId.of(switchId), OFStatsType.FLOW); + values = getSwitchStatistics(switchId, OFStatisticsType.FLOW); } else if (statType.equals("aggregate")) { - values = getSwitchStatistics(DatapathId.of(switchId), OFStatsType.AGGREGATE); + values = getSwitchStatistics(switchId, OFStatisticsType.AGGREGATE); } else if (statType.equals("desc")) { - values = getSwitchStatistics(DatapathId.of(switchId), OFStatsType.DESC); + values = getSwitchStatistics(switchId, OFStatisticsType.DESC); } else if (statType.equals("table")) { - values = getSwitchStatistics(DatapathId.of(switchId), OFStatsType.TABLE); + values = getSwitchStatistics(switchId, OFStatisticsType.TABLE); } else if (statType.equals("features")) { values = getSwitchFeaturesReply(switchId); } diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/ByteArrayMACSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/ByteArrayMACSerializer.java index f5f3aea8c93f9a5769ad3eb8aa536755cf83e802..ce4d3ba3c51480ad3ab3b488c398727efb6d85b4 100644 --- a/src/main/java/net/floodlightcontroller/core/web/serializers/ByteArrayMACSerializer.java +++ b/src/main/java/net/floodlightcontroller/core/web/serializers/ByteArrayMACSerializer.java @@ -23,7 +23,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; -import org.projectfloodlight.openflow.util.HexString; +import org.openflow.util.HexString; /** * Serialize a MAC as colon-separated hexadecimal diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/DPIDSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/DPIDSerializer.java index d07a254680074dc53bcd4095bf7dca98b39253e0..6df067ead245a209b12ce20347c237913a8f7b29 100644 --- a/src/main/java/net/floodlightcontroller/core/web/serializers/DPIDSerializer.java +++ b/src/main/java/net/floodlightcontroller/core/web/serializers/DPIDSerializer.java @@ -23,7 +23,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; -import org.projectfloodlight.openflow.util.HexString; +import org.openflow.util.HexString; /** * Serialize a DPID as colon-separated hexadecimal diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/MACSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/MACSerializer.java index c7ea09b8364fa9b6e740da82b2a3f55ec6458fb8..c797681edf2e8779365293188e89f51b9b4c30a1 100644 --- a/src/main/java/net/floodlightcontroller/core/web/serializers/MACSerializer.java +++ b/src/main/java/net/floodlightcontroller/core/web/serializers/MACSerializer.java @@ -23,7 +23,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; -import org.projectfloodlight.openflow.util.HexString; +import org.openflow.util.HexString; /** * Serialize a MAC as colon-separated hexadecimal diff --git a/src/main/java/net/floodlightcontroller/counter/CounterStore.java b/src/main/java/net/floodlightcontroller/counter/CounterStore.java index d3067e60d5a75ec31e3b5ae911a3f5c8633e6137..bfe3b963f82b61bc0529005e4edd5b1f0b4d5164 100644 --- a/src/main/java/net/floodlightcontroller/counter/CounterStore.java +++ b/src/main/java/net/floodlightcontroller/counter/CounterStore.java @@ -24,7 +24,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; - import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.FloodlightModuleException; @@ -34,10 +33,8 @@ import net.floodlightcontroller.counter.CounterValue.CounterType; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPv4; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPacketIn; -import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.types.DatapathId; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPacketIn; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,12 +69,12 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { } protected class CounterKeyTuple { - OFType msgType; - DatapathId dpid; + byte msgType; + long dpid; short l3type; byte l4type; - public CounterKeyTuple(OFType msgType, DatapathId dpid, short l3type, byte l4type){ + public CounterKeyTuple(byte msgType, long dpid, short l3type, byte l4type){ this.msgType = msgType; this.dpid = dpid; this.l3type = l3type; @@ -102,8 +99,8 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { public int hashCode() { final int prime = 283; int result = 1; - result = prime * result + msgType.hashCode(); - result = prime * result + (int) (dpid.getLong() ^ (dpid.getLong() >>> 32)); + result = prime * result + msgType; + result = prime * result + (int) (dpid ^ (dpid >>> 32)); result = prime * result + l3type; result = prime * result + l4type; return result; @@ -158,7 +155,7 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { @Override public void updatePacketInCountersLocal(IOFSwitch sw, OFMessage m, Ethernet eth) { - if (((OFPacketIn)m).getData().length <= 0) { + if (((OFPacketIn)m).getPacketData().length <= 0) { return; } CounterKeyTuple countersKey = this.getCountersKey(sw, m, eth); @@ -294,7 +291,7 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { //******************************* protected CounterKeyTuple getCountersKey(IOFSwitch sw, OFMessage m, Ethernet eth) { - OFType mtype = m.getType(); + byte mtype = m.getType().getTypeValue(); short l3type = 0; byte l4type = 0; @@ -325,7 +322,7 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { int l3type = eth.getEtherType() & 0xffff; String switchIdHex = sw.getStringId(); String etherType = String.format("%04x", eth.getEtherType()); - String packetName = m.getType().getClass().getName(); + String packetName = m.getType().toClass().getName(); packetName = packetName.substring(packetName.lastIndexOf('.')+1); // L2 Type @@ -473,7 +470,7 @@ public class CounterStore implements IFloodlightModule, ICounterStoreService { /* String values for names */ String switchIdHex = sw.getStringId(); - String packetName = m.getType().getClass().getName(); + String packetName = m.getType().toClass().getName(); packetName = packetName.substring(packetName.lastIndexOf('.')+1); String controllerFMCounterName = diff --git a/src/main/java/net/floodlightcontroller/counter/ICounterStoreService.java b/src/main/java/net/floodlightcontroller/counter/ICounterStoreService.java index 79bf4548d6e6a6fc677eb3ebd5da3c2918808e55..c0cbd55916086103bb4df820ca1b33974ed3dcca 100644 --- a/src/main/java/net/floodlightcontroller/counter/ICounterStoreService.java +++ b/src/main/java/net/floodlightcontroller/counter/ICounterStoreService.java @@ -19,7 +19,7 @@ package net.floodlightcontroller.counter; import java.util.List; import java.util.Map; -import org.projectfloodlight.openflow.protocol.OFMessage; +import org.openflow.protocol.OFMessage; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.module.IFloodlightService; diff --git a/src/main/java/net/floodlightcontroller/counter/NullCounterStore.java b/src/main/java/net/floodlightcontroller/counter/NullCounterStore.java index eff67dcacfa1a85b48c8fdaf820250046310912e..bdcc690b349c4497d8ca7b645b23a03d3af2ce85 100644 --- a/src/main/java/net/floodlightcontroller/counter/NullCounterStore.java +++ b/src/main/java/net/floodlightcontroller/counter/NullCounterStore.java @@ -23,7 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.projectfloodlight.openflow.protocol.OFMessage; +import org.openflow.protocol.OFMessage; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.module.FloodlightModuleContext; diff --git a/src/main/java/net/floodlightcontroller/debugcounter/CounterNode.java b/src/main/java/net/floodlightcontroller/debugcounter/CounterNode.java deleted file mode 100644 index 8da964aa2d4c203801de60af38a968aacd52ae30..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/debugcounter/CounterNode.java +++ /dev/null @@ -1,338 +0,0 @@ -package net.floodlightcontroller.debugcounter; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.TreeMap; -import java.util.regex.Pattern; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; - -/** - * A node in the counter hierarchy tree. We use a single tree and "merge" the - * counter modules into this single tree. - * - * <li> Adding modules or counters to the tree must happen at the root of the tree. - * <li> The root and the first level of the tree (root and module) are a shim - * layers that con't have an actual counter value. We represent this with a - * null counter. - * - * @author gregor - */ -class CounterNode implements Iterable<DebugCounterImpl> { - private static final String QUOTED_SEP = Pattern.quote("/"); - - /** path/hierarchy of this counter without leading /. An empty string - * represents the root. A string without a / is a module name - */ - private final String hierarchy; - /** - * The path/hierarchy elements split into a list - */ - private final List<String> hierarchyElements; - /** - * The actual counter instance for this node. Can be null for - * root level and module level. - */ - private final DebugCounterImpl counter; - private final TreeMap<String,CounterNode> children = new TreeMap<>(); - - /** - * convert module name and counter hierarchy into list of - * hierarchy elements - * @param moduleName - * @param counterHierarchy - * @return - */ - static List<String> - getHierarchyElements(String moduleName, String counterHierarchy) { - DebugCounterServiceImpl.verifyModuleNameSanity(moduleName); - List<String> ret = new ArrayList<>(); - ret.add(moduleName); - if (counterHierarchy == null || counterHierarchy.isEmpty()) { - return ret; - } - for (String element: counterHierarchy.split(QUOTED_SEP)) { - ret.add(element); - } - return ret; - } - - private CounterNode(List<String> hierarchyElements, - DebugCounterImpl counter) { - super(); - this.hierarchyElements = ImmutableList.copyOf(hierarchyElements); - this.hierarchy = Joiner.on("/").join(hierarchyElements); - this.counter = counter; - } - - /** - * Create a new counter hierarchy tree and return the root - * @return - */ - public static CounterNode newTree() { - return new CounterNode(ImmutableList.<String>of(), null); - } - - /** verify that this node is the root */ - private void verifyIsRoot() { - if (hierarchyElements.size() != 0) { - throw new IllegalStateException("This not is not the root. Can " - + "only call addCounter() on the root node. Current node: " - + hierarchy); - } - } - - - - /** - * return the full hierarchy as string, including module name - */ - @Nonnull - String getHierarchy() { - return hierarchy; - } - - /** - * @return the list of hierarchy elements for this node - */ - @Nonnull - List<String> getHierarchyElements() { - return hierarchyElements; - } - - /** - * @return this node's counters - */ - @Nullable - DebugCounterImpl getCounter() { - return counter; - } - - /** - * Reset this counter all counter below it in the hierarchy - */ - void resetHierarchy() { - for (DebugCounterImpl cur: this) { - cur.reset(); - } - } - - /** - * Return an Iterable over all DebugCounterImpls at and below this - * node. Note we return an Iterable<DebugCounterImpls> not - * Iterable<IDebugCounter> on purpose. - * @return - */ - Iterable<DebugCounterImpl> getCountersInHierarchy() { - return this; - } - - - /** - * Lookup the CounterNode identified by the hieraryElements if it exists. - * Returns null if no such CounterNode is found. Must only be called on - * the root of the tree. - * @param hierarchyElements - * @return - */ - CounterNode lookup(List<String> hierarchyElements) { - CounterNode cur = this; - for (String element: hierarchyElements) { - cur = cur.children.get(element); - if (cur == null) { - break; - } - } - return cur; - } - - /** - * Add the given moduleName to the tree. Can only be called on the root. - * If the module already exists, the all counters of the module will - * be reset. - * @param moduleName - * @return true if the module was newly added, false if the module already - * existed - */ - boolean addModule(@Nonnull String moduleName) { - verifyIsRoot(); - if (children.containsKey(moduleName)) { - children.get(moduleName).resetHierarchy(); - return false; - } else { - CounterNode newNode = - new CounterNode(ImmutableList.of(moduleName), null); - children.put(moduleName, newNode); - return true; - } - } - - /** - * Add the given Counter to the hierarchy. If the counterHierarcy already - * exists, reset the hierarchy - * @param counter - * @return null if the counterHierarchy is newly registered, otherwise - * returns the already registered DebugCounterImpl instance - * @throws IllegalArgumentException if the parent of the counter does not - * yet exist - */ - @Nullable - DebugCounterImpl addCounter(@Nonnull DebugCounterImpl counter) { - verifyIsRoot(); - ArrayList<String> path = new ArrayList<>(); - path.add(counter.getModuleName()); - for (String element: counter.getCounterHierarchy().split(QUOTED_SEP)) { - path.add(element); - } - String newCounterName = path.get(path.size()-1); - - CounterNode parent = lookup(path.subList(0, path.size()-1)); - if (parent == null) { - throw new IllegalArgumentException("Missing hierarchy level for " - + "counter: " + counter.getModuleName() + " " - + counter.getCounterHierarchy()); - } - if (parent.children.containsKey(newCounterName)) { - CounterNode old = parent.children.get(newCounterName); - // FIXME: we should check that old and new has the same - // description and meta-data, otherwise we should probably thrown - // and exception and refuse the operation. - old.resetHierarchy(); - return old.counter; - } else { - CounterNode newNode = new CounterNode(path, counter); - parent.children.put(newCounterName, newNode); - return counter; - } - } - - - /** - * Iterator over the counters in the counter hierarchy. - * Iteration order is a pre-order tree walk. Children of a node are - * visited in sorted order. - * @author gregor - */ - private final static class CounterIterator implements Iterator<DebugCounterImpl> { - // NOTE: since some counters - ArrayDeque<CounterNode> stack = new ArrayDeque<>(); - CounterNode curNode = null; - private CounterIterator(CounterNode root) { - stack.push(root); - gotoNextNode(); - } - - private void gotoNextNode() { - while (true) { - curNode = null; - if (stack.isEmpty()) { - break; - } - curNode = stack.pop(); - for (CounterNode child: curNode.children.descendingMap().values()) { - stack.push(child); - } - if (curNode.counter != null) { - break; - } - } - } - - @Override - public boolean hasNext() { - return curNode != null; - } - - @Override - public DebugCounterImpl next() { - if (curNode == null) { - throw new NoSuchElementException(); - } - DebugCounterImpl ret = curNode.counter; - gotoNextNode(); - return ret; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } - - @Override - public Iterator<DebugCounterImpl> iterator() { - return new CounterIterator(this); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((children == null) ? 0 : children.hashCode()); - result = prime * result - + ((counter == null) ? 0 : counter.hashCode()); - result = prime * result - + ((hierarchy == null) ? 0 : hierarchy.hashCode()); - result = prime - * result - + ((hierarchyElements == null) ? 0 - : hierarchyElements.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - CounterNode other = (CounterNode) obj; - if (children == null) { - if (other.children != null) return false; - } else if (!children.equals(other.children)) return false; - if (counter == null) { - if (other.counter != null) return false; - } else if (!counter.equals(other.counter)) return false; - if (hierarchy == null) { - if (other.hierarchy != null) return false; - } else if (!hierarchy.equals(other.hierarchy)) return false; - if (hierarchyElements == null) { - if (other.hierarchyElements != null) return false; - } else if (!hierarchyElements.equals(other.hierarchyElements)) - return false; - return true; - } - - @Override - public String toString() { - return toString(0); - } - - public String toString(int indent) { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < indent; i++) { - builder.append(" "); - } - builder.append("hierarchy="); - builder.append(hierarchy); - builder.append(", counter="); - builder.append(counter); - builder.append(", children="); - builder.append(children.keySet()); - builder.append("\n"); - for (CounterNode child: children.values()) { - builder.append(child.toString(indent + 3)); - } - return builder.toString(); - } - - - -} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..5bb1ce16757a4faa363205c346887ad1fbbc0d3e --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java @@ -0,0 +1,741 @@ +package net.floodlightcontroller.debugcounter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Sets; + +import net.floodlightcontroller.core.module.FloodlightModuleContext; +import net.floodlightcontroller.core.module.FloodlightModuleException; +import net.floodlightcontroller.core.module.IFloodlightModule; +import net.floodlightcontroller.core.module.IFloodlightService; +import net.floodlightcontroller.debugcounter.web.DebugCounterRoutable; +import net.floodlightcontroller.restserver.IRestApiService; + +/** + * This class implements a central store for all counters used for debugging the + * system. For counters based on traffic-type, see ICounterStoreService. + * + * @author Saurav + */ +public class DebugCounter implements IFloodlightModule, IDebugCounterService { + protected static Logger log = LoggerFactory.getLogger(DebugCounter.class); + + /** + * registered counters need a counter id + */ + protected AtomicInteger counterIdCounter = new AtomicInteger(); + + /** + * The counter value + */ + protected class MutableLong { + long value = 0; + public void increment() { value += 1; } + public void increment(long incr) { value += incr; } + public long get() { return value; } + public void set(long val) { value = val; } + } + + /** + * protected class to store counter information + */ + public static class CounterInfo { + String moduleCounterHierarchy; + String counterDesc; + CounterType ctype; + String moduleName; + String counterHierarchy; + int counterId; + boolean enabled; + String[] metaData; + + public CounterInfo(int counterId, boolean enabled, + String moduleName, String counterHierarchy, + String desc, CounterType ctype, String... metaData) { + this.moduleCounterHierarchy = moduleName + "/" + counterHierarchy; + this.moduleName = moduleName; + this.counterHierarchy = counterHierarchy; + this.counterDesc = desc; + this.ctype = ctype; + this.counterId = counterId; + this.enabled = enabled; + this.metaData = metaData; + } + + public String getModuleCounterHierarchy() { return moduleCounterHierarchy; } + public String getCounterDesc() { return counterDesc; } + public CounterType getCtype() { return ctype; } + public String getModuleName() { return moduleName; } + public String getCounterHierarchy() { return counterHierarchy; } + public int getCounterId() { return counterId; } + public boolean isEnabled() { return enabled; } + public String[] getMetaData() { return metaData; } + } + + //****************** + // Global stores + //****************** + + /** + * Counter info for a debug counter + */ + public class DebugCounterInfo { + CounterInfo cinfo; + AtomicLong cvalue; + + public DebugCounterInfo(CounterInfo cinfo) { + this.cinfo = cinfo; + this.cvalue = new AtomicLong(); + } + public CounterInfo getCounterInfo() { + return cinfo; + } + public Long getCounterValue() { + return cvalue.get(); + } + } + + /** + * Global debug-counter storage across all threads. These are + * updated from the local per thread counters by the flush counters method. + */ + protected static DebugCounterInfo[] allCounters = + new DebugCounterInfo[MAX_COUNTERS]; + + + /** + * per module counters, indexed by the module name and storing three levels + * of Counter information in the form of CounterIndexStore + */ + protected ConcurrentHashMap<String, ConcurrentHashMap<String, CounterIndexStore>> + moduleCounters = new ConcurrentHashMap<String, + ConcurrentHashMap<String, + CounterIndexStore>>(); + + protected class CounterIndexStore { + int index; + Map<String, CounterIndexStore> nextLevel; + + public CounterIndexStore(int index, Map<String,CounterIndexStore> cis) { + this.index = index; + this.nextLevel = cis; + } + } + + /** + * fast global cache for counter ids that are currently active + */ + protected Set<Integer> currentCounters = Collections.newSetFromMap( + new ConcurrentHashMap<Integer,Boolean>()); + + //****************** + // Thread local stores + //****************** + + /** + * Thread local storage of counter info + */ + protected class LocalCounterInfo { + boolean enabled; + MutableLong cvalue; + + public LocalCounterInfo(boolean enabled) { + this.enabled = enabled; + this.cvalue = new MutableLong(); + } + } + + /** + * Thread local debug counters used for maintaining counters local to a thread. + */ + protected final ThreadLocal<LocalCounterInfo[]> threadlocalCounters = + new ThreadLocal<LocalCounterInfo[]>() { + @Override + protected LocalCounterInfo[] initialValue() { + return new LocalCounterInfo[MAX_COUNTERS]; + } + }; + + /** + * Thread local cache for counter ids that are currently active. + */ + protected final ThreadLocal<Set<Integer>> threadlocalCurrentCounters = + new ThreadLocal<Set<Integer>>() { + @Override + protected Set<Integer> initialValue() { + return new HashSet<Integer>(); + } + }; + + //******************************* + // IDebugCounter + //******************************* + + protected class CounterImpl implements IDebugCounter { + private final int counterId; + + public CounterImpl(int counterId) { + this.counterId = counterId; + } + + @Override + public void updateCounterWithFlush() { + if (!validCounterId()) return; + updateCounter(counterId, 1, true); + } + + @Override + public void updateCounterNoFlush() { + if (!validCounterId()) return; + updateCounter(counterId, 1, false); + } + + @Override + public void updateCounterWithFlush(int incr) { + if (!validCounterId()) return; + updateCounter(counterId, incr, true); + } + + @Override + public void updateCounterNoFlush(int incr) { + if (!validCounterId()) return; + updateCounter(counterId, incr, false); + } + + @Override + public long getCounterValue() { + if (!validCounterId()) return -1; + return allCounters[counterId].cvalue.get(); + } + + private boolean validCounterId() { + if (counterId < 0 || counterId >= MAX_COUNTERS) { + log.error("Invalid counterId invoked"); + return false; + } + return true; + } + + } + + //******************************* + // IDebugCounterService + //******************************* + + @Override + public IDebugCounter registerCounter(String moduleName, String counterHierarchy, + String counterDescription, CounterType counterType, + String... metaData) + throws MaxCountersRegistered, MaxHierarchyRegistered, + MissingHierarchicalLevel { + // check if counter already exists + if (!moduleCounters.containsKey(moduleName)) { + moduleCounters.putIfAbsent(moduleName, + new ConcurrentHashMap<String, CounterIndexStore>()); + } + RetCtrInfo rci = getCounterId(moduleName, counterHierarchy); + if (rci.allLevelsFound) { + // counter exists + log.info("Counter exists for {}/{} -- resetting counters", moduleName, + counterHierarchy); + resetCounterHierarchy(moduleName, counterHierarchy); + return new CounterImpl(rci.ctrIds[rci.foundUptoLevel-1]); + } + // check for validity of counter + if (rci.levels.length > MAX_HIERARCHY) { + String err = "Registry of counterHierarchy " + counterHierarchy + + " exceeds max hierachy " + MAX_HIERARCHY + ".. aborting"; + throw new MaxHierarchyRegistered(err); + } + if (rci.foundUptoLevel < rci.levels.length-1) { + String needToRegister = ""; + for (int i=0; i<=rci.foundUptoLevel; i++) { + needToRegister += rci.levels[i]; + } + String err = "Attempting to register hierarchical counterHierarchy " + + counterHierarchy + " but parts of hierarchy missing. " + + "Please register " + needToRegister + " first"; + throw new MissingHierarchicalLevel(err); + } + + // get a new counter id + int counterId = counterIdCounter.getAndIncrement(); + if (counterId >= MAX_COUNTERS) { + throw new MaxCountersRegistered("max counters reached"); + } + // create storage for counter + boolean enabled = (counterType == CounterType.ALWAYS_COUNT) ? true : false; + CounterInfo ci = new CounterInfo(counterId, enabled, moduleName, + counterHierarchy, counterDescription, + counterType, metaData); + allCounters[counterId] = new DebugCounterInfo(ci); + + // account for the new counter in the module counter hierarchy + addToModuleCounterHierarchy(moduleName, counterId, rci); + + // finally add to active counters + if (enabled) { + currentCounters.add(counterId); + } + return new CounterImpl(counterId); + } + + private void updateCounter(int counterId, int incr, boolean flushNow) { + if (counterId < 0 || counterId >= MAX_COUNTERS) return; + + LocalCounterInfo[] thiscounters = this.threadlocalCounters.get(); + if (thiscounters[counterId] == null) { + // seeing this counter for the first time in this thread - create local + // store by consulting global store + DebugCounterInfo dc = allCounters[counterId]; + if (dc != null) { + thiscounters[counterId] = new LocalCounterInfo(dc.cinfo.enabled); + if (dc.cinfo.enabled) { + Set<Integer> thisset = this.threadlocalCurrentCounters.get(); + thisset.add(counterId); + } + } else { + log.error("updateCounter seen locally for counter {} but no global" + + "storage exists for it yet .. not updating", counterId); + return; + } + } + + // update local store if enabled locally for updating + LocalCounterInfo lc = thiscounters[counterId]; + if (lc.enabled) { + lc.cvalue.increment(incr); + if (flushNow) { + DebugCounterInfo dc = allCounters[counterId]; + if (dc.cinfo.enabled) { + // globally enabled - flush now + dc.cvalue.addAndGet(lc.cvalue.get()); + lc.cvalue.set(0); + } else { + // global counter is disabled - don't flush, disable locally + lc.enabled = false; + Set<Integer> thisset = this.threadlocalCurrentCounters.get(); + thisset.remove(counterId); + } + } + } + } + + @Override + public void flushCounters() { + LocalCounterInfo[] thiscounters = this.threadlocalCounters.get(); + Set<Integer> thisset = this.threadlocalCurrentCounters.get(); + ArrayList<Integer> temp = new ArrayList<Integer>(); + + for (int counterId : thisset) { + LocalCounterInfo lc = thiscounters[counterId]; + if (lc.cvalue.get() > 0) { + DebugCounterInfo dc = allCounters[counterId]; + if (dc.cinfo.enabled) { + // globally enabled - flush now + dc.cvalue.addAndGet(lc.cvalue.get()); + lc.cvalue.set(0); + } else { + // global counter is disabled - don't flush, disable locally + lc.enabled = false; + temp.add(counterId); + } + } + } + for (int cId : temp) { + thisset.remove(cId); + } + + // At this point it is possible that the thread-local set does not + // include a counter that has been enabled and is present in the global set. + // We need to sync thread-local currently enabled set of counterIds with + // the global set. + Sets.SetView<Integer> sv = Sets.difference(currentCounters, thisset); + for (int counterId : sv) { + if (thiscounters[counterId] != null) { + thiscounters[counterId].enabled = true; + thisset.add(counterId); + } + } + } + + @Override + public void resetCounterHierarchy(String moduleName, String counterHierarchy) { + RetCtrInfo rci = getCounterId(moduleName, counterHierarchy); + if (!rci.allLevelsFound) { + String missing = rci.levels[rci.foundUptoLevel]; + log.error("Cannot reset counter hierarchy - missing counter {}", missing); + return; + } + // reset at this level + allCounters[rci.ctrIds[rci.foundUptoLevel-1]].cvalue.set(0); + // reset all levels below + ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci); + for (int index : resetIds) { + allCounters[index].cvalue.set(0); + } + } + + @Override + public void resetAllCounters() { + RetCtrInfo rci = new RetCtrInfo(); + rci.levels = "".split("/"); + for (String moduleName : moduleCounters.keySet()) { + ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci); + for (int index : resetIds) { + allCounters[index].cvalue.set(0); + } + } + } + + @Override + public void resetAllModuleCounters(String moduleName) { + Map<String, CounterIndexStore> target = moduleCounters.get(moduleName); + RetCtrInfo rci = new RetCtrInfo(); + rci.levels = "".split("/"); + + if (target != null) { + ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci); + for (int index : resetIds) { + allCounters[index].cvalue.set(0); + } + } else { + if (log.isDebugEnabled()) + log.debug("No module found with name {}", moduleName); + } + } + + @Override + public void enableCtrOnDemand(String moduleName, String counterHierarchy) { + RetCtrInfo rci = getCounterId(moduleName, counterHierarchy); + if (!rci.allLevelsFound) { + String missing = rci.levels[rci.foundUptoLevel]; + log.error("Cannot enable counter - counter not found {}", missing); + return; + } + // enable specific counter + DebugCounterInfo dc = allCounters[rci.ctrIds[rci.foundUptoLevel-1]]; + dc.cinfo.enabled = true; + currentCounters.add(dc.cinfo.counterId); + } + + @Override + public void disableCtrOnDemand(String moduleName, String counterHierarchy) { + RetCtrInfo rci = getCounterId(moduleName, counterHierarchy); + if (!rci.allLevelsFound) { + String missing = rci.levels[rci.foundUptoLevel]; + log.error("Cannot disable counter - counter not found {}", missing); + return; + } + // disable specific counter + DebugCounterInfo dc = allCounters[rci.ctrIds[rci.foundUptoLevel-1]]; + if (dc.cinfo.ctype == CounterType.COUNT_ON_DEMAND) { + dc.cinfo.enabled = false; + dc.cvalue.set(0); + currentCounters.remove(dc.cinfo.counterId); + } + } + + @Override + public List<DebugCounterInfo> getCounterHierarchy(String moduleName, + String counterHierarchy) { + RetCtrInfo rci = getCounterId(moduleName, counterHierarchy); + if (!rci.allLevelsFound) { + String missing = rci.levels[rci.foundUptoLevel]; + log.error("Cannot fetch counter - counter not found {}", missing); + return Collections.emptyList(); + } + ArrayList<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>(); + // get counter and all below it + DebugCounterInfo dc = allCounters[rci.ctrIds[rci.foundUptoLevel-1]]; + dcilist.add(dc); + ArrayList<Integer> belowIds = getHierarchyBelow(moduleName, rci); + for (int index : belowIds) { + dcilist.add(allCounters[index]); + } + return dcilist; + } + + @Override + public List<DebugCounterInfo> getAllCounterValues() { + List<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>(); + RetCtrInfo rci = new RetCtrInfo(); + rci.levels = "".split("/"); + + for (String moduleName : moduleCounters.keySet()) { + ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci); + for (int index : resetIds) { + dcilist.add(allCounters[index]); + } + } + return dcilist; + } + + @Override + public List<DebugCounterInfo> getModuleCounterValues(String moduleName) { + List<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>(); + RetCtrInfo rci = new RetCtrInfo(); + rci.levels = "".split("/"); + + if (moduleCounters.containsKey(moduleName)) { + ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci); + for (int index : resetIds) { + dcilist.add(allCounters[index]); + } + } + return dcilist; + } + + @Override + public boolean containsModuleCounterHierarchy(String moduleName, + String counterHierarchy) { + if (!moduleCounters.containsKey(moduleName)) return false; + RetCtrInfo rci = getCounterId(moduleName, counterHierarchy); + return rci.allLevelsFound; + } + + @Override + public boolean containsModuleName(String moduleName) { + return (moduleCounters.containsKey(moduleName)) ? true : false; + } + + @Override + public List<String> getModuleList() { + List<String> retval = new ArrayList<String>(); + retval.addAll(moduleCounters.keySet()); + return retval; + } + + @Override + public List<String> getModuleCounterList(String moduleName) { + if (!moduleCounters.containsKey(moduleName)) + return Collections.emptyList(); + + List<String> retval = new ArrayList<String>(); + RetCtrInfo rci = new RetCtrInfo(); + rci.levels = "".split("/"); + + ArrayList<Integer> cids = getHierarchyBelow(moduleName, rci); + for (int index : cids) { + retval.add(allCounters[index].cinfo.counterHierarchy); + } + return retval; + } + + //******************************* + // Internal Methods + //******************************* + + protected class RetCtrInfo { + boolean allLevelsFound; // counter indices found all the way down the hierarchy + boolean hierarchical; // true if counterHierarchy is hierarchical + int foundUptoLevel; + int[] ctrIds; + String[] levels; + + public RetCtrInfo() { + ctrIds = new int[MAX_HIERARCHY]; + for (int i=0; i<MAX_HIERARCHY; i++) { + ctrIds[i] = -1; + } + } + + @Override + public boolean equals(Object oth) { + if (!(oth instanceof RetCtrInfo)) return false; + RetCtrInfo other = (RetCtrInfo)oth; + if (other.allLevelsFound != this.allLevelsFound) return false; + if (other.hierarchical != this.hierarchical) return false; + if (other.foundUptoLevel != this.foundUptoLevel) return false; + if (!Arrays.equals(other.ctrIds, this.ctrIds)) return false; + if (!Arrays.equals(other.levels, this.levels)) return false; + return true; + } + + } + + protected RetCtrInfo getCounterId(String moduleName, String counterHierarchy) { + RetCtrInfo rci = new RetCtrInfo(); + Map<String, CounterIndexStore> templevel = moduleCounters.get(moduleName); + rci.levels = counterHierarchy.split("/"); + if (rci.levels.length > 1) rci.hierarchical = true; + if (templevel == null) { + log.error("moduleName {} does not exist in debugCounters", moduleName); + return rci; + } + + /* + if (rci.levels.length > MAX_HIERARCHY) { + // chop off all array elems greater that MAX_HIERARCHY + String[] temp = new String[MAX_HIERARCHY]; + System.arraycopy(rci.levels, 0, temp, 0, MAX_HIERARCHY); + rci.levels = temp; + } + */ + for (int i=0; i<rci.levels.length; i++) { + if (templevel != null) { + CounterIndexStore cis = templevel.get(rci.levels[i]) ; + if (cis == null) { + // could not find counterHierarchy part at this level + break; + } else { + rci.ctrIds[i] = cis.index; + templevel = cis.nextLevel; + rci.foundUptoLevel++; + if (i == rci.levels.length-1) { + rci.allLevelsFound = true; + } + } + } else { + // there are no more levels, which means that some part of the + // counterHierarchy has no corresponding map + break; + } + } + return rci; + } + + protected void addToModuleCounterHierarchy(String moduleName, int counterId, + RetCtrInfo rci) { + Map<String, CounterIndexStore> target = moduleCounters.get(moduleName); + if (target == null) return; + CounterIndexStore cis = null; + + for (int i=0; i<rci.foundUptoLevel; i++) { + cis = target.get(rci.levels[i]); + target = cis.nextLevel; + } + if (cis != null) { + if (cis.nextLevel == null) + cis.nextLevel = new ConcurrentHashMap<String, CounterIndexStore>(); + cis.nextLevel.put(rci.levels[rci.foundUptoLevel], + new CounterIndexStore(counterId, null)); + } else { + target.put(rci.levels[rci.foundUptoLevel], + new CounterIndexStore(counterId, null)); + } + } + + // given a partial hierarchical counter, return the rest of the hierarchy + protected ArrayList<Integer> getHierarchyBelow(String moduleName, RetCtrInfo rci) { + Map<String, CounterIndexStore> target = moduleCounters.get(moduleName); + CounterIndexStore cis = null; + ArrayList<Integer> retval = new ArrayList<Integer>(); + if (target == null) return retval; + + // get to the level given + for (int i=0; i<rci.foundUptoLevel; i++) { + cis = target.get(rci.levels[i]); + target = cis.nextLevel; + } + + if (target == null || rci.foundUptoLevel == MAX_HIERARCHY) { + // no more levels + return retval; + } else { + // recursively get all ids + getIdsAtLevel(target, retval, rci.foundUptoLevel+1); + } + + return retval; + } + + protected void getIdsAtLevel(Map<String, CounterIndexStore> hcy, + ArrayList<Integer> retval, int level) { + if (level > MAX_HIERARCHY) return; + if (hcy == null || retval == null) return; + + // Can return the counter names as well but for now ids are enough. + for (CounterIndexStore cistemp : hcy.values()) { + retval.add(cistemp.index); // value at this level + if (cistemp.nextLevel != null) { + getIdsAtLevel(cistemp.nextLevel, retval, level+1); + } + } + } + + protected void printAllCounterIds() { + log.info("<moduleCounterHierarchy>"); + Set<String> keys = moduleCounters.keySet(); + for (String key : keys) { + log.info("ModuleName: {}", key); + Map<String, CounterIndexStore> lev1 = moduleCounters.get(key); + for (String key1 : lev1.keySet()) { + CounterIndexStore cis1 = lev1.get(key1); + log.info(" L1 {}:{}", key1, new Object[] {cis1.index, cis1.nextLevel}); + if (cis1.nextLevel != null) { + Map<String, CounterIndexStore> lev2 = cis1.nextLevel; + for (String key2 : lev2.keySet()) { + CounterIndexStore cis2 = lev2.get(key2); + log.info(" L2 {}:{}", key2, new Object[] {cis2.index, + cis2.nextLevel}); + if (cis2.nextLevel != null) { + Map<String, CounterIndexStore> lev3 = cis2.nextLevel; + for (String key3 : lev3.keySet()) { + CounterIndexStore cis3 = lev3.get(key3); + log.info(" L3 {}:{}", key3, new Object[] {cis3.index, + cis3.nextLevel}); + } + } + } + } + } + } + log.info("<\\moduleCounterHierarchy>"); + } + + //******************************* + // IFloodlightModule + //******************************* + + @Override + public Collection<Class<? extends IFloodlightService>> getModuleServices() { + Collection<Class<? extends IFloodlightService>> l = + new ArrayList<Class<? extends IFloodlightService>>(); + l.add(IDebugCounterService.class); + return l; + } + + @Override + public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { + Map<Class<? extends IFloodlightService>, IFloodlightService> m = + new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); + m.put(IDebugCounterService.class, this); + return m; + } + + @Override + public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { + ArrayList<Class<? extends IFloodlightService>> deps = + new ArrayList<Class<? extends IFloodlightService>>(); + deps.add(IRestApiService.class); + return deps; + } + + @Override + public void init(FloodlightModuleContext context) throws FloodlightModuleException { + + } + + @Override + public void startUp(FloodlightModuleContext context) { + IRestApiService restService = + context.getServiceImpl(IRestApiService.class); + restService.addRestletRoutable(new DebugCounterRoutable()); + } + +} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterImpl.java b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterImpl.java deleted file mode 100644 index c0aa316d953dc90a65c565f1d34719c8d19ddd5f..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterImpl.java +++ /dev/null @@ -1,141 +0,0 @@ -package net.floodlightcontroller.debugcounter; - -import java.util.Collection; -import java.util.concurrent.atomic.AtomicLong; - -import javax.annotation.Nonnull; - -import net.floodlightcontroller.debugcounter.IDebugCounterService.MetaData; - -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; - - -class DebugCounterImpl implements IDebugCounter { - private final String moduleName; - private final String counterHierarchy; - private final String description; - private final ImmutableSet<IDebugCounterService.MetaData> metaData; - - private final AtomicLong value = new AtomicLong(); - - - DebugCounterImpl(@Nonnull String moduleName, - @Nonnull String counterHierarchy, - @Nonnull String description, - @Nonnull Collection<MetaData> metaData) { - this.moduleName = moduleName; - this.counterHierarchy = counterHierarchy; - this.description = description; - this.metaData = Sets.immutableEnumSet(metaData); - } - - @Nonnull - String getModuleName() { - return moduleName; - } - - - @Nonnull - String getCounterHierarchy() { - return counterHierarchy; - } - - - @Nonnull - String getDescription() { - return description; - } - - @Nonnull - ImmutableSet<IDebugCounterService.MetaData> getMetaData() { - return metaData; - } - - public void reset() { - value.set(0); - } - - @Override - public void increment() { - value.incrementAndGet(); - } - - @Override - public void add(long increment) { - if (increment < 0) { - throw new IllegalArgumentException("increment must be > 0. Was " - + increment); - } - value.addAndGet(increment); - } - - @Override - public long getCounterValue() { - return value.get(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime - * result - + ((counterHierarchy == null) ? 0 - : counterHierarchy.hashCode()); - result = prime * result - + ((description == null) ? 0 : description.hashCode()); - result = prime * result - + ((metaData == null) ? 0 : metaData.hashCode()); - result = prime * result - + ((moduleName == null) ? 0 : moduleName.hashCode()); - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - DebugCounterImpl other = (DebugCounterImpl) obj; - if (counterHierarchy == null) { - if (other.counterHierarchy != null) return false; - } else if (!counterHierarchy.equals(other.counterHierarchy)) - return false; - if (description == null) { - if (other.description != null) return false; - } else if (!description.equals(other.description)) return false; - if (metaData == null) { - if (other.metaData != null) return false; - } else if (!metaData.equals(other.metaData)) return false; - if (moduleName == null) { - if (other.moduleName != null) return false; - } else if (!moduleName.equals(other.moduleName)) return false; - if (value == null) { - if (other.value != null) return false; - } else if (value.get() != other.value.get()) return false; - return true; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("["); - builder.append(moduleName); - builder.append(" "); - builder.append(counterHierarchy); - builder.append(", description="); - builder.append(description); - builder.append(", metaData="); - builder.append(metaData); - builder.append(", value="); - builder.append(value); - builder.append("]"); - return builder.toString(); - } - - - - -} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterResource.java b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterResource.java deleted file mode 100644 index 941802c31d4bd2b536002f0a094b6b781af78e32..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterResource.java +++ /dev/null @@ -1,119 +0,0 @@ -package net.floodlightcontroller.debugcounter; - -import java.util.Set; - -import javax.annotation.concurrent.Immutable; - -import net.floodlightcontroller.debugcounter.IDebugCounterService.MetaData; - -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableSet; - -/** - * Resource class for DebugCounter - * Serves the REST api with dynamic data - */ -@Immutable -public class DebugCounterResource { - - public static final String MODULE_NAME_PREDICATE = "modulename"; - public static final String HIERARCHY_PREDICATE = "hierarchy"; - private static final Joiner joiner = Joiner.on(", "); - - - private final Long counterValue; - private final String counterDesc; - private final String counterHierarchy; - private final String moduleName; - private final ImmutableSet<MetaData> metadata; - private final String metadataString; - - public DebugCounterResource(DebugCounterImpl counter) { - this.moduleName = counter.getModuleName(); - this.counterHierarchy = counter.getCounterHierarchy(); - this.counterDesc = counter.getDescription(); - this.metadata = counter.getMetaData(); - this.counterValue = counter.getCounterValue(); - this.metadataString = joiner.join(metadata); - } - - public Long getCounterValue() { - return counterValue; - } - - public String getCounterDesc() { - return counterDesc; - } - - public String getCounterHierarchy() { - return counterHierarchy; - } - - public String getModuleName() { - return moduleName; - } - - public Set<MetaData> getMetadata() { - return metadata; - } - - public String getMetadataString() { - return metadataString; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((counterDesc == null) ? 0 : counterDesc.hashCode()); - result = prime - * result - + ((counterHierarchy == null) ? 0 - : counterHierarchy.hashCode()); - result = prime * result - + ((counterValue == null) ? 0 : counterValue.hashCode()); - result = prime * result - + ((metadata == null) ? 0 : metadata.hashCode()); - result = prime - * result - + ((metadataString == null) ? 0 : metadataString.hashCode()); - result = prime * result - + ((moduleName == null) ? 0 : moduleName.hashCode()); - return result; - } - - /** - * Compare all fields, not only the "key" fields - */ - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - DebugCounterResource other = (DebugCounterResource) obj; - if (counterDesc == null) { - if (other.counterDesc != null) return false; - } else if (!counterDesc.equals(other.counterDesc)) return false; - if (counterHierarchy == null) { - if (other.counterHierarchy != null) return false; - } else if (!counterHierarchy.equals(other.counterHierarchy)) - return false; - if (counterValue == null) { - if (other.counterValue != null) return false; - } else if (!counterValue.equals(other.counterValue)) return false; - if (metadata == null) { - if (other.metadata != null) return false; - } else if (!metadata.equals(other.metadata)) return false; - if (metadataString == null) { - if (other.metadataString != null) return false; - } else if (!metadataString.equals(other.metadataString)) - return false; - if (moduleName == null) { - if (other.moduleName != null) return false; - } else if (!moduleName.equals(other.moduleName)) return false; - return true; - } - - -} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterResourceBase.java b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterResourceBase.java new file mode 100644 index 0000000000000000000000000000000000000000..9edd47473a75564334c217b240002612d31e6219 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterResourceBase.java @@ -0,0 +1,16 @@ +package net.floodlightcontroller.debugcounter; + +import org.restlet.resource.ResourceException; +import org.restlet.resource.ServerResource; + +public class DebugCounterResourceBase extends ServerResource { + + protected IDebugCounterService debugCounter; + + @Override + protected void doInit() throws ResourceException { + super.doInit(); + debugCounter = (IDebugCounterService)getContext().getAttributes(). + get(IDebugCounterService.class.getCanonicalName()); + } +} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterServiceImpl.java b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterServiceImpl.java deleted file mode 100644 index 2e2fe499f8ff592cfc9837091f0cd22fbe6be4b2..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounterServiceImpl.java +++ /dev/null @@ -1,251 +0,0 @@ -package net.floodlightcontroller.debugcounter; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import javax.annotation.Nonnull; -import javax.annotation.concurrent.GuardedBy; - -import org.projectfloodlight.core.IShutdownListener; -import org.projectfloodlight.core.IShutdownService; -import net.floodlightcontroller.core.module.FloodlightModuleContext; -import net.floodlightcontroller.core.module.IFloodlightModule; -import net.floodlightcontroller.core.module.IFloodlightService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class DebugCounterServiceImpl implements IFloodlightModule, IDebugCounterService { - protected static final Logger logger = - LoggerFactory.getLogger(DebugCounterServiceImpl.class); - - /** - * The tree of counters. - */ - private final CounterNode root = CounterNode.newTree(); - - /** - * protects the counter hierarchy tree. The writeLock is required to - * change to hierarchy, i.e., adding nodes. The readLock is required - * to query the counters or to reset them. - */ - private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - - static void verifyStringSanity(String str, String name) { - if (str == null) { - if (name == null) { - throw new NullPointerException(); - } else { - throw new NullPointerException(name + " must not be null"); - } - } - if (str.isEmpty()) { - if (name == null) { - throw new IllegalArgumentException(); - } else { - throw new IllegalArgumentException(name + " must not be empty"); - } - } - } - - static void verifyModuleNameSanity(String moduleName) { - verifyStringSanity(moduleName, "moduleName"); - if (moduleName.contains("/")) { - throw new IllegalArgumentException("moduleName must not contain /"); - } - } - - @Override - public boolean registerModule(String moduleName) { - verifyModuleNameSanity(moduleName); - lock.writeLock().lock(); - try { - return root.addModule(moduleName); - } finally { - lock.writeLock().unlock(); - } - } - - @Override - public IDebugCounter registerCounter(@Nonnull String moduleName, - @Nonnull String counterHierarchy, - @Nonnull String counterDescription, - @Nonnull MetaData... metaData) { - verifyModuleNameSanity(moduleName); - verifyStringSanity(counterHierarchy, "counterHierarchy"); - if (counterDescription == null) { - throw new Exception("counterDescription must not be null"); - } - if (metaData == null) { - // somebody passing in a null array. sigh. - throw new NullPointerException("metaData must not be null"); - } - DebugCounterImpl counter = - new DebugCounterImpl(moduleName, counterHierarchy, - counterDescription, - Arrays.asList(metaData)); - lock.writeLock().lock(); - try { - DebugCounterImpl oldCounter = root.addCounter(counter); - if (oldCounter != null && logger.isDebugEnabled()) { - logger.debug("Counter {} {} already registered. Resetting hierarchy", - moduleName, counterHierarchy); - } - return oldCounter; - } finally { - lock.writeLock().unlock(); - } - } - - @GuardedBy("lock.readLock") - private boolean resetInternal(List<String> hierarchyElements) { - CounterNode node = root.lookup(hierarchyElements); - if (node == null) { - return false; - } - node.resetHierarchy(); - return true; - } - - @Override - public boolean resetCounterHierarchy(String moduleName, - String counterHierarchy) { - verifyModuleNameSanity(moduleName); - verifyStringSanity(counterHierarchy, "counterHierarchy"); - lock.readLock().lock(); - try { - return resetInternal(CounterNode.getHierarchyElements(moduleName, counterHierarchy)); - } finally { - lock.readLock().unlock(); - } - } - - @Override - public void resetAllCounters() { - lock.readLock().lock(); - try { - root.resetHierarchy(); - } finally { - lock.readLock().unlock(); - } - } - - - @Override - public boolean resetAllModuleCounters(String moduleName) { - verifyModuleNameSanity(moduleName); - lock.readLock().lock(); - try { - return resetInternal(Collections.singletonList(moduleName)); - } finally { - lock.readLock().unlock(); - } - } - - @GuardedBy("lock.readLock") - private List<DebugCounterResource> getCountersFromNode(CounterNode node) { - if (node == null) { - return Collections.emptyList(); - } - List<DebugCounterResource> ret = new ArrayList<>(); - for (DebugCounterImpl counter: node.getCountersInHierarchy()) { - ret.add(new DebugCounterResource(counter)); - } - return ret; - } - - @Override - public List<DebugCounterResource> - getCounterHierarchy(String moduleName, String counterHierarchy) { - verifyModuleNameSanity(moduleName); - verifyStringSanity(counterHierarchy, "counterHierarchy"); - List<String> hierarchyElements = - CounterNode.getHierarchyElements(moduleName, counterHierarchy); - lock.readLock().lock(); - try { - return getCountersFromNode(root.lookup(hierarchyElements)); - } finally { - lock.readLock().unlock(); - } - } - - @Override - public List<DebugCounterResource> getAllCounterValues() { - lock.readLock().lock(); - try { - return getCountersFromNode(root); - } finally { - lock.readLock().unlock(); - } - } - - @Override - public List<DebugCounterResource> getModuleCounterValues(String moduleName) { - verifyModuleNameSanity(moduleName); - List<String> hierarchyElements = Collections.singletonList(moduleName); - lock.readLock().lock(); - try { - return getCountersFromNode(root.lookup(hierarchyElements)); - } finally { - lock.readLock().unlock(); - } - } - - private class ShutdownListenenerDelegate implements IShutdownListener { - @Override - public void floodlightIsShuttingDown() { - for (DebugCounterResource counter: getAllCounterValues()) { - logger.info("Module {} counterHierarchy {} value {}", - counter.getModuleName(), - counter.getCounterHierarchy(), - counter.getCounterValue()); - } - } - } - - - //******************************* - // IFloodlightModule - //******************************* - - @Override - public Collection<Class<? extends IFloodlightService>> getModuleServices() { - Collection<Class<? extends IFloodlightService>> l = - new ArrayList<Class<? extends IFloodlightService>>(); - l.add(IDebugCounterService.class); - return l; - } - - @Override - public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { - Map<Class<? extends IFloodlightService>, IFloodlightService> m = - new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); - m.put(IDebugCounterService.class, this); - return m; - } - - @Override - public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { - ArrayList<Class<? extends IFloodlightService>> deps = - new ArrayList<Class<? extends IFloodlightService>>(); - deps.add(IShutdownService.class); - return deps; - } - - @Override - public void init(FloodlightModuleContext context) { - } - - @Override - public void startUp(FloodlightModuleContext context) { - IShutdownService shutdownService = - context.getServiceImpl(IShutdownService.class); - shutdownService.registerShutdownListener(new ShutdownListenenerDelegate()); - } - -} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounter.java b/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..dbde1854e9c6f7707e983eedf5bfe6fef299d7ec --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounter.java @@ -0,0 +1,38 @@ +package net.floodlightcontroller.debugcounter; + +public interface IDebugCounter { + /** + * Increments the counter by 1 thread-locally, and immediately flushes to + * the global counter storage. This method should be used for counters that + * are updated outside the OF message processing pipeline. + */ + void updateCounterWithFlush(); + + /** + * Increments the counter by 1 thread-locally. Flushing to the global + * counter storage is delayed (happens with flushCounters() in IDebugCounterService), + * resulting in higher performance. This method should be used for counters + * updated in the OF message processing pipeline. + */ + void updateCounterNoFlush(); + + /** + * Increments the counter thread-locally by the 'incr' specified, and immediately + * flushes to the global counter storage. This method should be used for counters + * that are updated outside the OF message processing pipeline. + */ + void updateCounterWithFlush(int incr); + + /** + * Increments the counter thread-locally by the 'incr' specified. Flushing to the global + * counter storage is delayed (happens with flushCounters() in IDebugCounterService), + * resulting in higher performance. This method should be used for counters + * updated in the OF message processing pipeline. + */ + void updateCounterNoFlush(int incr); + + /** + * Retrieve the value of the counter from the global counter store + */ + long getCounterValue(); +} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java b/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java new file mode 100644 index 0000000000000000000000000000000000000000..538e60dad148c600e3802b53271ed6e111d8f6b9 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java @@ -0,0 +1,259 @@ +package net.floodlightcontroller.debugcounter; + +import net.floodlightcontroller.core.module.IFloodlightService; +import net.floodlightcontroller.debugcounter.DebugCounter.DebugCounterInfo; + +import java.util.List; + +public interface IDebugCounterService extends IFloodlightService { + + /** + * Different counter types. Counters that are meant to be counted-on-demand + * need to be separately enabled/disabled. + */ + public enum CounterType { + ALWAYS_COUNT, + COUNT_ON_DEMAND + } + + /** + * Debug Counter Qualifiers + */ + public static final String CTR_MDATA_WARN = "warn"; + public static final String CTR_MDATA_ERROR = "error"; + + /** + * A limit on the maximum number of counters that can be created + */ + public static final int MAX_COUNTERS = 5000; + + /** + * Exception thrown when MAX_COUNTERS have been registered + */ + public class MaxCountersRegistered extends CounterException { + private static final long serialVersionUID = 3173747663719376745L; + String errormsg; + public MaxCountersRegistered(String errormsg) { + this.errormsg = errormsg; + } + @Override + public String getMessage() { + return this.errormsg; + } + } + /** + * Exception thrown when MAX_HIERARCHY has been reached + */ + public class MaxHierarchyRegistered extends CounterException { + private static final long serialVersionUID = 967431358683523871L; + String errormsg; + public MaxHierarchyRegistered(String errormsg) { + this.errormsg = errormsg; + } + @Override + public String getMessage() { + return this.errormsg; + } + } + /** + * Exception thrown when attempting to register a hierarchical counter + * where higher levels of the hierarchy have not been pre-registered + */ + public class MissingHierarchicalLevel extends CounterException { + private static final long serialVersionUID = 517315311533995739L; + String errormsg; + public MissingHierarchicalLevel(String errormsg) { + this.errormsg = errormsg; + } + @Override + public String getMessage() { + return this.errormsg; + } + } + + public class CounterException extends Exception { + private static final long serialVersionUID = 2219781500857866035L; + } + + /** + * maximum levels of hierarchy + * Example of moduleName/counterHierarchy: + * switch/00:00:00:00:01:02:03:04/pktin/drops where + * moduleName ==> "switch" and + * counterHierarchy of 3 ==> "00:00:00:00:01:02:03:04/pktin/drops" + */ + public static final int MAX_HIERARCHY = 3; + + /** + * All modules that wish to have the DebugCounterService count for them, must + * register their counters by making this call (typically from that module's + * 'startUp' method). The counter can then be updated, displayed, reset etc. + * using the registered moduleName and counterHierarchy. + * + * @param moduleName the name of the module which is registering the + * counter eg. linkdiscovery or controller or switch + * @param counterHierarchy the hierarchical counter name specifying all + * the hierarchical levels that come above it. + * For example: to register a drop counter for + * packet-ins from a switch, the counterHierarchy + * can be "00:00:00:00:01:02:03:04/pktin/drops" + * It is necessary that counters in hierarchical levels + * above have already been pre-registered - in this + * example: "00:00:00:00:01:02:03:04/pktin" and + * "00:00:00:00:01:02:03:04" + * @param counterDescription a descriptive string that gives more information + * of what the counter is measuring. For example, + * "Measures the number of incoming packets seen by + * this module". + * @param counterType One of CounterType. On-demand counter types + * need to be explicitly enabled/disabled using other + * methods in this API -- i.e. registering them is + * not enough to start counting. + * @param metaData variable arguments that qualify a counter + * eg. warn, error etc. + * @return IDebugCounter with update methods that can be + * used to update a counter. + * @throws MaxCountersRegistered + * @throws MaxHierarchyRegistered + * @throws MissingHierarchicalLevel + */ + public IDebugCounter registerCounter(String moduleName, String counterHierarchy, + String counterDescription, CounterType counterType, + String... metaData) + throws MaxCountersRegistered, MaxHierarchyRegistered, + MissingHierarchicalLevel; + + /** + * Flush all thread-local counter values (from the current thread) + * to the global counter store. This method is not intended for use by any + * module. It's typical usage is from floodlight core and it is meant + * to flush those counters that are updated in the packet-processing pipeline, + * typically with the 'updateCounterNoFlush" methods in IDebugCounter. + */ + public void flushCounters(); + + /** + * Resets the value of counters in the hierarchy to zero. Note that the reset + * applies to the level of counter hierarchy specified AND ALL LEVELS BELOW it + * in the hierarchy. + * For example: If a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops" + * specifying a reset hierarchy: "00:00:00:00:01:02:03:04" + * will reset all counters for the switch dpid specified; + * while specifying a reset hierarchy: ""00:00:00:00:01:02:03:04/pktin" + * will reset the pktin counter and all levels below it (like drops) + * for the switch dpid specified. + */ + void resetCounterHierarchy(String moduleName, String counterHierarchy); + + /** + * Resets the values of all counters in the system. + */ + public void resetAllCounters(); + + /** + * Resets the values of all counters belonging + * to a module with the given 'moduleName'. + */ + public void resetAllModuleCounters(String moduleName); + + /** + * This method applies only to CounterType.COUNT_ON_DEMAND. It is used to + * enable counting on the counter. Note that this step is necessary to start + * counting for these counter types - merely registering the counter is not + * enough (as is the case for CounterType.ALWAYS_COUNT). Newly + * enabled counters start from an initial value of zero. + * + * Enabling a counter in a counterHierarchy enables only THAT counter. It + * does not enable any other part of the counterHierarchy. For example, if + * a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops", where the + * 'pktin' and 'drops' counters are CounterType.COUNT_ON_DEMAND, then enabling + * the 'pktin' counter by specifying the counterHierarchy as + * "00:00:00:00:01:02:03:04/pktin" does NOT enable the 'drops' counter. + */ + public void enableCtrOnDemand(String moduleName, String counterHierarchy); + + /** + * This method applies only to CounterType.COUNT_ON_DEMAND. It is used to + * enable counting on the counter. Note that disabling a counter results in a loss + * of the counter value. When re-enabled the counter will restart from zero. + * + * Disabling a counter in a counterHierarchy disables only THAT counter. It + * does not disable any other part of the counterHierarchy. For example, if + * a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops", where the + * 'pktin' and 'drops' counters are CounterType.COUNT_ON_DEMAND, then disabling + * the 'pktin' counter by specifying the counterHierarchy as + * "00:00:00:00:01:02:03:04/pktin" does NOT disable the 'drops' counter. + */ + public void disableCtrOnDemand(String moduleName, String counterHierarchy); + + /** + * Get counter value and associated information for the specified counterHierarchy. + * Note that information on the level of counter hierarchy specified + * AND ALL LEVELS BELOW it in the hierarchy will be returned. + * + * For example, + * if a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops", then + * specifying a counterHierarchy of "00:00:00:00:01:02:03:04/pktin" in the + * get call will return information on the 'pktin' as well as the 'drops' + * counters for the switch dpid specified. + * + * @return A list of DebugCounterInfo or an empty list if the counter + * could not be found + */ + public List<DebugCounterInfo> getCounterHierarchy(String moduleName, + String counterHierarchy); + + /** + * Get counter values and associated information for all counters in the + * system + * + * @return the list of values/info or an empty list + */ + public List<DebugCounterInfo> getAllCounterValues(); + + /** + * Get counter values and associated information for all counters associated + * with a module. + * + * @param moduleName + * @return the list of values/info or an empty list + */ + public List<DebugCounterInfo> getModuleCounterValues(String moduleName); + + /** + * Convenience method to figure out if the the given 'counterHierarchy' corresponds + * to a registered counterHierarchy for 'moduleName'. Note that the counter may or + * may not be enabled for counting, but if it is registered the method will + * return true. + * + * @param param + * @return false if moduleCounterHierarchy is not a registered counter + */ + public boolean containsModuleCounterHierarchy(String moduleName, + String counterHierarchy); + + /** + * Convenience method to figure out if the the given 'moduleName' corresponds + * to a registered moduleName or not. Note that the module may or may not have + * a counter enabled for counting, but if it is registered the method will + * return true. + * + * @param param + * @return false if moduleName is not a registered counter + */ + public boolean containsModuleName(String moduleName); + + /** + * Returns a list of moduleNames registered for debug counters or an empty + * list if no counters have been registered in the system + */ + public List<String> getModuleList(); + + /** + * Returns a list of all counters registered for a specific moduleName + * or a empty list + */ + public List<String> getModuleCounterList(String moduleName); + + +} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/MockDebugCounterService.java b/src/main/java/net/floodlightcontroller/debugcounter/MockDebugCounterService.java deleted file mode 100644 index 3540519639911602788f965e52d2793f27d0f737..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/debugcounter/MockDebugCounterService.java +++ /dev/null @@ -1,115 +0,0 @@ -package net.floodlightcontroller.debugcounter; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.projectfloodlight.core.module.FloodlightModuleContext; -import org.projectfloodlight.core.module.FloodlightModuleException; -import org.projectfloodlight.core.module.IFloodlightModule; -import org.projectfloodlight.core.module.IFloodlightService; - -public class MockDebugCounterService implements IFloodlightModule, IDebugCounterService { - - - @Override - public Collection<Class<? extends IFloodlightService>> - getModuleServices() { - Collection<Class<? extends IFloodlightService>> services = - new ArrayList<Class<? extends IFloodlightService>>(1); - services.add(IDebugCounterService.class); - return services; - } - - @Override - public Map<Class<? extends IFloodlightService>, IFloodlightService> - getServiceImpls() { - Map<Class<? extends IFloodlightService>, - IFloodlightService> m = - new HashMap<Class<? extends IFloodlightService>, - IFloodlightService>(); - m.put(IDebugCounterService.class, this); - return m; - } - - @Override - public Collection<Class<? extends IFloodlightService>> - getModuleDependencies() { - return null; - } - - @Override - public void init(FloodlightModuleContext context) - throws FloodlightModuleException { - - } - - @Override - public void startUp(FloodlightModuleContext context) { - - } - - @Override - public boolean registerModule(String moduleName) { - return true; - } - - @Override - public IDebugCounter registerCounter(String moduleName, - String counterHierarchy, - String counterDescription, - MetaData... metaData) { - return new MockCounterImpl(); - } - - @Override - public boolean - resetCounterHierarchy(String moduleName, String counterHierarchy) { - return true; - } - - @Override - public void resetAllCounters() { - } - - @Override - public boolean resetAllModuleCounters(String moduleName) { - return true; - } - - @Override - public List<DebugCounterResource> - getCounterHierarchy(String moduleName, String counterHierarchy) { - return Collections.emptyList(); - } - - @Override - public List<DebugCounterResource> getAllCounterValues() { - return Collections.emptyList(); - } - - @Override - public List<DebugCounterResource> - getModuleCounterValues(String moduleName) { - return Collections.emptyList(); - } - - public static class MockCounterImpl implements IDebugCounter { - @Override - public void increment() { - } - - @Override - public void add(long incr) { - } - - @Override - public long getCounterValue() { - return -1; - } - } - -} diff --git a/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java b/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..5dc7b319219de1acb202ffdf3df1b347127bb10c --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java @@ -0,0 +1,162 @@ +package net.floodlightcontroller.debugcounter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.floodlightcontroller.core.module.FloodlightModuleContext; +import net.floodlightcontroller.core.module.FloodlightModuleException; +import net.floodlightcontroller.core.module.IFloodlightModule; +import net.floodlightcontroller.core.module.IFloodlightService; +import net.floodlightcontroller.debugcounter.DebugCounter.DebugCounterInfo; + +public class NullDebugCounter implements IFloodlightModule, IDebugCounterService { + + + @Override + public Collection<Class<? extends IFloodlightService>> + getModuleServices() { + Collection<Class<? extends IFloodlightService>> services = + new ArrayList<Class<? extends IFloodlightService>>(1); + services.add(IDebugCounterService.class); + return services; + } + + @Override + public Map<Class<? extends IFloodlightService>, IFloodlightService> + getServiceImpls() { + Map<Class<? extends IFloodlightService>, + IFloodlightService> m = + new HashMap<Class<? extends IFloodlightService>, + IFloodlightService>(); + m.put(IDebugCounterService.class, this); + return m; + } + + @Override + public Collection<Class<? extends IFloodlightService>> + getModuleDependencies() { + return null; + } + + @Override + public void init(FloodlightModuleContext context) + throws FloodlightModuleException { + + } + + @Override + public void startUp(FloodlightModuleContext context) { + + } + + + @Override + public void flushCounters() { + + } + + @Override + public void resetAllCounters() { + + } + + @Override + public void resetAllModuleCounters(String moduleName) { + + } + + + @Override + public void resetCounterHierarchy(String moduleName, String counterHierarchy) { + + } + + @Override + public void enableCtrOnDemand(String moduleName, String counterHierarchy) { + + } + + @Override + public void disableCtrOnDemand(String moduleName, String counterHierarchy) { + + } + + @Override + public List<DebugCounterInfo> getCounterHierarchy(String moduleName, + String counterHierarchy) { + return Collections.emptyList(); + } + + @Override + public List<DebugCounterInfo> getAllCounterValues() { + return Collections.emptyList(); + } + + @Override + public List<DebugCounterInfo> getModuleCounterValues(String moduleName) { + return Collections.emptyList(); + } + + @Override + public boolean containsModuleCounterHierarchy(String moduleName, + String counterHierarchy) { + return false; + } + + @Override + public boolean containsModuleName(String moduleName) { + return false; + } + + @Override + public + IDebugCounter + registerCounter(String moduleName, String counterHierarchy, + String counterDescription, + CounterType counterType, String... metaData) + throws MaxCountersRegistered { + return new NullCounterImpl(); + } + + @Override + public List<String> getModuleList() { + return Collections.emptyList(); + } + + @Override + public List<String> getModuleCounterList(String moduleName) { + return Collections.emptyList(); + } + + public class NullCounterImpl implements IDebugCounter { + + @Override + public void updateCounterWithFlush() { + + } + + @Override + public void updateCounterNoFlush() { + + } + + @Override + public void updateCounterWithFlush(int incr) { + } + + @Override + public void updateCounterNoFlush(int incr) { + + } + + @Override + public long getCounterValue() { + return -1; + } + + } +} diff --git a/src/main/java/net/floodlightcontroller/debugevent/DebugEventResource.java b/src/main/java/net/floodlightcontroller/debugevent/DebugEventResource.java deleted file mode 100644 index 7e71d5490faacbd4710d4d9b8411fedc9bc5c9eb..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/debugevent/DebugEventResource.java +++ /dev/null @@ -1,207 +0,0 @@ -package net.floodlightcontroller.debugevent; - -import java.util.List; - -import javax.annotation.concurrent.Immutable; - -import org.projectfloodlight.db.data.annotation.DBProperty; -import org.projectfloodlight.debugevent.DebugEventService.EventInfo; -import org.projectfloodlight.debugevent.IDebugEventService.EventType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.ComparisonChain; -import com.google.common.collect.ImmutableList; - -/** - * All the Immutable Resource classes for REST API. - */ -@Immutable -public class DebugEventResource { - - protected static final Logger logger = LoggerFactory.getLogger(DebugEventResource.class); - public static final String MODULE_NAME_PREDICATE = "module-name"; - public static final String EVENT_NAME_PREDICATE = "event-name"; - public static final String LAST_PREDICATE = "num-of-events"; - public static final String EVENT_ID = "event-id"; - public static final String EVENT_INSTANCE_ID = "event-instance-id"; - public static final String ACKED = "acked"; - - /** - * Resource class for {@link EventInfo}. Used to create Immutable objects - * returned in response to REST calls. - */ - @Immutable - public static class EventInfoResource implements - Comparable<EventInfoResource> { - - private final int eventId; - private final boolean enabled; - private final int bufferCapacity; - private final EventType etype; - private final String eventDesc; - private final String eventName; - private final String moduleName; - private final int numOfEvents; - private final boolean ackable; - public final ImmutableList<EventResource> events; - - public EventInfoResource(EventInfo eventInfo, - List<EventResource> events) { - super(); - this.eventId = eventInfo.getEventId(); - this.enabled = eventInfo.isEnabled(); - this.bufferCapacity = eventInfo.getBufferCapacity(); - this.etype = eventInfo.getEtype(); - this.eventDesc = eventInfo.getEventDesc(); - this.eventName = eventInfo.getEventName(); - this.moduleName = eventInfo.getModuleName(); - this.numOfEvents = eventInfo.getNumOfEvents(); - this.ackable = eventInfo.isAckable(); - this.events = ImmutableList.copyOf(events); - } - - @DBProperty(value = "enabled") - public boolean isEnabled() { - return enabled; - } - - @DBProperty(value = "buffer-capacity") - public int getBufferCapacity() { - return bufferCapacity; - } - - @DBProperty(value = "etype") - public EventType getEtype() { - return etype; - } - - @DBProperty(value = "event-desc") - public String getEventDesc() { - return eventDesc; - } - - @DBProperty(value = "event-name") - public String getEventName() { - return eventName; - } - - @DBProperty(value = "module-name") - public String getModuleName() { - return moduleName; - } - - @DBProperty(value = "num-of-events") - public int getNumOfEvents() { - return numOfEvents; - } - - @DBProperty(value = "ackable") - public boolean isAckable() { - return ackable; - } - - @DBProperty(value = "event") - public List<EventResource> getEvents() { - return events; - } - - @DBProperty(value = "event-id") - public int getEventId() { - return eventId; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (ackable ? 1231 : 1237); - result = prime * result + bufferCapacity; - result = prime * result + (enabled ? 1231 : 1237); - result = prime * result - + ((etype == null) ? 0 : etype.hashCode()); - result = prime * result - + ((eventDesc == null) ? 0 : eventDesc.hashCode()); - result = prime * result + eventId; - result = prime * result - + ((eventName == null) ? 0 : eventName.hashCode()); - result = prime * result - + ((events == null) ? 0 : events.hashCode()); - result = prime * result - + ((moduleName == null) ? 0 : moduleName.hashCode()); - result = prime * result + numOfEvents; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - EventInfoResource other = (EventInfoResource) obj; - if (ackable != other.ackable) return false; - if (bufferCapacity != other.bufferCapacity) return false; - if (enabled != other.enabled) return false; - if (etype != other.etype) return false; - if (eventDesc == null) { - if (other.eventDesc != null) return false; - } else if (!eventDesc.equals(other.eventDesc)) return false; - if (eventId != other.eventId) return false; - if (eventName == null) { - if (other.eventName != null) return false; - } else if (!eventName.equals(other.eventName)) return false; - if (events == null) { - if (other.events != null) return false; - } else if (!events.equals(other.events)) return false; - if (moduleName == null) { - if (other.moduleName != null) return false; - } else if (!moduleName.equals(other.moduleName)) return false; - if (numOfEvents != other.numOfEvents) return false; - return true; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("EventInfoResource [eventId="); - builder.append(eventId); - builder.append(", enabled="); - builder.append(enabled); - builder.append(", bufferCapacity="); - builder.append(bufferCapacity); - builder.append(", etype="); - builder.append(etype); - builder.append(", eventDesc="); - builder.append(eventDesc); - builder.append(", eventName="); - builder.append(eventName); - builder.append(", moduleName="); - builder.append(moduleName); - builder.append(", numOfEvents="); - builder.append(numOfEvents); - builder.append(", ackable="); - builder.append(ackable); - builder.append(", events="); - builder.append(events); - builder.append("]"); - return builder.toString(); - } - - /** - * The natural order of this class is ascending on eventId and - * consistent with equals. - */ - @Override - public int compareTo(EventInfoResource o) { - return ComparisonChain.start().compare(eventId, o.eventId) - .compareFalseFirst(enabled, o.enabled) - .compare(bufferCapacity, o.bufferCapacity) - .compare(etype, o.etype) - .compare(eventDesc, o.eventDesc) - .compare(eventName, o.eventName) - .compare(moduleName, o.moduleName) - .compare(numOfEvents, o.numOfEvents) - .result(); - } - } -} diff --git a/src/main/java/net/floodlightcontroller/debugevent/DebugEventService.java b/src/main/java/net/floodlightcontroller/debugevent/DebugEventService.java deleted file mode 100644 index 113a42002ffdd43a35c903a029024e599d302bf4..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/debugevent/DebugEventService.java +++ /dev/null @@ -1,711 +0,0 @@ -package net.floodlightcontroller.debugevent; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -import org.projectfloodlight.core.IShutdownListener; -import org.projectfloodlight.core.IShutdownService; -import org.projectfloodlight.core.module.FloodlightModuleContext; -import org.projectfloodlight.core.module.IFloodlightModule; -import org.projectfloodlight.core.module.IFloodlightService; -import org.projectfloodlight.debugevent.DebugEventResource.EventInfoResource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Sets; - -/** - * This class implements a central store for all events used for debugging the - * system. The basic idea is that given the functionality provided by this - * class, it should be unnecessary to resort to scraping through system - * DEBUG/TRACE logs to understand behavior in a running system. - * - * @author Saurav - */ -public class DebugEventService implements IFloodlightModule, IDebugEventService { - protected static final Logger log = LoggerFactory.getLogger(DebugEventService.class); - - /** - * Every registered event type gets an event id AtomicInt to make the get - * operation thread-safe - */ - private final AtomicInteger eventIdCounter = new AtomicInteger(); - - /** - * Unique Event Instance Id for ack-ing - */ - private final AtomicLong eventInstanceId = new AtomicLong(Long.MAX_VALUE); - - private static final int PCT_LOCAL_CAP = 10; // % of global capacity - private static final int MIN_LOCAL_CAPACITY = 10; // elements - - /** - * EnumMap from {@link EventFieldType} to {@link CustomFormatter} - */ - static final ImmutableMap<EventFieldType, CustomFormatter<?>> customFormatter = - new ImmutableMap.Builder<EventFieldType, CustomFormatter<?>>() - .put(EventFieldType.DPID, new CustomFormatterDpid()) - .put(EventFieldType.IPv4, new CustomFormatterIpv4()) - .put(EventFieldType.MAC, new CustomFormatterMac()) - .put(EventFieldType.STRING, new CustomFormatterString()) - .put(EventFieldType.OBJECT, new CustomFormatterObject()) - .put(EventFieldType.PRIMITIVE, new CustomFormatterPrimitive()) - .put(EventFieldType.COLLECTION_IPV4, new CustomFormatterCollectionIpv4()) - .put(EventFieldType.COLLECTION_ATTACHMENT_POINT, new CustomFormatterCollectionAttachmentPoint()) - .put(EventFieldType.COLLECTION_OBJECT, new CustomFormatterCollectionObject()) - .put(EventFieldType.SREF_COLLECTION_OBJECT, new CustomFormatterSrefCollectionObject()) - .put(EventFieldType.SREF_OBJECT, new CustomFormatterSrefObject()) - .build(); - - /** - * Event Information - */ - public static class EventInfo { - private final int eventId; - private final boolean enabled; - private final int bufferCapacity; - private int numOfEvents; - private final EventType etype; - private final String eventDesc; - private final String eventName; - private final String moduleName; - private final String moduleEventName; - private final Class<?> eventClass; - private final boolean ackable; - - public EventInfo(int eventId, boolean enabled, boolean ackable, - int bufferCapacity, EventType etype, - Class<?> eventClass, String eventDesc, - String eventName, String moduleName) { - this.enabled = enabled; - this.ackable = ackable; - this.eventId = eventId; - this.bufferCapacity = bufferCapacity; - this.numOfEvents = bufferCapacity; - this.etype = etype; - this.eventClass = eventClass; - this.eventDesc = eventDesc; - this.eventName = eventName; - this.moduleName = moduleName; - this.moduleEventName = moduleName + "/" + eventName; - } - - public int getEventId() { - return eventId; - } - - public boolean isEnabled() { - return enabled; - } - - public boolean isAckable() { - return ackable; - } - - public int getBufferCapacity() { - return bufferCapacity; - } - - public int getNumOfEvents() { - return numOfEvents; - } - - public EventType getEtype() { - return etype; - } - - public String getEventDesc() { - return eventDesc; - } - - public String getEventName() { - return eventName; - } - - public String getModuleName() { - return moduleName; - } - - public String getModuleEventName() { - return moduleEventName; - } - } - - // ****************** - // Global stores - // ****************** - - /** - * Event history for a particular event-id is stored in a circular buffer - */ - protected static class DebugEventHistory { - EventInfo einfo; - LinkedBlockingDeque<Event> circularEventBuffer; - - public DebugEventHistory(EventInfo einfo, int capacity) { - this.einfo = einfo; - this.circularEventBuffer = new LinkedBlockingDeque<Event>( - capacity); - } - } - - /** - * Global storage for all event types and their corresponding event buffers. - * A particular event type is accessed by directly indexing into the Map - * with the corresponding event-id. <br/> - * It is a <b>Map</b> with <br/> - * <b>Key</b> Integer eventId <br/> - * <b>Value</b> DebugEventHistory - */ - protected final ConcurrentHashMap<Integer, DebugEventHistory> allEvents = new ConcurrentHashMap<Integer, DebugEventHistory>(); - - /** - * Global storage for all event ids registered for a module. The map is - * indexed by the module name and event name and returns the event-ids that - * correspond to the event types registered by that module (for example - * module 'linkdiscovery' may register events that have ids 0 and 1 that - * correspond to link up/down events, and receiving malformed LLDP packets, - * respectively). - */ - protected final ConcurrentHashMap<String, ConcurrentHashMap<String, Integer>> moduleEvents = new ConcurrentHashMap<String, ConcurrentHashMap<String, Integer>>(); - - /** - * A collection of event ids that are currently enabled for logging - */ - protected final Set<Integer> currentEvents = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>()); - - // ****************** - // Thread local stores - // ****************** - - /** - * Thread local storage for events - */ - protected static class LocalEventHistory { - private boolean enabled; - private final int capacity; - private ArrayList<Event> eventList; - - public LocalEventHistory(boolean enabled, int maxCapacity) { - this.enabled = enabled; - this.eventList = new ArrayList<Event>(maxCapacity); - this.capacity = maxCapacity; - } - - public boolean add(Event e) { - if (this.eventList.size() < capacity) { - this.eventList.add(e); - return true; - } - return false; - } - - public int drainTo(List<Event> eventList) { - int size = this.eventList.size(); - Iterator<Event> iter = this.eventList.iterator(); - while (iter.hasNext()) { - eventList.add(iter.next()); - } - this.eventList.clear(); - return size; - } - - public boolean isFull() { - if (eventList.size() == capacity) return true; - return false; - } - - public boolean isEmpty() { - return this.eventList.isEmpty(); - } - } - - /** - * Thread local event buffers used for maintaining event history local to a - * thread. Eventually this locally maintained information is flushed into - * the global event buffers. <br/> - * It is a <b>Map</b> with <br/> - * <b>Key</b> Integer eventId <br/> - * <b>Value</b> LocalEventHistory - */ - protected final ThreadLocal<Map<Integer, LocalEventHistory>> threadlocalEvents = new ThreadLocal<Map<Integer, LocalEventHistory>>() { - @Override - protected Map<Integer, LocalEventHistory> initialValue() { - return new HashMap<Integer, LocalEventHistory>(); - } - }; - - /** - * Thread local cache for event-ids that are currently active. - */ - protected final ThreadLocal<Set<Integer>> threadlocalCurrentEvents = new ThreadLocal<Set<Integer>>() { - @Override - protected Set<Integer> initialValue() { - return new HashSet<Integer>(); - } - }; - - // ******************************* - // IEventCategory - // ******************************* - - protected class EventCategory<T> implements IEventCategory<T> { - private final int eventId; - - public EventCategory(int evId) { - this.eventId = evId; - } - - @Override - public void newEventNoFlush(Object event) { - if (!validEventId()) return; - newEvent(eventId, false, event); - } - - @Override - public void newEventWithFlush(Object event) { - if (!validEventId()) return; - newEvent(eventId, true, event); - } - - private boolean validEventId() { - if (eventId < 0) { - throw new IllegalStateException(); - } - return true; - } - } - - public class EventCategoryBuilder<T> { - private int eventId; - private String moduleName; - private String eventName; - private String eventDescription; - private EventType eventType; - private Class<T> eventClass; - private int bufferCapacity; - private boolean ackable; - - public EventCategoryBuilder(Class<T> evClass) { - this.eventId = eventIdCounter.incrementAndGet(); - this.eventClass = evClass; - } - - public EventCategoryBuilder<T> setModuleName(String moduleName) { - this.moduleName = moduleName; - return this; - } - - public EventCategoryBuilder<T> setEventName(String eventName) { - this.eventName = eventName; - return this; - } - - public EventCategoryBuilder<T> setEventDescription(String eventDescription) { - this.eventDescription = eventDescription; - return this; - } - - public EventCategoryBuilder<T> setEventType(EventType et) { - this.eventType = et; - return this; - } - - public EventCategoryBuilder<T> setBufferCapacity(int bufferCapacity) { - this.bufferCapacity = bufferCapacity; - return this; - } - - public EventCategoryBuilder<T> setAckable(boolean ackable) { - this.ackable = ackable; - return this; - } - - /** - * Build a new {@link EventCategory<T>} <br/> - * Requires the following parameters to be set before being called: - * - * @param moduleName - * module registering event eg. linkdiscovery, virtualrouting. - * @param eventName - * name given to event. - * @param eventDescription - * A descriptive string describing the event. - * @param eventType - * EventType for this event. On-demand events have to be - * explicitly enabled using other methods in this API - * @param eventClass - * A user defined class that annotates the fields with - * @EventColumn. This class specifies the fields/columns for this - * event. - * @param bufferCapacity - * Number of events to store for this event in a circular buffer. - * Older events will be discarded once the buffer is full. - * @param ackable - * is the event used as part of ackable-event framework boolean - * - * @return IEventCategory with <b>newEvent</b> method that can be used - * to create instances of event of the given eventClass - */ - public EventCategory<T> register() { - // register event id for moduleName - moduleEvents.putIfAbsent(moduleName, - new ConcurrentHashMap<String, Integer>()); - Integer eventExists = moduleEvents.get(moduleName) - .putIfAbsent(eventName, eventId); - if (eventExists != null) { - log.error("Duplicate event registration for moduleName {} eventName {}", - moduleName, eventName); - return new EventCategory<T>(eventExists); - } - - // create storage for event-type - boolean enabled = (eventType == EventType.ALWAYS_LOG) ? true : false; - EventInfo ei = new EventInfo(eventId, enabled, ackable, - bufferCapacity, eventType, eventClass, - eventDescription, eventName, moduleName); - allEvents.put(eventId, new DebugEventHistory(ei, bufferCapacity)); - if (enabled) { - currentEvents.add(eventId); - } - - return new EventCategory<T>(this.eventId); - } - } - - // ******************************* - // IDebugEventService - // ******************************* - - @Override - public <T> EventCategoryBuilder<T> buildEvent(Class<T> evClass) { - return new EventCategoryBuilder<T>(evClass); - } - - private void flushLocalToGlobal(int eventId, LocalEventHistory le) { - DebugEventHistory de = allEvents.get(eventId); - if (de.einfo.enabled) { - List<Event> transferEvents = new ArrayList<Event>(); - // drain local buffer to Collection - int size = le.drainTo(transferEvents); - // if global buffer doesn't have enough space, clear - // some space - int requiredSpace = size - - de.circularEventBuffer.remainingCapacity(); - if (requiredSpace > 0) { - for (int i = 0; i < requiredSpace; i++) { - de.circularEventBuffer.removeFirst(); - } - } - de.circularEventBuffer.addAll(transferEvents); - } else { - le.enabled = false; - this.threadlocalCurrentEvents.get().remove(eventId); - } - } - - private void newEvent(int eventId, boolean flushNow, Object eventData) { - if (eventId < 0) { - throw new IllegalStateException("Invalid eventId"); - } - - Map<Integer, LocalEventHistory> thishist = this.threadlocalEvents.get(); - if (!thishist.containsKey(eventId)) { - // seeing this event for the first time in this thread - create - // local - // store by consulting global store - if (allEvents.containsKey(eventId)) { - DebugEventHistory de = allEvents.get(eventId); - boolean enabled = de.einfo.enabled; - int localCapacity = de.einfo.bufferCapacity * PCT_LOCAL_CAP - / 100; - if (localCapacity < 10) localCapacity = MIN_LOCAL_CAPACITY; - thishist.put(eventId, new LocalEventHistory(enabled, - localCapacity)); - if (enabled) { - Set<Integer> thisset = this.threadlocalCurrentEvents.get(); - thisset.add(eventId); - } - } else { - log.error("updateEvent seen locally for event {} but no global" - + "storage exists for it yet .. not updating", - eventId); - return; - } - } - - // update local store if enabled locally for updating - LocalEventHistory le = thishist.get(eventId); - if (le.enabled) { - try { - le.add(new Event(System.currentTimeMillis(), - Thread.currentThread().getId(), - Thread.currentThread().getName(), - eventData, - /* - * the eventInstanceId is started in reverse - * order so BigDB gets the values in ascending - * order Initialization in startUp() - */ - eventInstanceId.decrementAndGet())); - - if (le.isFull() || flushNow) { - flushLocalToGlobal(eventId, le); - } - } catch (IllegalStateException ise) { - log.debug("Exception while adding event locally: " - + ise.getMessage()); - } - } - } - - @Override - public void flushEvents() { - Map<Integer, LocalEventHistory> thishist = this.threadlocalEvents.get(); - Set<Integer> thisset = this.threadlocalCurrentEvents.get(); - - for (int eventId : thisset) { - if (thishist.containsKey(eventId)) { - LocalEventHistory le = thishist.get(eventId); - if (!le.isEmpty()) { - flushLocalToGlobal(eventId, le); - } - } - } - - // sync thread local currently enabled set of eventIds with global set. - Sets.SetView<Integer> sv = Sets.difference(currentEvents, thisset); - for (int eventId : sv) { - if (thishist.containsKey(eventId)) { - thishist.get(eventId).enabled = true; - thisset.add(eventId); - } - } - - } - - @Override - public boolean containsModuleEventName(String moduleName, - String eventName) { - if (!moduleEvents.containsKey(moduleName)) return false; - if (moduleEvents.get(moduleName).containsKey(eventName)) - return true; - return false; - } - - @Override - public boolean containsModuleName(String moduleName) { - return moduleEvents.containsKey(moduleName); - } - - @Override - public List<EventInfoResource> getAllEventHistory() { - List<EventInfoResource> moduleEventList = new ArrayList<EventInfoResource>(); - for (Map<String, Integer> modev : moduleEvents.values()) { - for (int eventId : modev.values()) { - if (allEvents.containsKey(eventId)) { - DebugEventHistory de = allEvents.get(eventId); - - List<EventResource> eventData = new ArrayList<EventResource>(); - // take snapshot and iterate - Iterator<Event> iter = de.circularEventBuffer.descendingIterator(); - while (iter.hasNext()) { - Event e = iter.next(); - eventData.add(e.getFormattedEvent(de.einfo.eventClass, - de.einfo.moduleEventName)); - } - moduleEventList.add(new EventInfoResource(de.einfo, - eventData)); - } - } - } - traceLogDebugHistory(moduleEventList); - return moduleEventList; - } - - @Override - public List<EventInfoResource> getModuleEventHistory(String moduleName) { - if (!moduleEvents.containsKey(moduleName)) - return Collections.emptyList(); - List<EventInfoResource> moduleEventList = new ArrayList<EventInfoResource>(); - for (int eventId : moduleEvents.get(moduleName).values()) { - if (allEvents.containsKey(eventId)) { - DebugEventHistory de = allEvents.get(eventId); - - List<EventResource> eventData = new ArrayList<EventResource>(); - // take snapshot and iterate - Iterator<Event> iter = de.circularEventBuffer.descendingIterator(); - while (iter.hasNext()) { - Event e = iter.next(); - eventData.add(e.getFormattedEvent(de.einfo.eventClass, - de.einfo.moduleEventName)); - } - moduleEventList.add(new EventInfoResource(de.einfo, - eventData)); - } - } - traceLogDebugHistory(moduleEventList); - return moduleEventList; - } - - @Override - public EventInfoResource getSingleEventHistory(String moduleName, - String eventName, - int numOfEvents) { - if (!moduleEvents.containsKey(moduleName)) return null; - Integer eventId = moduleEvents.get(moduleName).get(eventName); - if (eventId == null) return null; - - if (!allEvents.containsKey(eventId)) return null; - - DebugEventHistory de = allEvents.get(eventId); - if (numOfEvents == 0) numOfEvents = de.einfo.bufferCapacity; - - de.einfo.numOfEvents = numOfEvents; - int num = 1; - List<EventResource> eventData = new ArrayList<EventResource>(); - // take snapshot and iterate - Iterator<Event> iter = de.circularEventBuffer.descendingIterator(); - while (iter.hasNext()) { - Event e = iter.next(); - if (num > numOfEvents) break; - eventData.add(e.getFormattedEvent(de.einfo.eventClass, - de.einfo.moduleEventName)); - num++; - } - EventInfoResource ret = new EventInfoResource(de.einfo, eventData); - traceLogDebugHistory(Collections.singletonList(ret)); - return ret; - } - - @Override - public void resetAllEvents() { - for (Map<String, Integer> eventMap : moduleEvents.values()) { - for (Integer evId : eventMap.values()) { - allEvents.get(evId).circularEventBuffer.clear(); - } - } - } - - @Override - public void resetAllModuleEvents(String moduleName) { - if (!moduleEvents.containsKey(moduleName)) return; - Map<String, Integer> modEvents = moduleEvents.get(moduleName); - for (Integer evId : modEvents.values()) { - allEvents.get(evId).circularEventBuffer.clear(); - } - } - - @Override - public void resetSingleEvent(String moduleName, String eventName) { - if (!moduleEvents.containsKey(moduleName)) return; - Integer eventId = moduleEvents.get(moduleName).get(eventName); - if (eventId == null) return; - - if (allEvents.containsKey(eventId)) { - allEvents.get(eventId).circularEventBuffer.clear(); - } - } - - @Override - public void setAck(int eventId, long eventInstanceId, boolean ack) { - if (allEvents.containsKey(eventId)) { - for (Event e : allEvents.get(eventId).circularEventBuffer) { - if (e.getEventInstanceId() == eventInstanceId) { - e.setAcked(ack); - } - } - } - } - - @Override - public List<String> getModuleList() { - List<String> el = new ArrayList<String>(); - el.addAll(moduleEvents.keySet()); - return el; - } - - @Override - public List<String> getModuleEventList(String moduleName) { - if (!moduleEvents.containsKey(moduleName)) - return Collections.emptyList(); - List<String> el = new ArrayList<String>(); - el.addAll(moduleEvents.get(moduleName).keySet()); - return el; - } - - - private void traceLogDebugHistory(List<EventInfoResource> l) { - if (!log.isTraceEnabled()) { - return; - } - for (EventInfoResource eir: l) { - for (EventResource der: eir.getEvents()) { - log.trace("{}", der); - } - } - } - - private class ShutdownListenenerDelegate implements IShutdownListener { - @Override - public void floodlightIsShuttingDown() { - for (EventInfoResource eir: getAllEventHistory()) { - for (EventResource der: eir.getEvents()) { - log.info("{}", der); - } - } - } - } - - // ******************************* - // IFloodlightModule - // ******************************* - - @Override - public Collection<Class<? extends IFloodlightService>> - getModuleServices() { - Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); - l.add(IDebugEventService.class); - return l; - } - - @Override - public Map<Class<? extends IFloodlightService>, IFloodlightService> - getServiceImpls() { - Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); - m.put(IDebugEventService.class, this); - return m; - } - - @Override - public Collection<Class<? extends IFloodlightService>> - getModuleDependencies() { - ArrayList<Class<? extends IFloodlightService>> deps = new ArrayList<Class<? extends IFloodlightService>>(); - deps.add(IShutdownService.class); - return deps; - } - - @Override - public void init(FloodlightModuleContext context) { - } - - @Override - public void startUp(FloodlightModuleContext context) { - IShutdownService shutdownService = - context.getServiceImpl(IShutdownService.class); - shutdownService.registerShutdownListener(new ShutdownListenenerDelegate()); - DebugEventAppender.setDebugEventServiceImpl(this); - } -} diff --git a/src/main/java/net/floodlightcontroller/debugevent/Event.java b/src/main/java/net/floodlightcontroller/debugevent/Event.java index 1eb76a1a80b0b1227e1867d27f2bb07849358ae7..3e0ac6db32eba864cb27c335553ade8a1bf5cfc6 100644 --- a/src/main/java/net/floodlightcontroller/debugevent/Event.java +++ b/src/main/java/net/floodlightcontroller/debugevent/Event.java @@ -11,10 +11,8 @@ import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn; import net.floodlightcontroller.devicemanager.SwitchPort; import net.floodlightcontroller.packet.IPv4; -import org.projectfloodlight.openflow.protocol.OFFlowMod; -import org.projectfloodlight.openflow.protocol.OFFlowModFlags; -import org.projectfloodlight.openflow.protocol.OFFlowModify; -import org.projectfloodlight.openflow.util.HexString; +import org.openflow.protocol.OFFlowMod; +import org.openflow.util.HexString; public class Event { long timestamp; @@ -112,8 +110,8 @@ public class Event { if (flags == 0) { builder.append("None"); } - else { //towirevalue - if ((flags & OFFlowModFlags.SEND_FLOW_REM.) != 0) { + else { + if ((flags & OFFlowMod.OFPFF_SEND_FLOW_REM) != 0) { builder.append("SEND_FLOW_REM "); } if ((flags & OFFlowMod.OFPFF_CHECK_OVERLAP) != 0) { diff --git a/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java b/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java new file mode 100644 index 0000000000000000000000000000000000000000..d54e9f632af539f53ecdd9e4b2b4477cba8d79d5 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java @@ -0,0 +1,189 @@ +package net.floodlightcontroller.debugevent; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; +import java.util.Map; + +import net.floodlightcontroller.core.module.IFloodlightService; +import net.floodlightcontroller.debugevent.DebugEvent.EventInfo; + +public interface IDebugEventService extends IFloodlightService { + + /** + * Different event types. Events that are meant to be logged on demand + * need to be separately enabled/disabled. + */ + public enum EventType { + ALWAYS_LOG, + LOG_ON_DEMAND + } + + /** + * Describes the type of field obtained from reflection + */ + enum EventFieldType { + DPID, IPv4, MAC, STRING, OBJECT, PRIMITIVE, LIST_IPV4, + LIST_ATTACHMENT_POINT, LIST_OBJECT, SREF_LIST_OBJECT, SREF_OBJECT, + FLOW_MOD_FLAGS + } + + /** + * EventColumn is the only annotation given to the fields of the event + * when updating an event. + */ + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.RUNTIME) + public @interface EventColumn { + String name() default "param"; + EventFieldType description() default EventFieldType.PRIMITIVE; + } + + /** + * Debug Event Qualifiers + */ + public static final String EV_MDATA_WARN = "warn"; + public static final String EV_MDATA_ERROR = "error"; + + /** + * A limit on the maximum number of events that can be created + */ + public static final int MAX_EVENTS = 2000; + + /** + * Public class for information returned in response to rest API calls. + */ + public class DebugEventInfo { + EventInfo eventInfo; + List<Map<String,String>> events; + + public DebugEventInfo(EventInfo eventInfo, + List<Map<String, String>> eventHistory) { + this.eventInfo = eventInfo; + this.events = eventHistory; + } + + public EventInfo getEventInfo() { + return eventInfo; + } + + public List<Map<String,String>> getEvents() { + return events; + } + } + + /** + * exception thrown when MAX_EVENTS have been registered + */ + public class MaxEventsRegistered extends Exception { + private static final long serialVersionUID = 2609587082227510262L; + } + + /** + * Register an event for debugging. + * + * @param moduleName module registering event eg. linkdiscovery, virtualrouting. + * @param eventName name given to event. + * @param eventDescription A descriptive string describing the event. + * @param eventType EventType for this event. On-demand events have to + * be explicitly enabled using other methods in this API + * @param eventClass A user defined class that annotates the fields + * with @EventColumn. This class specifies the + * fields/columns for this event. + * @param bufferCapacity Number of events to store for this event in a circular + * buffer. Older events will be discarded once the + * buffer is full. + * @param metaData variable arguments that qualify an event + * eg. EV_MDATA_WARN, EV_MDATA_ERROR etc. See Debug Event Qualifiers + * @return IEventUpdater with update methods that can be used to + * update an event of the given eventClass + * @throws MaxEventsRegistered + */ + public <T> IEventUpdater<T> registerEvent(String moduleName, String eventName, + String eventDescription, + EventType eventType, + Class<T> eventClass, + int bufferCapacity, + String... metaData) + throws MaxEventsRegistered; + + /** + * Update the global event stores with values from the thread local stores. This + * method is not typically intended for use by any module. It's typical usage is from + * floodlight core for events that happen in the packet processing pipeline. + * For other rare events, flushEvents should be called. + */ + public void flushEvents(); + + /** + * Determine if eventName is a registered event for a given moduleName + */ + public boolean containsModuleEventName(String moduleName, String eventName); + + /** + * Determine if any events have been registered for module of name moduleName + */ + public boolean containsModuleName(String moduleName); + + /** + * Get event history for all events. This call can be expensive as it + * formats the event histories for all events. + * + * @return a list of all event histories or an empty list if no events have + * been registered + */ + public List<DebugEventInfo> getAllEventHistory(); + + /** + * Get event history for all events registered for a given moduleName + * + * @return a list of all event histories for all events registered for the + * the module or an empty list if there are no events for this module + */ + public List<DebugEventInfo> getModuleEventHistory(String moduleName); + + /** + * Get event history for a single event + * + * @param moduleName registered module name + * @param eventName registered event name for moduleName + * @param last last X events + * @return DebugEventInfo for that event, or null if the moduleEventName + * does not correspond to a registered event. + */ + public DebugEventInfo getSingleEventHistory(String moduleName, String eventName, int last); + + /** + * Wipe out all event history for all registered events + */ + public void resetAllEvents(); + + /** + * Wipe out all event history for all events registered for a specific module + * + * @param moduleName registered module name + */ + public void resetAllModuleEvents(String moduleName); + + /** + * Wipe out event history for a single event + * @param moduleName registered module name + * @param eventName registered event name for moduleName + */ + public void resetSingleEvent(String moduleName, String eventName); + + /** + * Retrieve a list of moduleNames registered for debug events or an empty + * list if no events have been registered in the system + */ + public List<String> getModuleList(); + + /** + * Returns a list of all events registered for a specific moduleName + * or a empty list + */ + public List<String> getModuleEventList(String moduleName); + +} diff --git a/src/main/java/net/floodlightcontroller/debugevent/IEventCategory.java b/src/main/java/net/floodlightcontroller/debugevent/IEventCategory.java deleted file mode 100644 index 00cea123269b5aadef0d5438889ffaf00b24c2fb..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/debugevent/IEventCategory.java +++ /dev/null @@ -1,30 +0,0 @@ -package net.floodlightcontroller.debugevent; - -/** - * EventCategory is used to log events for pre-registered events. - */ -public interface IEventCategory<T> { - - /** - * Logs the instance of the event thread-locally. Flushing to the global - * circular buffer for this event is delayed resulting in better - * performance. This method should typically be used by those events that - * happen in the packet processing pipeline - * - * @param event - * an instance of the user-defined event of type T - */ - public void newEventNoFlush(T event); - - /** - * Logs the instance of the event thread-locally and immediated flushes to - * the global circular buffer for this event. This method should typically - * be used by those events that happen outside the packet processing - * pipeline - * - * @param event - * an instance of the user-defined event of type T - */ - public void newEventWithFlush(T event); - -} diff --git a/src/main/java/net/floodlightcontroller/debugevent/MockDebugEventService.java b/src/main/java/net/floodlightcontroller/debugevent/MockDebugEventService.java deleted file mode 100644 index 7f0676a556f37be26f259dfd883e8191c932b5a6..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/debugevent/MockDebugEventService.java +++ /dev/null @@ -1,128 +0,0 @@ -package net.floodlightcontroller.debugevent; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import net.floodlightcontroller.core.module.FloodlightModuleContext; -import net.floodlightcontroller.core.module.FloodlightModuleException; -import net.floodlightcontroller.core.module.IFloodlightModule; -import net.floodlightcontroller.core.module.IFloodlightService; -import net.floodlightcontroller.debugevent.DebugEventResource.EventInfoResource; -import net.floodlightcontroller.debugevent.DebugEventService.EventCategoryBuilder; - -public class MockDebugEventService implements IFloodlightModule, IDebugEventService { - - @Override - public <T> EventCategoryBuilder<T> buildEvent(Class<T> evClass) { - DebugEventService des = new DebugEventService(); - return des.buildEvent(evClass); - } - - @Override - public void flushEvents() { - - } - - @Override - public boolean containsModuleEventName(String moduleName, - String eventName) { - return false; - } - - @Override - public boolean containsModuleName(String moduleName) { - return false; - } - - @Override - public List<EventInfoResource> getAllEventHistory() { - return Collections.emptyList(); - } - - @Override - public List<EventInfoResource> getModuleEventHistory(String moduleName) { - return Collections.emptyList(); - } - - @Override - public EventInfoResource getSingleEventHistory(String moduleName, - String eventName, - int numOfEvents) { - return null; - } - - @Override - public void resetAllEvents() { - - } - - @Override - public void resetAllModuleEvents(String moduleName) { - - } - - @Override - public void resetSingleEvent(String moduleName, String eventName) { - - } - - @Override - public List<String> getModuleList() { - return Collections.emptyList(); - } - - @Override - public List<String> getModuleEventList(String moduleName) { - return Collections.emptyList(); - } - - @Override - public void setAck(int eventId, long eventInstanceId, boolean ack) { - - } - - @Override - public Collection<Class<? extends IFloodlightService>> - getModuleServices() { - Collection<Class<? extends IFloodlightService>> services = - new ArrayList<Class<? extends IFloodlightService>>(1); - services.add(IDebugEventService.class); - return services; - } - - @Override - public Map<Class<? extends IFloodlightService>, IFloodlightService> - getServiceImpls() { - Map<Class<? extends IFloodlightService>, IFloodlightService> m = - new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); - m.put(IDebugEventService.class, this); - return m; - } - - @Override - public Collection<Class<? extends IFloodlightService>> - getModuleDependencies() { - return null; - } - - @Override - public - void - init(FloodlightModuleContext context) - throws FloodlightModuleException { - - } - - @Override - public - void - startUp(FloodlightModuleContext context) - throws FloodlightModuleException { - - } - -} diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java index 4d5ba9be6d5d5ef7f31a4316cb92d2303a62502f..4593b7bac9d8f4b25f9a3c1e5c4693bfd1a0fca0 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java @@ -19,10 +19,6 @@ package net.floodlightcontroller.devicemanager; import java.util.Date; -import org.projectfloodlight.openflow.types.IPv4Address; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.VlanVid; - /** * Represents an independent device on the network. A device consists of a @@ -41,7 +37,7 @@ public interface IDevice { * Get the MAC address of the device as a Long value. * @return the MAC address for the device */ - public MacAddress getMACAddress(); + public long getMACAddress(); /** * Get the MAC address of the device as a String value. @@ -54,13 +50,13 @@ public interface IDevice { * entities, then the value -1 will be returned. * @return an array containing all unique VLAN IDs for the device. */ - public VlanVid[] getVlanId(); + public Short[] getVlanId(); /** * Get all unique IPv4 addresses associated with the device. * @return an array containing the unique IPv4 addresses for the device. */ - public IPv4Address[] getIPv4Addresses(); + public Integer[] getIPv4Addresses(); /** * Get all unique attachment points associated with the device. This will @@ -89,7 +85,7 @@ public interface IDevice { * @param swp the switch port to query * @return an array containing the unique VLAN IDs */ - public VlanVid[] getSwitchPortVlanIds(SwitchPort swp); + public Short[] getSwitchPortVlanIds(SwitchPort swp); /** * Get the most recent timestamp for this device diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java index 40d729f6894149d9a1e3142c618bc606c0753a4b..eb3801344f73d68ab0c35e6db556c09edfa1ba68 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java @@ -22,12 +22,6 @@ import java.util.EnumSet; import java.util.Iterator; import java.util.Set; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.IPv4Address; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.VlanVid; -import org.projectfloodlight.openflow.types.OFPort; - import net.floodlightcontroller.core.FloodlightContextStore; import net.floodlightcontroller.core.module.IFloodlightService; @@ -101,9 +95,9 @@ public interface IDeviceService extends IFloodlightService { * @throws IllegalArgumentException if not all key fields of the * current {@link IEntityClassifierService} are specified. */ - public IDevice findDevice(MacAddress macAddress, VlanVid vlan, - IPv4Address ipv4Address, DatapathId switchDPID, - OFPort switchPort) + public IDevice findDevice(long macAddress, Short vlan, + Integer ipv4Address, Long switchDPID, + Integer switchPort) throws IllegalArgumentException; /** @@ -128,8 +122,8 @@ public interface IDeviceService extends IFloodlightService { * source's {@link IEntityClass} are specified. */ public IDevice findClassDevice(IEntityClass entityClass, - MacAddress macAddress, VlanVid vlan, - IPv4Address ipv4Address) + long macAddress, Short vlan, + Integer ipv4Address) throws IllegalArgumentException; /** @@ -168,11 +162,11 @@ public interface IDeviceService extends IFloodlightService { * @see IDeviceService#queryClassDevices(IEntityClass, Long, * Short, Integer, Long, Integer) */ - public Iterator<? extends IDevice> queryDevices(MacAddress macAddress, - VlanVid vlan, - IPv4Address ipv4Address, - DatapathId switchDPID, - OFPort switchPort); + public Iterator<? extends IDevice> queryDevices(Long macAddress, + Short vlan, + Integer ipv4Address, + Long switchDPID, + Integer switchPort); /** * Find devices that match the provided query. Only the index for @@ -193,11 +187,11 @@ public interface IDeviceService extends IFloodlightService { * Short, Integer, Long, Integer) */ public Iterator<? extends IDevice> queryClassDevices(IEntityClass entityClass, - MacAddress macAddress, - VlanVid vlan, - IPv4Address ipv4Address, - DatapathId switchDPID, - OFPort switchPort); + Long macAddress, + Short vlan, + Integer ipv4Address, + Long switchDPID, + Integer switchPort); /** * Adds a listener to listen for IDeviceManagerServices notifications @@ -213,9 +207,9 @@ public interface IDeviceService extends IFloodlightService { * @param sw * @param port */ - public void addSuppressAPs(DatapathId swId, OFPort port); + public void addSuppressAPs(long swId, short port); - public void removeSuppressAPs(DatapathId swId, OFPort port); + public void removeSuppressAPs(long swId, short port); public Set<SwitchPort> getSuppressAPs(); diff --git a/src/main/java/net/floodlightcontroller/devicemanager/SwitchPort.java b/src/main/java/net/floodlightcontroller/devicemanager/SwitchPort.java index d057f9c4a353f0501631f6cf7725fd00d4be5b4e..9fe62d9507c0d2e575bebaa8bdcc86b8c15676a4 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/SwitchPort.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/SwitchPort.java @@ -17,8 +17,7 @@ package net.floodlightcontroller.devicemanager; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFPort; +import org.openflow.util.HexString; import net.floodlightcontroller.core.web.serializers.DPIDSerializer; @@ -56,8 +55,8 @@ public class SwitchPort { } } - private final DatapathId switchDPID; - private final OFPort port; + private final long switchDPID; + private final int port; private final ErrorStatus errorStatus; /** @@ -66,7 +65,7 @@ public class SwitchPort { * @param port the port * @param errorStatus any error status for the switch port */ - public SwitchPort(DatapathId switchDPID, OFPort port, ErrorStatus errorStatus) { + public SwitchPort(long switchDPID, int port, ErrorStatus errorStatus) { super(); this.switchDPID = switchDPID; this.port = port; @@ -78,7 +77,7 @@ public class SwitchPort { * @param switchDPID the dpid * @param port the port */ - public SwitchPort(DatapathId switchDPID, OFPort port) { + public SwitchPort(long switchDPID, int port) { super(); this.switchDPID = switchDPID; this.port = port; @@ -90,11 +89,11 @@ public class SwitchPort { // *************** @JsonSerialize(using=DPIDSerializer.class) - public DatapathId getSwitchDPID() { + public long getSwitchDPID() { return switchDPID; } - public OFPort getPort() { + public int getPort() { return port; } @@ -114,8 +113,8 @@ public class SwitchPort { + ((errorStatus == null) ? 0 : errorStatus.hashCode()); - result = prime * result + port.getPortNumber(); - result = prime * result + (int) (switchDPID.getLong() ^ (switchDPID.getLong() >>> 32)); + result = prime * result + port; + result = prime * result + (int) (switchDPID ^ (switchDPID >>> 32)); return result; } @@ -133,7 +132,7 @@ public class SwitchPort { @Override public String toString() { - return "SwitchPort [switchDPID=" + switchDPID.toString() + + return "SwitchPort [switchDPID=" + HexString.toHexString(switchDPID) + ", port=" + port + ", errorStatus=" + errorStatus + "]"; } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java index 46c2408faef824cb4ccf74c9dd69515037f70f3a..a08a3a5d2cb723582cf422b5ca988a22ac61e3f2 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java @@ -21,16 +21,11 @@ package net.floodlightcontroller.devicemanager.internal; -import java.util.Date; - -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFPort; - public class AttachmentPoint { - DatapathId sw; - OFPort port; - Date activeSince; - Date lastSeen; + long sw; + short port; + long activeSince; + long lastSeen; // Timeout for moving attachment points from OF/broadcast // domain to another. @@ -39,15 +34,15 @@ public class AttachmentPoint { public static final long OPENFLOW_TO_EXTERNAL_TIMEOUT = 30000; // 30 seconds public static final long CONSISTENT_TIMEOUT = 30000; // 30 seconds - public AttachmentPoint(DatapathId sw, OFPort port, Date activeSince, - Date lastSeen) { + public AttachmentPoint(long sw, short port, long activeSince, + long lastSeen) { this.sw = sw; this.port = port; this.activeSince = activeSince; this.lastSeen = lastSeen; } - public AttachmentPoint(DatapathId sw, OFPort port, Date lastSeen) { + public AttachmentPoint(long sw, short port, long lastSeen) { this.sw = sw; this.port = port; this.lastSeen = lastSeen; @@ -61,31 +56,31 @@ public class AttachmentPoint { this.lastSeen = ap.lastSeen; } - public DatapathId getSw() { + public long getSw() { return sw; } - public void setSw(DatapathId sw) { + public void setSw(long sw) { this.sw = sw; } - public OFPort getPort() { + public short getPort() { return port; } - public void setPort(OFPort port) { + public void setPort(short port) { this.port = port; } - public Date getActiveSince() { + public long getActiveSince() { return activeSince; } - public void setActiveSince(Date activeSince) { + public void setActiveSince(long activeSince) { this.activeSince = activeSince; } - public Date getLastSeen() { + public long getLastSeen() { return lastSeen; } - public void setLastSeen(Date lastSeen) { - if (this.lastSeen.getTime() + INACTIVITY_INTERVAL < lastSeen.getTime()) + public void setLastSeen(long lastSeen) { + if (this.lastSeen + INACTIVITY_INTERVAL < lastSeen) this.activeSince = lastSeen; - if (this.lastSeen.before(lastSeen)) + if (this.lastSeen < lastSeen) this.lastSeen = lastSeen; } @@ -96,8 +91,8 @@ public class AttachmentPoint { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + port.getPortNumber(); - result = prime * result + (int) (sw.getLong() ^ (sw.getLong() >>> 32)); + result = prime * result + port; + result = prime * result + (int) (sw ^ (sw >>> 32)); return result; } @@ -123,7 +118,7 @@ public class AttachmentPoint { @Override public String toString() { return "AttachmentPoint [sw=" + sw + ", port=" + port - + ", activeSince=" + activeSince + ", lastSeen=" + lastSeen.toString() + + ", activeSince=" + activeSince + ", lastSeen=" + lastSeen + "]"; } } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java index 07e5b77bfbacf879ce6b749fe7cbe811d1c90f85..ef0ba346809496090c83e273be35c5576699369c 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java @@ -30,11 +30,7 @@ import java.util.Map; import java.util.TreeSet; import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.types.VlanVid; -import org.projectfloodlight.openflow.util.HexString; +import org.openflow.util.HexString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,7 +61,7 @@ public class Device implements IDevice { protected final String macAddressString; // the vlan Ids from the entities of this device - protected final VlanVid[] vlanIds; + protected final Short[] vlanIds; protected volatile String dhcpClientName; /** @@ -107,7 +103,7 @@ public class Device implements IDevice { if (entity.getSwitchDPID() != null && entity.getSwitchPort() != null){ - DatapathId sw = entity.getSwitchDPID(); + long sw = entity.getSwitchDPID(); short port = entity.getSwitchPort().shortValue(); if (deviceManager.isValidAttachmentPoint(sw, port)) { @@ -213,29 +209,30 @@ public class Device implements IDevice { new ArrayList<AttachmentPoint>(device.attachmentPoints); } - this.macAddressString = this.entities[0].getMacAddress().toString(); + this.macAddressString = + HexString.toHexString(this.entities[0].getMacAddress(), 6); this.entityClass = device.entityClass; vlanIds = computeVlandIds(); } - private VlanVid[] computeVlandIds() { + private Short[] computeVlandIds() { if (entities.length == 1) { if (entities[0].getVlan() != null) { - return new VlanVid[]{ entities[0].getVlan() }; + return new Short[]{ entities[0].getVlan() }; } else { - return new VlanVid[] { VlanVid.ofVlan(-1) }; + return new Short[] { Short.valueOf((short)-1) }; } } - TreeSet<VlanVid> vals = new TreeSet<VlanVid>(); + TreeSet<Short> vals = new TreeSet<Short>(); for (Entity e : entities) { if (e.getVlan() == null) - vals.add(VlanVid.ofVlan(-1)); + vals.add((short)-1); else vals.add(e.getVlan()); } - return vals.toArray(new VlanVid[vals.size()]); + return vals.toArray(new Short[vals.size()]); } /** @@ -296,7 +293,7 @@ public class Device implements IDevice { if (apList == null) return false; for(AttachmentPoint ap: apList) { - if (ap.getLastSeen().getTime() + AttachmentPoint.INACTIVITY_INTERVAL < + if (ap.getLastSeen() + AttachmentPoint.INACTIVITY_INTERVAL < System.currentTimeMillis()) expiredAPs.add(ap); } @@ -335,8 +332,8 @@ public class Device implements IDevice { if (trueAP == null) continue; boolean c = (topology.isConsistent(trueAP.getSw(), trueAP.getPort(), ap.getSw(), ap.getPort())); - boolean active = (ap.getActiveSince().after(trueAP.getActiveSince())); - boolean last = ap.getLastSeen().getTime() > timeThreshold; + boolean active = (ap.getActiveSince() > trueAP.getActiveSince()); + boolean last = ap.getLastSeen() > timeThreshold; if (!c && active && last) { dupAPs.add(ap); } @@ -388,7 +385,7 @@ public class Device implements IDevice { * @param lastSeen * @return */ - protected boolean updateAttachmentPoint(DatapathId sw, OFPort port, Date lastSeen){ + protected boolean updateAttachmentPoint(long sw, short port, long lastSeen){ ITopologyService topology = deviceManager.topology; List<AttachmentPoint> oldAPList; List<AttachmentPoint> apList; diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java index d9a7a4532284bc30bc74bd2f8017ecca854bee89..2cbea66e5577495461171661f479c4f26335f11f 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java @@ -20,12 +20,6 @@ package net.floodlightcontroller.devicemanager.internal; import java.util.Arrays; import java.util.Iterator; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.IPv4Address; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.types.VlanVid; - import net.floodlightcontroller.devicemanager.IEntityClass; import net.floodlightcontroller.devicemanager.SwitchPort; import net.floodlightcontroller.util.FilterIterator; @@ -36,11 +30,11 @@ import net.floodlightcontroller.util.FilterIterator; public class DeviceIterator extends FilterIterator<Device> { private IEntityClass[] entityClasses; - private MacAddress macAddress; - private VlanVid vlan; - private IPv4Address ipv4Address; - private DatapathId switchDPID; - private OFPort switchPort; + private Long macAddress; + private Short vlan; + private Integer ipv4Address; + private Long switchDPID; + private Integer switchPort; /** * Construct a new device iterator over the key fields @@ -54,11 +48,11 @@ public class DeviceIterator extends FilterIterator<Device> { */ public DeviceIterator(Iterator<Device> subIterator, IEntityClass[] entityClasses, - MacAddress macAddress, - VlanVid vlan, - IPv4Address ipv4Address, - DatapathId switchDPID, - OFPort switchPort) { + Long macAddress, + Short vlan, + Integer ipv4Address, + Long switchDPID, + Integer switchPort) { super(subIterator); this.entityClasses = entityClasses; this.subIterator = subIterator; @@ -86,7 +80,7 @@ public class DeviceIterator extends FilterIterator<Device> { if (!match) return false; } if (macAddress != null) { - if (macAddress.getLong() != value.getMACAddress()) + if (macAddress.longValue() != value.getMACAddress()) return false; } if (vlan != null) { @@ -106,11 +100,11 @@ public class DeviceIterator extends FilterIterator<Device> { match = false; for (SwitchPort sp : sps) { if (switchDPID != null) { - if (switchDPID.getLong() != sp.getSwitchDPID().getLong()) + if (switchDPID.longValue() != sp.getSwitchDPID()) return false; } if (switchPort != null) { - if (switchPort.getPortNumber() != sp.getPort().getPortNumber()) + if (switchPort.intValue() != sp.getPort()) return false; } match = true; diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java index 7d34fc3f6d789dfdd9bb1c3ecd42369ab85a1244..e4f67420bed7489558a389dd6714c14b57451766 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java @@ -47,7 +47,7 @@ import net.floodlightcontroller.core.IHAListener; import net.floodlightcontroller.core.IInfoProvider; import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.HARole; +import net.floodlightcontroller.core.IFloodlightProviderService.Role; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.FloodlightModuleException; import net.floodlightcontroller.core.module.IFloodlightModule; @@ -96,15 +96,11 @@ import net.floodlightcontroller.util.MultiIterator; import static net.floodlightcontroller.devicemanager.internal. DeviceManagerImpl.DeviceUpdate.Change.*; -import org.projectfloodlight.openflow.protocol.OFMatchWithSwDpid; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPacketIn; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.IPv4Address; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.types.VlanVid; -import org.projectfloodlight.openflow.protocol.OFType; +import org.openflow.protocol.OFMatchWithSwDpid; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFPort; +import org.openflow.protocol.OFType; import org.sdnplatform.sync.IClosableIterator; import org.sdnplatform.sync.IStoreClient; import org.sdnplatform.sync.ISyncService; @@ -364,33 +360,33 @@ IFlowReconcileListener, IInfoProvider { public int compare(AttachmentPoint oldAP, AttachmentPoint newAP) { //First compare based on L2 domain ID; - DatapathId oldSw = oldAP.getSw(); - OFPort oldPort = oldAP.getPort(); - DatapathId oldDomain = topology.getL2DomainId(oldSw); + long oldSw = oldAP.getSw(); + short oldPort = oldAP.getPort(); + long oldDomain = topology.getL2DomainId(oldSw); boolean oldBD = topology.isBroadcastDomainPort(oldSw, oldPort); - DatapathId newSw = newAP.getSw(); - OFPort newPort = newAP.getPort(); - DatapathId newDomain = topology.getL2DomainId(newSw); + long newSw = newAP.getSw(); + short newPort = newAP.getPort(); + long newDomain = topology.getL2DomainId(newSw); boolean newBD = topology.isBroadcastDomainPort(newSw, newPort); - if (oldDomain.getLong() < newDomain.getLong()) return -1; - else if (oldDomain.getLong() > newDomain.getLong()) return 1; + if (oldDomain < newDomain) return -1; + else if (oldDomain > newDomain) return 1; - // Give preference to LOCAL always - if (oldPort != OFPort.LOCAL && - newPort == OFPort.LOCAL) { + // Give preference to OFPP_LOCAL always + if (oldPort != OFPort.OFPP_LOCAL.getValue() && + newPort == OFPort.OFPP_LOCAL.getValue()) { return -1; - } else if (oldPort == OFPort.LOCAL && - newPort != OFPort.LOCAL) { + } else if (oldPort == OFPort.OFPP_LOCAL.getValue() && + newPort != OFPort.OFPP_LOCAL.getValue()) { return 1; } // We expect that the last seen of the new AP is higher than // old AP, if it is not, just reverse and send the negative // of the result. - if (oldAP.getActiveSince().after(newAP.getActiveSince())) + if (oldAP.getActiveSince() > newAP.getActiveSince()) return -compare(newAP, oldAP); long activeOffset = 0; @@ -411,8 +407,8 @@ IFlowReconcileListener, IInfoProvider { } - if ((newAP.getActiveSince().getTime() > oldAP.getLastSeen().getTime() + activeOffset) || - (newAP.getLastSeen().getTime() > oldAP.getLastSeen().getTime() + + if ((newAP.getActiveSince() > oldAP.getLastSeen() + activeOffset) || + (newAP.getLastSeen() > oldAP.getLastSeen() + AttachmentPoint.INACTIVITY_INTERVAL)) { return -1; } @@ -457,13 +453,13 @@ IFlowReconcileListener, IInfoProvider { } @Override - public IDevice findDevice(MacAddress macAddress, VlanVid vlan, - IPv4Address ipv4Address, DatapathId switchDPID, - OFPort switchPort) + public IDevice findDevice(long macAddress, Short vlan, + Integer ipv4Address, Long switchDPID, + Integer switchPort) throws IllegalArgumentException { - if (vlan != null && vlan.getVlan() <= 0) + if (vlan != null && vlan.shortValue() <= 0) vlan = null; - if (ipv4Address != null && ipv4Address.getInt() == 0) + if (ipv4Address != null && ipv4Address == 0) ipv4Address = null; Entity e = new Entity(macAddress, vlan, ipv4Address, switchDPID, switchPort, null); @@ -475,12 +471,12 @@ IFlowReconcileListener, IInfoProvider { } @Override - public IDevice findClassDevice(IEntityClass entityClass, MacAddress macAddress, - VlanVid vlan, IPv4Address ipv4Address) + public IDevice findClassDevice(IEntityClass entityClass, long macAddress, + Short vlan, Integer ipv4Address) throws IllegalArgumentException { - if (vlan != null && vlan.getVlan() <= 0) + if (vlan != null && vlan.shortValue() <= 0) vlan = null; - if (ipv4Address != null && ipv4Address.getInt() == 0) + if (ipv4Address != null && ipv4Address == 0) ipv4Address = null; Entity e = new Entity(macAddress, vlan, ipv4Address, null, null, null); @@ -510,11 +506,11 @@ IFlowReconcileListener, IInfoProvider { } @Override - public Iterator<? extends IDevice> queryDevices(MacAddress macAddress, - VlanVid vlan, - IPv4Address ipv4Address, - DatapathId switchDPID, - OFPort switchPort) { + public Iterator<? extends IDevice> queryDevices(Long macAddress, + Short vlan, + Integer ipv4Address, + Long switchDPID, + Integer switchPort) { DeviceIndex index = null; if (secondaryIndexMap.size() > 0) { EnumSet<DeviceField> keys = @@ -529,7 +525,7 @@ IFlowReconcileListener, IInfoProvider { deviceIterator = deviceMap.values().iterator(); } else { // index lookup - Entity entity = new Entity(macAddress, + Entity entity = new Entity((macAddress == null ? 0 : macAddress), vlan, ipv4Address, switchDPID, @@ -552,11 +548,11 @@ IFlowReconcileListener, IInfoProvider { @Override public Iterator<? extends IDevice> queryClassDevices(IEntityClass entityClass, - MacAddress macAddress, - VlanVid vlan, - IPv4Address ipv4Address, - DatapathId switchDPID, - OFPort switchPort) { + Long macAddress, + Short vlan, + Integer ipv4Address, + Long switchDPID, + Integer switchPort) { ArrayList<Iterator<Device>> iterators = new ArrayList<Iterator<Device>>(); ClassState classState = getClassState(entityClass); @@ -585,7 +581,7 @@ IFlowReconcileListener, IInfoProvider { } else { // index lookup Entity entity = - new Entity(macAddress, + new Entity((macAddress == null ? 0 : macAddress), vlan, ipv4Address, switchDPID, @@ -599,11 +595,11 @@ IFlowReconcileListener, IInfoProvider { return new MultiIterator<Device>(iterators.iterator()); } - protected Iterator<Device> getDeviceIteratorForQuery(MacAddress macAddress, - VlanVid vlan, - IPv4Address ipv4Address, - DatapathId switchDPID, - OFPort switchPort) { + protected Iterator<Device> getDeviceIteratorForQuery(Long macAddress, + Short vlan, + Integer ipv4Address, + Long switchDPID, + Integer switchPort) { DeviceIndex index = null; if (secondaryIndexMap.size() > 0) { EnumSet<DeviceField> keys = @@ -618,7 +614,7 @@ IFlowReconcileListener, IInfoProvider { deviceIterator = deviceMap.values().iterator(); } else { // index lookup - Entity entity = new Entity(macAddress, + Entity entity = new Entity((macAddress == null ? 0 : macAddress), vlan, ipv4Address, switchDPID, @@ -646,12 +642,12 @@ IFlowReconcileListener, IInfoProvider { } @Override - public void addSuppressAPs(DatapathId swId, OFPort port) { + public void addSuppressAPs(long swId, short port) { suppressAPs.add(new SwitchPort(swId, port)); } @Override - public void removeSuppressAPs(DatapathId swId, OFPort port) { + public void removeSuppressAPs(long swId, short port) { suppressAPs.remove(new SwitchPort(swId, port)); } @@ -718,14 +714,14 @@ IFlowReconcileListener, IInfoProvider { } private void generateDeviceEvent(IDevice device, String reason) { - List<IPv4Address> ipv4Addresses = - new ArrayList<IPv4Address>(Arrays.asList(device.getIPv4Addresses())); + List<Integer> ipv4Addresses = + new ArrayList<Integer>(Arrays.asList(device.getIPv4Addresses())); List<SwitchPort> oldAps = new ArrayList<SwitchPort>(Arrays.asList(device.getOldAP())); List<SwitchPort> currentAps = new ArrayList<SwitchPort>(Arrays.asList(device.getAttachmentPoints())); - List<VlanVid> vlanIds = - new ArrayList<VlanVid>(Arrays.asList(device.getVlanId())); + List<Short> vlanIds = + new ArrayList<Short>(Arrays.asList(device.getVlanId())); evDevice.updateEventNoFlush( new DeviceEvent(device.getMACAddress(), @@ -775,7 +771,7 @@ IFlowReconcileListener, IInfoProvider { FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: - cntIncoming.increment(); + cntIncoming.updateCounterNoFlush(); return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); default: @@ -807,19 +803,19 @@ IFlowReconcileListener, IInfoProvider { } protected Command reconcileFlow(OFMatchReconcile ofm) { - cntReconcileRequest.increment(); + cntReconcileRequest.updateCounterNoFlush(); // Extract source entity information Entity srcEntity = getEntityFromFlowMod(ofm.ofmWithSwDpid, true); if (srcEntity == null) { - cntReconcileNoSource.increment(); + cntReconcileNoSource.updateCounterNoFlush(); return Command.STOP; } // Find the device by source entity Device srcDevice = findDeviceByEntity(srcEntity); if (srcDevice == null) { - cntReconcileNoSource.increment(); + cntReconcileNoSource.updateCounterNoFlush(); return Command.STOP; } // Store the source device in the context @@ -834,9 +830,9 @@ IFlowReconcileListener, IInfoProvider { if (dstDevice != null) fcStore.put(ofm.cntx, CONTEXT_DST_DEVICE, dstDevice); else - cntReconcileNoDest.increment(); + cntReconcileNoDest.updateCounterNoFlush(); } else { - cntReconcileNoDest.increment(); + cntReconcileNoDest.updateCounterNoFlush(); } if (logger.isTraceEnabled()) { logger.trace("Reconciling flow: match={}, srcEntity={}, srcDev={}, " @@ -1193,7 +1189,7 @@ IFlowReconcileListener, IInfoProvider { Entity srcEntity = getSourceEntityFromPacket(eth, sw.getId(), pi.getInPort()); if (srcEntity == null) { - cntInvalidSource.increment(); + cntInvalidSource.updateCounterNoFlush(); return Command.STOP; } @@ -1208,7 +1204,7 @@ IFlowReconcileListener, IInfoProvider { // Learn/lookup device information Device srcDevice = learnDeviceByEntity(srcEntity); if (srcDevice == null) { - cntNoSource.increment(); + cntNoSource.updateCounterNoFlush(); return Command.STOP; } @@ -1218,7 +1214,7 @@ IFlowReconcileListener, IInfoProvider { // Find the device matching the destination from the entity // classes of the source. if (eth.getDestinationMAC().toLong() == 0) { - cntInvalidDest.increment(); + cntInvalidDest.updateCounterNoFlush(); return Command.STOP; } Entity dstEntity = getDestEntityFromPacket(eth); @@ -1229,9 +1225,9 @@ IFlowReconcileListener, IInfoProvider { if (dstDevice != null) fcStore.put(cntx, CONTEXT_DST_DEVICE, dstDevice); else - cntNoDest.increment(); + cntNoDest.updateCounterNoFlush(); } else { - cntNoDest.increment(); + cntNoDest.updateCounterNoFlush(); } if (logger.isTraceEnabled()) { @@ -1266,7 +1262,7 @@ IFlowReconcileListener, IInfoProvider { DHCPOption dhcpOption = dhcp.getOption( DHCPOptionCode.OptionCode_Hostname); if (dhcpOption != null) { - cntDhcpClientNameSnooped.increment(); + cntDhcpClientNameSnooped.updateCounterNoFlush(); srcDevice.dhcpClientName = new String(dhcpOption.getData()); } } @@ -1318,22 +1314,23 @@ IFlowReconcileListener, IInfoProvider { * @return the entity from the packet */ protected Entity getSourceEntityFromPacket(Ethernet eth, - DatapathId swdpid, - OFPort port) { - MacAddress dlAddr = MacAddress.of(eth.getSourceMACAddress()); - + long swdpid, + int port) { + byte[] dlAddrArr = eth.getSourceMACAddress(); + long dlAddr = Ethernet.toLong(dlAddrArr); + // Ignore broadcast/multicast source - if (dlAddr.isBroadcast() || dlAddr.isMulticast()) + if ((dlAddrArr[0] & 0x1) != 0) return null; // Ignore 0 source mac - if (dlAddr.getLong() == 0) + if (dlAddr == 0) return null; - VlanVid vlan = VlanVid.ofVlan(eth.getVlanID()); - IPv4Address nwSrc = getSrcNwAddr(eth, dlAddr); + short vlan = eth.getVlanID(); + int nwSrc = getSrcNwAddr(eth, dlAddr); return new Entity(dlAddr, - vlan, - nwSrc, + ((vlan >= 0) ? vlan : null), + ((nwSrc != 0) ? nwSrc : null), swdpid, port, new Date()); @@ -1345,8 +1342,8 @@ IFlowReconcileListener, IInfoProvider { * address in ARP data. */ protected void learnDeviceFromArpResponseData(Ethernet eth, - DatapathId swdpid, - OFPort port) { + long swdpid, + int port) { if (!(eth.getPayload() instanceof ARP)) return; ARP arp = (ARP) eth.getPayload(); @@ -1615,8 +1612,8 @@ IFlowReconcileListener, IInfoProvider { // attachment point port. Otherwise ignore. if (entity.hasSwitchPort() && !topology.isAttachmentPointPort(entity.getSwitchDPID(), - entity.getSwitchPort())) { - cntDeviceOnInternalPortNotLearned.increment(); + entity.getSwitchPort().shortValue())) { + cntDeviceOnInternalPortNotLearned.updateCounterNoFlush(); if (logger.isDebugEnabled()) { logger.debug("Not learning new device on internal" + " link: {}", entity); @@ -1627,7 +1624,7 @@ IFlowReconcileListener, IInfoProvider { // Before we create the new device also check if // the entity is allowed (e.g., for spoofing protection) if (!isEntityAllowed(entity, entityClass)) { - cntPacketNotAllowed.increment(); + cntPacketNotAllowed.updateCounterNoFlush(); if (logger.isDebugEnabled()) { logger.debug("PacketIn is not allowed {} {}", entityClass.getName(), entity); @@ -1655,7 +1652,7 @@ IFlowReconcileListener, IInfoProvider { // We need to count and log here. If we log earlier we could // hit a concurrent modification and restart the dev creation // and potentially count the device twice. - cntNewDevice.increment(); + cntNewDevice.updateCounterNoFlush(); if (logger.isDebugEnabled()) { logger.debug("New device created: {} deviceKey={}, entity={}", new Object[]{device, deviceKey, entity}); @@ -1669,7 +1666,7 @@ IFlowReconcileListener, IInfoProvider { } // if it gets here, we have a pre-existing Device for this Entity if (!isEntityAllowed(entity, device.getEntityClass())) { - cntPacketNotAllowed.increment(); + cntPacketNotAllowed.updateCounterNoFlush(); if (logger.isDebugEnabled()) { logger.info("PacketIn is not allowed {} {}", device.getEntityClass().getName(), entity); @@ -1681,8 +1678,8 @@ IFlowReconcileListener, IInfoProvider { // the chain. if (entity.hasSwitchPort() && !topology.isAttachmentPointPort(entity.getSwitchDPID(), - entity.getSwitchPort())) { - cntPacketOnInternalPortForKnownDevice.increment(); + entity.getSwitchPort().shortValue())) { + cntPacketOnInternalPortForKnownDevice.updateCounterNoFlush(); break; } int entityindex = -1; @@ -1726,9 +1723,9 @@ IFlowReconcileListener, IInfoProvider { // We need to count here after all the possible "continue" // statements in this branch - cntNewEntity.increment(); + cntNewEntity.updateCounterNoFlush(); if (changedFields.size() > 0) { - cntDeviceChanged.increment(); + cntDeviceChanged.updateCounterNoFlush(); deviceUpdates = updateUpdates(deviceUpdates, new DeviceUpdate(newDevice, CHANGE, @@ -1741,8 +1738,8 @@ IFlowReconcileListener, IInfoProvider { if (entity.hasSwitchPort()) { boolean moved = device.updateAttachmentPoint(entity.getSwitchDPID(), - entity.getSwitchPort(), - entity.getLastSeenTimestamp()); + entity.getSwitchPort().shortValue(), + entity.getLastSeenTimestamp().getTime()); // TODO: use update mechanism instead of sending the // notification directly if (moved) { @@ -1989,7 +1986,7 @@ IFlowReconcileListener, IInfoProvider { * Clean up expired entities/devices */ protected void cleanupEntities () { - cntCleanupEntitiesRuns.increment(); + cntCleanupEntitiesRuns.updateCounterWithFlush(); Calendar c = Calendar.getInstance(); c.add(Calendar.MILLISECOND, -ENTITY_TIMEOUT); @@ -2022,7 +2019,7 @@ IFlowReconcileListener, IInfoProvider { break; } - cntEntityRemovedTimeout.increment(); + cntEntityRemovedTimeout.updateCounterWithFlush(); for (Entity e : toRemove) { removeEntity(e, d.getEntityClass(), d.getDeviceKey(), toKeep); } @@ -2058,7 +2055,7 @@ IFlowReconcileListener, IInfoProvider { if (update != null) { // need to count after all possibly continue stmts in // this branch - cntDeviceChanged.increment(); + cntDeviceChanged.updateCounterWithFlush(); deviceUpdates.add(update); } } else { @@ -2070,7 +2067,7 @@ IFlowReconcileListener, IInfoProvider { d = deviceMap.get(d.getDeviceKey()); if (null != d) continue; - cntDeviceDeleted.increment(); + cntDeviceDeleted.updateCounterWithFlush(); } deviceUpdates.add(update); } @@ -2128,11 +2125,11 @@ IFlowReconcileListener, IInfoProvider { } } - private EnumSet<DeviceField> getEntityKeys(MacAddress macAddress, - VlanVid vlan, - IPv4Address ipv4Address, - DatapathId switchDPID, - OFPort switchPort) { + private EnumSet<DeviceField> getEntityKeys(Long macAddress, + Short vlan, + Integer ipv4Address, + Long switchDPID, + Integer switchPort) { // FIXME: vlan==null is a valid search. Need to handle this // case correctly. Note that the code will still work correctly. // But we might do a full device search instead of using an index. @@ -2186,8 +2183,8 @@ IFlowReconcileListener, IInfoProvider { for (Entity entity : entities) { if (entity.switchDPID != null && entity.switchPort != null) { AttachmentPoint aP = - new AttachmentPoint(entity.switchDPID, - entity.switchPort, new Date(0)); + new AttachmentPoint(entity.switchDPID.longValue(), + entity.switchPort.shortValue(), 0); newPossibleAPs.add(aP); } } @@ -2244,7 +2241,7 @@ IFlowReconcileListener, IInfoProvider { * @param updates the updates to process. */ protected void sendDeviceMovedNotification(Device d) { - cntDeviceMoved.increment(); + cntDeviceMoved.updateCounterNoFlush(); deviceSyncManager.storeDevice(d); List<IDeviceListener> listeners = deviceListeners.getOrderedListeners(); if (listeners != null) { @@ -2303,7 +2300,7 @@ IFlowReconcileListener, IInfoProvider { return false; } - cntDeviceReclassifyDelete.increment(); + cntDeviceReclassifyDelete.updateCounterNoFlush(); LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>(); // delete this device and then re-learn all the entities @@ -2388,7 +2385,7 @@ IFlowReconcileListener, IInfoProvider { writeUpdatedDeviceToStorage(d); lastWriteTimes.put(d.getDeviceKey(), now); } else { - cntDeviceStoreThrottled.increment(); + cntDeviceStoreThrottled.updateCounterWithFlush(); } } @@ -2409,12 +2406,12 @@ IFlowReconcileListener, IInfoProvider { // TODO: should probably do versioned delete. OTOH, even // if we accidentally delete, we'll write it again after // the next entity .... - cntDeviceRemovedFromStore.increment(); + cntDeviceRemovedFromStore.updateCounterWithFlush(); storeClient.delete(DeviceSyncRepresentation.computeKey(d)); } catch(ObsoleteVersionException e) { // FIXME } catch (SyncException e) { - cntSyncException.increment(); + cntSyncException.updateCounterWithFlush(); logger.error("Could not remove device " + d + " from store", e); } } @@ -2426,14 +2423,14 @@ IFlowReconcileListener, IInfoProvider { */ private void removeDevice(Versioned<DeviceSyncRepresentation> dev) { try { - cntDeviceRemovedFromStore.increment(); + cntDeviceRemovedFromStore.updateCounterWithFlush(); storeClient.delete(dev.getValue().getKey(), dev.getVersion()); } catch(ObsoleteVersionException e) { // Key was locally modified by another thread. // Do not delete and ignore. } catch(SyncException e) { - cntSyncException.increment(); + cntSyncException.updateCounterWithFlush(); logger.error("Failed to remove device entry for " + dev.toString() + " from store.", e); } @@ -2447,13 +2444,13 @@ IFlowReconcileListener, IInfoProvider { if (logger.isDebugEnabled()) { logger.debug("Transitioning to MASTER role"); } - cntTransitionToMaster.increment(); + cntTransitionToMaster.updateCounterWithFlush(); IClosableIterator<Map.Entry<String,Versioned<DeviceSyncRepresentation>>> iter = null; try { iter = storeClient.entries(); } catch (SyncException e) { - cntSyncException.increment(); + cntSyncException.updateCounterWithFlush(); logger.error("Failed to read devices from sync store", e); return; } @@ -2465,7 +2462,7 @@ IFlowReconcileListener, IInfoProvider { versionedDevice.getValue(); if (storedDevice == null) continue; - cntDevicesFromStore.increment(); + cntDevicesFromStore.updateCounterWithFlush(); for(SyncEntity se: storedDevice.getEntities()) { learnDeviceByEntity(se.asEntity()); } @@ -2485,7 +2482,7 @@ IFlowReconcileListener, IInfoProvider { */ private void writeUpdatedDeviceToStorage(Device device) { try { - cntDeviceStrored.increment(); + cntDeviceStrored.updateCounterWithFlush(); // FIXME: use a versioned put DeviceSyncRepresentation storeDevice = new DeviceSyncRepresentation(device); @@ -2494,7 +2491,7 @@ IFlowReconcileListener, IInfoProvider { // FIXME: what's the right behavior here. Can the store client // even throw this error? } catch (SyncException e) { - cntSyncException.increment(); + cntSyncException.updateCounterWithFlush(); logger.error("Could not write device " + device + " to sync store:", e); } @@ -2518,7 +2515,7 @@ IFlowReconcileListener, IInfoProvider { private void consolidateStore() { if (!isMaster) return; - cntConsolidateStoreRuns.increment(); + cntConsolidateStoreRuns.updateCounterWithFlush(); if (logger.isDebugEnabled()) { logger.debug("Running consolidateStore."); } @@ -2527,7 +2524,7 @@ IFlowReconcileListener, IInfoProvider { try { iter = storeClient.entries(); } catch (SyncException e) { - cntSyncException.increment(); + cntSyncException.updateCounterWithFlush(); logger.error("Failed to read devices from sync store", e); return; } @@ -2564,7 +2561,7 @@ IFlowReconcileListener, IInfoProvider { + "corresponding live device", storedDevice.getKey()); } - cntConsolidateStoreDevicesRemoved.increment(); + cntConsolidateStoreDevicesRemoved.updateCounterWithFlush(); removeDevice(versionedDevice); } } @@ -2599,24 +2596,24 @@ IFlowReconcileListener, IInfoProvider { */ private class DeviceEvent { @EventColumn(name = "MAC", description = EventFieldType.MAC) - private final MacAddress macAddress; - @EventColumn(name = "IPs", description = EventFieldType.IPv4) - private final List<IPv4Address> ipv4Addresses; + private final long macAddress; + @EventColumn(name = "IPs", description = EventFieldType.LIST_IPV4) + private final List<Integer> ipv4Addresses; @EventColumn(name = "Old Attachment Points", - description = EventFieldType.COLLECTION_ATTACHMENT_POINT) + description = EventFieldType.LIST_ATTACHMENT_POINT) private final List<SwitchPort> oldAttachmentPoints; @EventColumn(name = "Current Attachment Points", - description = EventFieldType.COLLECTION_ATTACHMENT_POINT) + description = EventFieldType.LIST_ATTACHMENT_POINT) private final List<SwitchPort> currentAttachmentPoints; - @EventColumn(name = "VLAN IDs", description = EventFieldType.COLLECTION_OBJECT) - private final List<VlanVid> vlanIds; + @EventColumn(name = "VLAN IDs", description = EventFieldType.LIST_OBJECT) + private final List<Short> vlanIds; @EventColumn(name = "Reason", description = EventFieldType.STRING) private final String reason; - public DeviceEvent(MacAddress macAddress, List<IPv4Address> ipv4Addresses, + public DeviceEvent(long macAddress, List<Integer> ipv4Addresses, List<SwitchPort> oldAttachmentPoints, List<SwitchPort> currentAttachmentPoints, - List<VlanVid> vlanIds, String reason) { + List<Short> vlanIds, String reason) { super(); this.macAddress = macAddress; this.ipv4Addresses = ipv4Addresses; diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java index b1c395d4c9664553550685125f686bdf87508283..42bfb01321cfc704d9578672630893bff31d4e0e 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java @@ -6,14 +6,9 @@ import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.List; +import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.IPv4Address; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.types.VlanVid; -import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField; import net.floodlightcontroller.devicemanager.SwitchPort; import com.fasterxml.jackson.annotation.JsonProperty; @@ -21,15 +16,15 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class DeviceSyncRepresentation { public static class SyncEntity implements Comparable<SyncEntity> { @JsonProperty - public MacAddress macAddress; + public long macAddress; @JsonProperty - public IPv4Address ipv4Address; + public Integer ipv4Address; @JsonProperty - public VlanVid vlan; + public Short vlan; @JsonProperty - public DatapathId switchDPID; + public Long switchDPID; @JsonProperty - public OFPort switchPort; + public Integer switchPort; @JsonProperty public Date lastSeenTimestamp; @JsonProperty diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java index 39a8d79cd7c67b8e08522943bc97055b1d45745d..db8d8e9fb3ff92256e0e9db55751e9345e6765fa 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java @@ -22,15 +22,11 @@ import java.util.Date; import net.floodlightcontroller.core.web.serializers.IPv4Serializer; import net.floodlightcontroller.core.web.serializers.MACSerializer; import net.floodlightcontroller.core.web.serializers.DPIDSerializer; +import net.floodlightcontroller.packet.IPv4; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.IPv4Address; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.types.VlanVid; +import org.openflow.util.HexString; /** * An entity on the network is a visible trace of a device that corresponds @@ -56,30 +52,30 @@ public class Entity implements Comparable<Entity> { /** * The MAC address associated with this entity */ - protected MacAddress macAddress; + protected long macAddress; /** * The IP address associated with this entity, or null if no IP learned * from the network observation associated with this entity */ - protected IPv4Address ipv4Address; + protected Integer ipv4Address; /** * The VLAN tag on this entity, or null if untagged */ - protected VlanVid vlan; + protected Short vlan; /** * The DPID of the switch for the ingress point for this entity, * or null if not present */ - protected DatapathId switchDPID; + protected Long switchDPID; /** * The port number of the switch for the ingress point for this entity, * or null if not present */ - protected OFPort switchPort; + protected Integer switchPort; /** * The last time we observed this entity on the network @@ -112,8 +108,8 @@ public class Entity implements Comparable<Entity> { * @param switchPort * @param lastSeenTimestamp */ - public Entity(MacAddress macAddress, VlanVid vlan, - IPv4Address ipv4Address, DatapathId switchDPID, OFPort switchPort, + public Entity(long macAddress, Short vlan, + Integer ipv4Address, Long switchDPID, Integer switchPort, Date lastSeenTimestamp) { this.macAddress = macAddress; this.ipv4Address = ipv4Address; @@ -129,25 +125,25 @@ public class Entity implements Comparable<Entity> { // *************** @JsonSerialize(using=MACSerializer.class) - public MacAddress getMacAddress() { + public long getMacAddress() { return macAddress; } @JsonSerialize(using=IPv4Serializer.class) - public IPv4Address getIpv4Address() { + public Integer getIpv4Address() { return ipv4Address; } - public VlanVid getVlan() { + public Short getVlan() { return vlan; } @JsonSerialize(using=DPIDSerializer.class) - public DatapathId getSwitchDPID() { + public Long getSwitchDPID() { return switchDPID; } - public OFPort getSwitchPort() { + public Integer getSwitchPort() { return switchPort; } @@ -189,7 +185,7 @@ public class Entity implements Comparable<Entity> { hashCode = 1; hashCode = prime * hashCode + ((ipv4Address == null) ? 0 : ipv4Address.hashCode()); - hashCode = prime * hashCode + (int) (macAddress.getLong() ^ (macAddress.getLong() >>> 32)); + hashCode = prime * hashCode + (int) (macAddress ^ (macAddress >>> 32)); hashCode = prime * hashCode + ((switchDPID == null) ? 0 : switchDPID.hashCode()); hashCode = prime * hashCode @@ -227,15 +223,16 @@ public class Entity implements Comparable<Entity> { public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Entity [macAddress="); - builder.append(macAddress.toString()); + builder.append(HexString.toHexString(macAddress, 6)); builder.append(", ipv4Address="); - builder.append(ipv4Address.toString()); + builder.append(IPv4.fromIPv4Address(ipv4Address==null ? + 0 : ipv4Address.intValue())); builder.append(", vlan="); - builder.append(vlan.getVlan()); + builder.append(vlan); builder.append(", switchDPID="); - builder.append(switchDPID.toString()); + builder.append(switchDPID); builder.append(", switchPort="); - builder.append(switchPort.getPortNumber()); + builder.append(switchPort); builder.append(", lastSeenTimestamp="); builder.append(lastSeenTimestamp == null? "null" : lastSeenTimestamp.getTime()); builder.append(", activeSince="); @@ -246,8 +243,8 @@ public class Entity implements Comparable<Entity> { @Override public int compareTo(Entity o) { - if (macAddress.getLong() < o.macAddress.getLong()) return -1; - if (macAddress.getLong() > o.macAddress.getLong()) return 1; + if (macAddress < o.macAddress) return -1; + if (macAddress > o.macAddress) return 1; int r; if (switchDPID == null) diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java index a1e7160b2b4987ffa2d87a07b3d6367a191ba43c..13a78a49accfa001c51fb3260325179abc362a46 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java @@ -89,8 +89,8 @@ public class IndexedEntity { switch (f) { case MAC: hashCode = prime * hashCode - + (int) (entity.macAddress.getLong() ^ - (entity.macAddress.getLong() >>> 32)); + + (int) (entity.macAddress ^ + (entity.macAddress >>> 32)); break; case IPV4: hashCode = prime * hashCode diff --git a/src/main/java/net/floodlightcontroller/firewall/Firewall.java b/src/main/java/net/floodlightcontroller/firewall/Firewall.java index 6b6b5578f06dbe18416fc0eb166a7245dd076f4f..21dc8aed6ca937f3123e9a00971ee8b0fc13fd80 100644 --- a/src/main/java/net/floodlightcontroller/firewall/Firewall.java +++ b/src/main/java/net/floodlightcontroller/firewall/Firewall.java @@ -24,16 +24,9 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPacketIn; -import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.EthType; -import org.projectfloodlight.openflow.types.IPv4AddressWithMask; -import org.projectfloodlight.openflow.types.IpProtocol; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.types.TransportPort; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFType; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IOFMessageListener; @@ -42,11 +35,11 @@ import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.FloodlightModuleException; import net.floodlightcontroller.core.module.IFloodlightModule; import net.floodlightcontroller.core.module.IFloodlightService; + import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.devicemanager.IDeviceService; import java.util.ArrayList; - import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPv4; import net.floodlightcontroller.restserver.IRestApiService; @@ -191,7 +184,7 @@ public class Firewall implements IFirewallService, IOFMessageListener, try { r.ruleid = Integer .parseInt((String) row.get(COLUMN_RULEID)); - r.dpid = DatapathId.of((String) row.get(COLUMN_DPID)); + r.dpid = Long.parseLong((String) row.get(COLUMN_DPID)); for (String key : row.keySet()) { if (row.get(key) == null) @@ -203,58 +196,58 @@ public class Firewall implements IFirewallService, IOFMessageListener, } else if (key.equals(COLUMN_IN_PORT)) { - r.in_port = OFPort.of(Integer.parseInt((String) row - .get(COLUMN_IN_PORT))); + r.in_port = Short.parseShort((String) row + .get(COLUMN_IN_PORT)); } else if (key.equals(COLUMN_DL_SRC)) { - r.dl_src = MacAddress.of(Long.parseLong((String) row - .get(COLUMN_DL_SRC))); + r.dl_src = Long.parseLong((String) row + .get(COLUMN_DL_SRC)); } else if (key.equals(COLUMN_DL_DST)) { - r.dl_dst = MacAddress.of(Long.parseLong((String) row - .get(COLUMN_DL_DST))); + r.dl_dst = Long.parseLong((String) row + .get(COLUMN_DL_DST)); } else if (key.equals(COLUMN_DL_TYPE)) { - r.dl_type = EthType.of(Integer.parseInt((String) row - .get(COLUMN_DL_TYPE))); + r.dl_type = Short.parseShort((String) row + .get(COLUMN_DL_TYPE)); } else if (key.equals(COLUMN_NW_SRC_PREFIX)) { - r.nw_src_prefix_and_mask = IPv4AddressWithMask.of(Integer.parseInt((String) row - .get(COLUMN_NW_SRC_PREFIX)), r.nw_src_prefix_and_mask.getMask().getInt()); + r.nw_src_prefix = Integer.parseInt((String) row + .get(COLUMN_NW_SRC_PREFIX)); } else if (key.equals(COLUMN_NW_SRC_MASKBITS)) { - r.nw_src_prefix_and_mask = IPv4AddressWithMask.of(r.nw_src_prefix_and_mask.getValue().getInt(), Integer.parseInt((String) row - .get(COLUMN_NW_SRC_MASKBITS))); + r.nw_src_maskbits = Integer.parseInt((String) row + .get(COLUMN_NW_SRC_MASKBITS)); } else if (key.equals(COLUMN_NW_DST_PREFIX)) { - r.nw_dst_prefix_and_mask = IPv4AddressWithMask.of(Integer.parseInt((String) row - .get(COLUMN_NW_DST_PREFIX)), r.nw_dst_prefix_and_mask.getMask().getInt()); + r.nw_dst_prefix = Integer.parseInt((String) row + .get(COLUMN_NW_DST_PREFIX)); } else if (key.equals(COLUMN_NW_DST_MASKBITS)) { - r.nw_dst_prefix_and_mask = IPv4AddressWithMask.of(r.nw_dst_prefix_and_mask.getValue().getInt(), Integer.parseInt((String) row - .get(COLUMN_NW_DST_MASKBITS))); + r.nw_dst_maskbits = Integer.parseInt((String) row + .get(COLUMN_NW_DST_MASKBITS)); } else if (key.equals(COLUMN_NW_PROTO)) { - r.nw_proto = IpProtocol.of(Short.parseShort((String) row - .get(COLUMN_NW_PROTO))); + r.nw_proto = Short.parseShort((String) row + .get(COLUMN_NW_PROTO)); } else if (key.equals(COLUMN_TP_SRC)) { - r.tp_src = TransportPort.of(Integer.parseInt((String) row - .get(COLUMN_TP_SRC))); + r.tp_src = Short.parseShort((String) row + .get(COLUMN_TP_SRC)); } else if (key.equals(COLUMN_TP_DST)) { - r.tp_dst = TransportPort.of(Integer.parseInt((String) row - .get(COLUMN_TP_DST))); + r.tp_dst = Short.parseShort((String) row + .get(COLUMN_TP_DST)); } else if (key.equals(COLUMN_WILDCARD_DPID)) { @@ -459,18 +452,18 @@ public class Firewall implements IFirewallService, IOFMessageListener, // add rule to database Map<String, Object> entry = new HashMap<String, Object>(); entry.put(COLUMN_RULEID, Integer.toString(rule.ruleid)); - entry.put(COLUMN_DPID, Long.toString(rule.dpid.getLong())); - entry.put(COLUMN_IN_PORT, Integer.toString(rule.in_port.getPortNumber())); - entry.put(COLUMN_DL_SRC, Long.toString(rule.dl_src.getLong())); - entry.put(COLUMN_DL_DST, Long.toString(rule.dl_dst.getLong())); - entry.put(COLUMN_DL_TYPE, Integer.toString(rule.dl_type.getValue())); - entry.put(COLUMN_NW_SRC_PREFIX, Integer.toString(rule.nw_src_prefix_and_mask.getValue().getInt())); - entry.put(COLUMN_NW_SRC_MASKBITS, Integer.toString(rule.nw_src_prefix_and_mask.getMask().getInt())); - entry.put(COLUMN_NW_DST_PREFIX, Integer.toString(rule.nw_dst_prefix_and_mask.getValue().getInt())); - entry.put(COLUMN_NW_DST_MASKBITS, Integer.toString(rule.nw_dst_prefix_and_mask.getMask().getInt())); - entry.put(COLUMN_NW_PROTO, Short.toString(rule.nw_proto.getIpProtocolNumber())); - entry.put(COLUMN_TP_SRC, Integer.toString(rule.tp_src.getPort())); - entry.put(COLUMN_TP_DST, Integer.toString(rule.tp_dst.getPort())); + entry.put(COLUMN_DPID, Long.toString(rule.dpid)); + entry.put(COLUMN_IN_PORT, Short.toString(rule.in_port)); + entry.put(COLUMN_DL_SRC, Long.toString(rule.dl_src)); + entry.put(COLUMN_DL_DST, Long.toString(rule.dl_dst)); + entry.put(COLUMN_DL_TYPE, Short.toString(rule.dl_type)); + entry.put(COLUMN_NW_SRC_PREFIX, Integer.toString(rule.nw_src_prefix)); + entry.put(COLUMN_NW_SRC_MASKBITS, Integer.toString(rule.nw_src_maskbits)); + entry.put(COLUMN_NW_DST_PREFIX, Integer.toString(rule.nw_dst_prefix)); + entry.put(COLUMN_NW_DST_MASKBITS, Integer.toString(rule.nw_dst_maskbits)); + entry.put(COLUMN_NW_PROTO, Short.toString(rule.nw_proto)); + entry.put(COLUMN_TP_SRC, Integer.toString(rule.tp_src)); + entry.put(COLUMN_TP_DST, Integer.toString(rule.tp_dst)); entry.put(COLUMN_WILDCARD_DPID, Boolean.toString(rule.wildcard_dpid)); entry.put(COLUMN_WILDCARD_IN_PORT, diff --git a/src/main/java/net/floodlightcontroller/firewall/FirewallRule.java b/src/main/java/net/floodlightcontroller/firewall/FirewallRule.java index bee8fddef5942d16806c3cef144ded5a01dcfd3b..f457ce8d9e85510f91fdefe2bb2f3ac84276eec4 100644 --- a/src/main/java/net/floodlightcontroller/firewall/FirewallRule.java +++ b/src/main/java/net/floodlightcontroller/firewall/FirewallRule.java @@ -18,15 +18,7 @@ package net.floodlightcontroller.firewall; import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.projectfloodlight.openflow.protocol.match.MatchField; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.EthType; -import org.projectfloodlight.openflow.types.IPv4AddressWithMask; -import org.projectfloodlight.openflow.types.IpProtocol; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.types.TransportPort; +import org.openflow.protocol.OFMatch; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPacket; @@ -38,18 +30,18 @@ import net.floodlightcontroller.packet.UDP; public class FirewallRule implements Comparable<FirewallRule> { public int ruleid; - public DatapathId dpid; - public OFPort in_port; - public MacAddress dl_src; - public MacAddress dl_dst; - public EthType dl_type; - public IPv4AddressWithMask nw_src_prefix_and_mask; - //public int nw_src_maskbits; - public IPv4AddressWithMask nw_dst_prefix_and_mask; - //public int nw_dst_maskbits; - public IpProtocol nw_proto; - public TransportPort tp_src; - public TransportPort tp_dst; + public long dpid; + public short in_port; + public long dl_src; + public long dl_dst; + public short dl_type; + public int nw_src_prefix; + public int nw_src_maskbits; + public int nw_dst_prefix; + public int nw_dst_maskbits; + public short nw_proto; + public short tp_src; + public short tp_dst; public boolean wildcard_dpid; public boolean wildcard_in_port; @@ -75,18 +67,18 @@ public class FirewallRule implements Comparable<FirewallRule> { } public FirewallRule() { - this.in_port = OFPort.ZERO; - this.dl_src = MacAddress.NONE; - this.nw_src_prefix_and_mask = IPv4AddressWithMask.NONE; - //this.nw_src_maskbits = 0; - this.dl_dst = MacAddress.NONE; - this.nw_proto = IpProtocol.NONE; - this.tp_src = TransportPort.NONE; - this.tp_dst = TransportPort.NONE; - this.dl_dst = MacAddress.NONE; - this.nw_dst_prefix_and_mask = IPv4AddressWithMask.NONE; - //this.nw_dst_maskbits = 0; - this.dpid = DatapathId.NONE; + this.in_port = 0; + this.dl_src = 0; + this.nw_src_prefix = 0; + this.nw_src_maskbits = 0; + this.dl_dst = 0; + this.nw_proto = 0; + this.tp_src = 0; + this.tp_dst = 0; + this.dl_dst = 0; + this.nw_dst_prefix = 0; + this.nw_dst_maskbits = 0; + this.dpid = -1; this.wildcard_dpid = true; this.wildcard_in_port = true; this.wildcard_dl_src = true; @@ -96,7 +88,7 @@ public class FirewallRule implements Comparable<FirewallRule> { this.wildcard_nw_dst = true; this.wildcard_nw_proto = true; this.wildcard_tp_src = true; - this.wildcard_tp_dst = true; + this.wildcard_tp_dst = true; this.priority = 0; this.action = FirewallAction.ALLOW; this.ruleid = 0; @@ -140,23 +132,23 @@ public class FirewallRule implements Comparable<FirewallRule> { public boolean isSameAs(FirewallRule r) { if (this.action != r.action || this.wildcard_dl_type != r.wildcard_dl_type - || (this.wildcard_dl_type == false && !this.dl_type.equals(r.dl_type)) + || (this.wildcard_dl_type == false && this.dl_type != r.dl_type) || this.wildcard_tp_src != r.wildcard_tp_src - || (this.wildcard_tp_src == false && !this.tp_src.equals(r.tp_src)) + || (this.wildcard_tp_src == false && this.tp_src != r.tp_src) || this.wildcard_tp_dst != r.wildcard_tp_dst - || (this.wildcard_tp_dst == false && !this.tp_dst.equals(r.tp_dst)) + || (this.wildcard_tp_dst == false &&this.tp_dst != r.tp_dst) || this.wildcard_dpid != r.wildcard_dpid - || (this.wildcard_dpid == false && !this.dpid.equals(r.dpid)) + || (this.wildcard_dpid == false && this.dpid != r.dpid) || this.wildcard_in_port != r.wildcard_in_port - || (this.wildcard_in_port == false && !this.in_port.equals(r.in_port)) + || (this.wildcard_in_port == false && this.in_port != r.in_port) || this.wildcard_nw_src != r.wildcard_nw_src - || (this.wildcard_nw_src == false && !this.nw_src_prefix_and_mask.equals(r.nw_src_prefix_and_mask)) + || (this.wildcard_nw_src == false && (this.nw_src_prefix != r.nw_src_prefix || this.nw_src_maskbits != r.nw_src_maskbits)) || this.wildcard_dl_src != r.wildcard_dl_src - || (this.wildcard_dl_src == false && !this.dl_src.equals(r.dl_src)) + || (this.wildcard_dl_src == false && this.dl_src != r.dl_src) || this.wildcard_nw_proto != r.wildcard_nw_proto - || (this.wildcard_nw_proto == false && !this.nw_proto.equals(r.nw_proto)) + || (this.wildcard_nw_proto == false && this.nw_proto != r.nw_proto) || this.wildcard_nw_dst != r.wildcard_nw_dst - || (this.wildcard_nw_dst == false && !this.nw_dst_prefix_and_mask.equals(r.nw_dst_prefix_and_mask)) + || (this.wildcard_nw_dst == false && (this.nw_dst_prefix != r.nw_dst_prefix || this.nw_dst_maskbits != r.nw_dst_maskbits)) || this.wildcard_dl_dst != r.wildcard_dl_dst || (this.wildcard_dl_dst == false && this.dl_dst != r.dl_dst)) { return false; @@ -179,7 +171,7 @@ public class FirewallRule implements Comparable<FirewallRule> { * method to derive wildcards for the decision to be taken * @return true if the rule matches the given packet-in, false otherwise */ - public boolean matchesFlow(DatapathId switchDpid, OFPort inPort, Ethernet packet, + public boolean matchesFlow(long switchDpid, short inPort, Ethernet packet, WildcardsPair wildcards) { IPacket pkt = packet.getPayload(); @@ -195,39 +187,35 @@ public class FirewallRule implements Comparable<FirewallRule> { short pkt_tp_dst = 0; // switchID matches? - if (wildcard_dpid == false && !dpid.equals(switchDpid)) + if (wildcard_dpid == false && dpid != switchDpid) return false; // in_port matches? - if (wildcard_in_port == false && !in_port.equals(inPort)) + if (wildcard_in_port == false && in_port != inPort) return false; if (action == FirewallRule.FirewallAction.DENY) { - //wildcards.drop &= ~OFMatch.OFPFW_IN_PORT; - wildcards.drop.setExact(MatchField.IN_PORT, this.in_port); + wildcards.drop &= ~OFMatch.OFPFW_IN_PORT; } else { - //wildcards.allow &= ~OFMatch.OFPFW_IN_PORT; - wildcards.allow.setExact(MatchField.IN_PORT, this.in_port); + wildcards.allow &= ~OFMatch.OFPFW_IN_PORT; } // mac address (src and dst) match? - if (wildcard_dl_src == false && !dl_src.equals(packet.getSourceMAC())) + if (wildcard_dl_src == false + && dl_src != packet.getSourceMAC().toLong()) return false; if (action == FirewallRule.FirewallAction.DENY) { - //wildcards.drop &= ~OFMatch.OFPFW_DL_SRC; - wildcards.drop.setExact(MatchField.ETH_SRC, this.dl_src); + wildcards.drop &= ~OFMatch.OFPFW_DL_SRC; } else { - //wildcards.allow &= ~OFMatch.OFPFW_DL_SRC; - wildcards.allow.setExact(MatchField.ETH_SRC, this.dl_src); + wildcards.allow &= ~OFMatch.OFPFW_DL_SRC; } - if (wildcard_dl_dst == false && !dl_dst.equals(packet.getDestinationMAC())) + if (wildcard_dl_dst == false + && dl_dst != packet.getDestinationMAC().toLong()) return false; if (action == FirewallRule.FirewallAction.DENY) { - //wildcards.drop &= ~OFMatch.OFPFW_DL_DST; - wildcards.drop.setExact(MatchField.ETH_DST, this.dl_dst); + wildcards.drop &= ~OFMatch.OFPFW_DL_DST; } else { - //wildcards.allow &= ~OFMatch.OFPFW_DL_DST; - wildcards.allow.setExact(MatchField.ETH_DST, this.dl_dst); + wildcards.allow &= ~OFMatch.OFPFW_DL_DST; } // dl_type check: ARP, IP @@ -235,128 +223,103 @@ public class FirewallRule implements Comparable<FirewallRule> { // if this is not an ARP rule but the pkt is ARP, // return false match - no need to continue protocol specific check if (wildcard_dl_type == false) { - if (dl_type.equals(EthType.ARP)) { - if (packet.getEtherType() != EthType.ARP.getValue()) + if (dl_type == Ethernet.TYPE_ARP) { + if (packet.getEtherType() != Ethernet.TYPE_ARP) return false; else { if (action == FirewallRule.FirewallAction.DENY) { - //wildcards.drop &= ~OFMatch.OFPFW_DL_TYPE; - wildcards.drop.setExact(MatchField.ETH_TYPE, this.dl_type); + wildcards.drop &= ~OFMatch.OFPFW_DL_TYPE; } else { - //wildcards.allow &= ~OFMatch.OFPFW_DL_TYPE; - wildcards.allow.setExact(MatchField.ETH_TYPE, this.dl_type); + wildcards.allow &= ~OFMatch.OFPFW_DL_TYPE; } } - } else if (dl_type.equals(EthType.IPv4)) { - if (packet.getEtherType() != EthType.IPv4.getValue()) + } else if (dl_type == Ethernet.TYPE_IPv4) { + if (packet.getEtherType() != Ethernet.TYPE_IPv4) return false; else { if (action == FirewallRule.FirewallAction.DENY) { - //wildcards.drop &= ~OFMatch.OFPFW_NW_PROTO; - wildcards.drop.setExact(MatchField.IP_PROTO, this.nw_proto); + wildcards.drop &= ~OFMatch.OFPFW_NW_PROTO; } else { - //wildcards.allow &= ~OFMatch.OFPFW_NW_PROTO; - wildcards.allow.setExact(MatchField.IP_PROTO, this.nw_proto); + wildcards.allow &= ~OFMatch.OFPFW_NW_PROTO; } // IP packets, proceed with ip address check pkt_ip = (IPv4) pkt; // IP addresses (src and dst) match? - if (wildcard_nw_src == false && this.matchIPAddress(nw_src_prefix_and_mask.getValue().getInt(), nw_src_prefix_and_mask.getMask().getInt(), pkt_ip.getSourceAddress()) == false) + if (wildcard_nw_src == false + && this.matchIPAddress(nw_src_prefix, + nw_src_maskbits, pkt_ip.getSourceAddress()) == false) return false; if (action == FirewallRule.FirewallAction.DENY) { - //wildcards.drop &= ~OFMatch.OFPFW_NW_SRC_ALL; - //wildcards.drop |= (nw_src_maskbits << OFMatch.OFPFW_NW_SRC_SHIFT); - wildcards.drop.setMasked(MatchField.IPV4_SRC, nw_src_prefix_and_mask); + wildcards.drop &= ~OFMatch.OFPFW_NW_SRC_ALL; + wildcards.drop |= (nw_src_maskbits << OFMatch.OFPFW_NW_SRC_SHIFT); } else { - //wildcards.allow &= ~OFMatch.OFPFW_NW_SRC_ALL; - //wildcards.allow |= (nw_src_maskbits << OFMatch.OFPFW_NW_SRC_SHIFT); - wildcards.allow.setMasked(MatchField.IPV4_SRC, nw_src_prefix_and_mask); + wildcards.allow &= ~OFMatch.OFPFW_NW_SRC_ALL; + wildcards.allow |= (nw_src_maskbits << OFMatch.OFPFW_NW_SRC_SHIFT); } - if (wildcard_nw_dst == false && this.matchIPAddress(nw_dst_prefix_and_mask.getValue().getInt(), nw_dst_prefix_and_mask.getMask().getInt(), pkt_ip.getDestinationAddress()) == false) + if (wildcard_nw_dst == false + && this.matchIPAddress(nw_dst_prefix, + nw_dst_maskbits, + pkt_ip.getDestinationAddress()) == false) return false; if (action == FirewallRule.FirewallAction.DENY) { - //wildcards.drop &= ~OFMatch.OFPFW_NW_DST_ALL; - //wildcards.drop |= (nw_dst_maskbits << OFMatch.OFPFW_NW_DST_SHIFT); - wildcards.drop.setMasked(MatchField.IPV4_DST, nw_dst_prefix_and_mask); + wildcards.drop &= ~OFMatch.OFPFW_NW_DST_ALL; + wildcards.drop |= (nw_dst_maskbits << OFMatch.OFPFW_NW_DST_SHIFT); } else { - //wildcards.allow &= ~OFMatch.OFPFW_NW_DST_ALL; - //wildcards.allow |= (nw_dst_maskbits << OFMatch.OFPFW_NW_DST_SHIFT); - wildcards.allow.setMasked(MatchField.IPV4_DST, nw_dst_prefix_and_mask); + wildcards.allow &= ~OFMatch.OFPFW_NW_DST_ALL; + wildcards.allow |= (nw_dst_maskbits << OFMatch.OFPFW_NW_DST_SHIFT); } // nw_proto check if (wildcard_nw_proto == false) { - if (nw_proto.equals(IpProtocol.TCP)) { - if ((short) pkt_ip.getProtocol() != IpProtocol.TCP.getIpProtocolNumber()) + if (nw_proto == IPv4.PROTOCOL_TCP) { + if (pkt_ip.getProtocol() != IPv4.PROTOCOL_TCP) return false; else { pkt_tcp = (TCP) pkt_ip.getPayload(); pkt_tp_src = pkt_tcp.getSourcePort(); pkt_tp_dst = pkt_tcp.getDestinationPort(); } - } else if (nw_proto.equals(IpProtocol.UDP)) { - if ((short) pkt_ip.getProtocol() != IpProtocol.UDP.getIpProtocolNumber()) + } else if (nw_proto == IPv4.PROTOCOL_UDP) { + if (pkt_ip.getProtocol() != IPv4.PROTOCOL_UDP) return false; else { pkt_udp = (UDP) pkt_ip.getPayload(); pkt_tp_src = pkt_udp.getSourcePort(); pkt_tp_dst = pkt_udp.getDestinationPort(); } - } else if (nw_proto.equals(IpProtocol.ICMP)) { - if ((short) pkt_ip.getProtocol() != IpProtocol.ICMP.getIpProtocolNumber()) + } else if (nw_proto == IPv4.PROTOCOL_ICMP) { + if (pkt_ip.getProtocol() != IPv4.PROTOCOL_ICMP) return false; else { // nothing more needed for ICMP } } if (action == FirewallRule.FirewallAction.DENY) { - //wildcards.drop &= ~OFMatch.OFPFW_NW_PROTO; - wildcards.drop.setExact(MatchField.IP_PROTO, this.nw_proto); + wildcards.drop &= ~OFMatch.OFPFW_NW_PROTO; } else { - //wildcards.allow &= ~OFMatch.OFPFW_NW_PROTO; - wildcards.allow.setExact(MatchField.IP_PROTO, this.nw_proto); + wildcards.allow &= ~OFMatch.OFPFW_NW_PROTO; } // TCP/UDP source and destination ports match? if (pkt_tcp != null || pkt_udp != null) { // does the source port match? - if (tp_src.getPort() != 0 && tp_src.getPort() != pkt_tp_src) + if (tp_src != 0 && tp_src != pkt_tp_src) return false; if (action == FirewallRule.FirewallAction.DENY) { - //wildcards.drop &= ~OFMatch.OFPFW_TP_SRC; - if (pkt_tcp != null) { - wildcards.drop.setExact(MatchField.TCP_SRC, this.tp_src); - } else { - wildcards.drop.setExact(MatchField.UDP_SRC, this.tp_src); - } + wildcards.drop &= ~OFMatch.OFPFW_TP_SRC; } else { - //wildcards.allow &= ~OFMatch.OFPFW_TP_SRC; - if (pkt_tcp != null) { - wildcards.allow.setExact(MatchField.TCP_SRC, this.tp_src); - } else { - wildcards.allow.setExact(MatchField.UDP_SRC, this.tp_src); - } + wildcards.allow &= ~OFMatch.OFPFW_TP_SRC; } // does the destination port match? - if (tp_dst.getPort() != 0 && tp_dst.getPort() != pkt_tp_dst) + if (tp_dst != 0 && tp_dst != pkt_tp_dst) return false; if (action == FirewallRule.FirewallAction.DENY) { - //wildcards.drop &= ~OFMatch.OFPFW_TP_DST; - if (pkt_tcp != null) { - wildcards.drop.setExact(MatchField.TCP_DST, this.tp_dst); - } else { - wildcards.drop.setExact(MatchField.UDP_DST, this.tp_dst); - } + wildcards.drop &= ~OFMatch.OFPFW_TP_DST; } else { - //wildcards.allow &= ~OFMatch.OFPFW_TP_DST; - if (pkt_tcp != null) { - wildcards.allow.setExact(MatchField.TCP_DST, this.tp_dst); - } else { - wildcards.allow.setExact(MatchField.UDP_DST, this.tp_dst); - } + wildcards.allow &= ~OFMatch.OFPFW_TP_DST; } } } @@ -368,11 +331,9 @@ public class FirewallRule implements Comparable<FirewallRule> { } } if (action == FirewallRule.FirewallAction.DENY) { - //wildcards.drop &= ~OFMatch.OFPFW_DL_TYPE; - wildcards.drop.setExact(MatchField.ETH_TYPE, this.dl_type); + wildcards.drop &= ~OFMatch.OFPFW_DL_TYPE; } else { - //wildcards.allow &= ~OFMatch.OFPFW_DL_TYPE; - wildcards.allow.setExact(MatchField.ETH_TYPE, this.dl_type); + wildcards.allow &= ~OFMatch.OFPFW_DL_TYPE; } // all applicable checks passed @@ -421,18 +382,18 @@ public class FirewallRule implements Comparable<FirewallRule> { public int hashCode() { final int prime = 2521; int result = super.hashCode(); - result = prime * result + (int) dpid.getLong(); - result = prime * result + in_port.getPortNumber(); - result = prime * result + (int) dl_src.getLong(); - result = prime * result + (int) dl_dst.getLong(); - result = prime * result + dl_type.getValue(); - result = prime * result + nw_src_prefix_and_mask.getValue().getInt(); - result = prime * result + nw_src_prefix_and_mask.getMask().getInt(); - result = prime * result + nw_dst_prefix_and_mask.getValue().getInt(); - result = prime * result + nw_dst_prefix_and_mask.getMask().getInt(); - result = prime * result + nw_proto.getIpProtocolNumber(); - result = prime * result + tp_src.getPort(); - result = prime * result + tp_dst.getPort(); + result = prime * result + (int) dpid; + result = prime * result + in_port; + result = prime * result + (int) dl_src; + result = prime * result + (int) dl_dst; + result = prime * result + dl_type; + result = prime * result + nw_src_prefix; + result = prime * result + nw_src_maskbits; + result = prime * result + nw_dst_prefix; + result = prime * result + nw_dst_maskbits; + result = prime * result + nw_proto; + result = prime * result + tp_src; + result = prime * result + tp_dst; result = prime * result + action.ordinal(); result = prime * result + priority; result = prime * result + (new Boolean(wildcard_dpid)).hashCode(); diff --git a/src/main/java/net/floodlightcontroller/firewall/FirewallRuleSerializer.java b/src/main/java/net/floodlightcontroller/firewall/FirewallRuleSerializer.java index 42cb2727f79e3403d0b1ddd0d777468a07092627..3180968d1dd1786ef183933bc8584a5b896b123d 100644 --- a/src/main/java/net/floodlightcontroller/firewall/FirewallRuleSerializer.java +++ b/src/main/java/net/floodlightcontroller/firewall/FirewallRuleSerializer.java @@ -19,10 +19,14 @@ package net.floodlightcontroller.firewall; import java.io.IOException; +import net.floodlightcontroller.packet.IPv4; +import net.floodlightcontroller.util.MACAddress; + import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; +import org.openflow.util.HexString; /** * Serialize a FirewallRule object @@ -38,18 +42,18 @@ public class FirewallRuleSerializer extends JsonSerializer<FirewallRule> { jGen.writeStartObject(); jGen.writeNumberField("ruleid", rule.ruleid); - jGen.writeStringField("dpid", rule.dpid.toString()); - jGen.writeNumberField("in_port", rule.in_port.getPortNumber()); - jGen.writeStringField("dl_src", rule.dl_src.toString()); - jGen.writeStringField("dl_dst", rule.dl_dst.toString()); - jGen.writeNumberField("dl_type", rule.dl_type.getValue()); - jGen.writeStringField("nw_src_prefix", rule.nw_src_prefix_and_mask.getValue().toString()); - jGen.writeNumberField("nw_src_maskbits", rule.nw_src_prefix_and_mask.getMask().asCidrMaskLength()); - jGen.writeStringField("nw_dst_prefix", rule.nw_dst_prefix_and_mask.getValue().toString()); - jGen.writeNumberField("nw_dst_maskbits", rule.nw_dst_prefix_and_mask.getMask().asCidrMaskLength()); - jGen.writeNumberField("nw_proto", rule.nw_proto.getIpProtocolNumber()); - jGen.writeNumberField("tp_src", rule.tp_src.getPort()); - jGen.writeNumberField("tp_dst", rule.tp_dst.getPort()); + jGen.writeStringField("dpid", HexString.toHexString(rule.dpid)); + jGen.writeNumberField("in_port", rule.in_port); + jGen.writeStringField("dl_src",String.valueOf(MACAddress.valueOf(rule.dl_src))); + jGen.writeStringField("dl_dst", String.valueOf(MACAddress.valueOf(rule.dl_dst))); + jGen.writeNumberField("dl_type", rule.dl_type); + jGen.writeStringField("nw_src_prefix", IPv4.fromIPv4Address(rule.nw_src_prefix)); + jGen.writeNumberField("nw_src_maskbits", rule.nw_src_maskbits); + jGen.writeStringField("nw_dst_prefix", IPv4.fromIPv4Address(rule.nw_dst_prefix)); + jGen.writeNumberField("nw_dst_maskbits", rule.nw_dst_maskbits); + jGen.writeNumberField("nw_proto", rule.nw_proto); + jGen.writeNumberField("tp_src", rule.tp_src); + jGen.writeNumberField("tp_dst", rule.tp_dst); jGen.writeBooleanField("wildcard_dpid", rule.wildcard_dpid); jGen.writeBooleanField("wildcard_in_port", rule.wildcard_in_port); jGen.writeBooleanField("wildcard_dl_src", rule.wildcard_dl_src); diff --git a/src/main/java/net/floodlightcontroller/firewall/FirewallRulesResource.java b/src/main/java/net/floodlightcontroller/firewall/FirewallRulesResource.java index 27e40d3ffecebecc8b268125ee0441cf7ea8348c..7af219cc63f70879eda9cdd5ab7971b1a3111cea 100644 --- a/src/main/java/net/floodlightcontroller/firewall/FirewallRulesResource.java +++ b/src/main/java/net/floodlightcontroller/firewall/FirewallRulesResource.java @@ -25,14 +25,7 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.MappingJsonFactory; - -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.EthType; -import org.projectfloodlight.openflow.types.IPv4AddressWithMask; -import org.projectfloodlight.openflow.types.IpProtocol; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.types.TransportPort; +import org.openflow.util.HexString; import org.restlet.resource.Delete; import org.restlet.resource.Post; import org.restlet.resource.Get; @@ -40,6 +33,7 @@ import org.restlet.resource.ServerResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPv4; public class FirewallRulesResource extends ServerResource { @@ -172,13 +166,13 @@ public class FirewallRulesResource extends ServerResource { tmp = jp.getText(); if (tmp.equalsIgnoreCase("-1") == false) { // user inputs hex format dpid - rule.dpid = DatapathId.of(tmp); + rule.dpid = HexString.toLong(tmp); rule.wildcard_dpid = false; } } else if (n == "src-inport") { - rule.in_port = OFPort.of(Integer.parseInt(jp.getText())); + rule.in_port = Short.parseShort(jp.getText()); rule.wildcard_in_port = false; } @@ -186,7 +180,7 @@ public class FirewallRulesResource extends ServerResource { tmp = jp.getText(); if (tmp.equalsIgnoreCase("ANY") == false) { rule.wildcard_dl_src = false; - rule.dl_src = MacAddress.of(tmp); + rule.dl_src = Ethernet.toLong(Ethernet.toMACAddress(tmp)); } } @@ -194,7 +188,7 @@ public class FirewallRulesResource extends ServerResource { tmp = jp.getText(); if (tmp.equalsIgnoreCase("ANY") == false) { rule.wildcard_dl_dst = false; - rule.dl_dst = MacAddress.of(tmp); + rule.dl_dst = Ethernet.toLong(Ethernet.toMACAddress(tmp)); } } @@ -202,11 +196,11 @@ public class FirewallRulesResource extends ServerResource { tmp = jp.getText(); if (tmp.equalsIgnoreCase("ARP")) { rule.wildcard_dl_type = false; - rule.dl_type = EthType.ARP; + rule.dl_type = Ethernet.TYPE_ARP; } if (tmp.equalsIgnoreCase("IPv4")) { rule.wildcard_dl_type = false; - rule.dl_type = EthType.IPv4; + rule.dl_type = Ethernet.TYPE_IPv4; } } @@ -215,8 +209,10 @@ public class FirewallRulesResource extends ServerResource { if (tmp.equalsIgnoreCase("ANY") == false) { rule.wildcard_nw_src = false; rule.wildcard_dl_type = false; - rule.dl_type = EthType.IPv4; - rule.nw_src_prefix_and_mask = IPv4AddressWithMask.of(tmp); + rule.dl_type = Ethernet.TYPE_IPv4; + int[] cidr = IPCIDRToPrefixBits(tmp); + rule.nw_src_prefix = cidr[0]; + rule.nw_src_maskbits = cidr[1]; } } @@ -225,8 +221,10 @@ public class FirewallRulesResource extends ServerResource { if (tmp.equalsIgnoreCase("ANY") == false) { rule.wildcard_nw_dst = false; rule.wildcard_dl_type = false; - rule.dl_type = EthType.IPv4; - rule.nw_dst_prefix_and_mask = IPv4AddressWithMask.of(tmp); + rule.dl_type = Ethernet.TYPE_IPv4; + int[] cidr = IPCIDRToPrefixBits(tmp); + rule.nw_dst_prefix = cidr[0]; + rule.nw_dst_maskbits = cidr[1]; } } @@ -234,30 +232,30 @@ public class FirewallRulesResource extends ServerResource { tmp = jp.getText(); if (tmp.equalsIgnoreCase("TCP")) { rule.wildcard_nw_proto = false; - rule.nw_proto = IpProtocol.TCP; + rule.nw_proto = IPv4.PROTOCOL_TCP; rule.wildcard_dl_type = false; - rule.dl_type = EthType.IPv4; + rule.dl_type = Ethernet.TYPE_IPv4; } else if (tmp.equalsIgnoreCase("UDP")) { rule.wildcard_nw_proto = false; - rule.nw_proto = IpProtocol.UDP; + rule.nw_proto = IPv4.PROTOCOL_UDP; rule.wildcard_dl_type = false; - rule.dl_type = EthType.IPv4; + rule.dl_type = Ethernet.TYPE_IPv4; } else if (tmp.equalsIgnoreCase("ICMP")) { rule.wildcard_nw_proto = false; - rule.nw_proto = IpProtocol.ICMP; + rule.nw_proto = IPv4.PROTOCOL_ICMP; rule.wildcard_dl_type = false; - rule.dl_type = EthType.IPv4; + rule.dl_type = Ethernet.TYPE_IPv4; } } else if (n == "tp-src") { rule.wildcard_tp_src = false; - rule.tp_src = TransportPort.of(Integer.parseInt(jp.getText())); + rule.tp_src = Short.parseShort(jp.getText()); } else if (n == "tp-dst") { rule.wildcard_tp_dst = false; - rule.tp_dst = TransportPort.of(Integer.parseInt(jp.getText())); + rule.tp_dst = Short.parseShort(jp.getText()); } else if (n == "priority") { diff --git a/src/main/java/net/floodlightcontroller/firewall/RuleWildcardsPair.java b/src/main/java/net/floodlightcontroller/firewall/RuleWildcardsPair.java index 114dc3eee332c005030152b69141873e1a7371b8..d2aa2e5ff458b4fdef9c00bb4544e9851df3fe5c 100644 --- a/src/main/java/net/floodlightcontroller/firewall/RuleWildcardsPair.java +++ b/src/main/java/net/floodlightcontroller/firewall/RuleWildcardsPair.java @@ -17,9 +17,9 @@ package net.floodlightcontroller.firewall; -import org.projectfloodlight.openflow.protocol.match.Match; +import org.openflow.protocol.OFMatch; public class RuleWildcardsPair { public FirewallRule rule; - public Match.Builder wildcards; + public int wildcards = OFMatch.OFPFW_ALL; } diff --git a/src/main/java/net/floodlightcontroller/firewall/WildcardsPair.java b/src/main/java/net/floodlightcontroller/firewall/WildcardsPair.java index f6443c403e1de021319e84869b8f07f911e48f21..db4cf7441a11de5931a7b9b2db4205a9d66c54e6 100644 --- a/src/main/java/net/floodlightcontroller/firewall/WildcardsPair.java +++ b/src/main/java/net/floodlightcontroller/firewall/WildcardsPair.java @@ -17,12 +17,9 @@ package net.floodlightcontroller.firewall; -import org.projectfloodlight.openflow.protocol.match.Match; - +import org.openflow.protocol.OFMatch; public class WildcardsPair { - //public int allow = OFMatch.OFPFW_ALL; - //public int drop = OFMatch.OFPFW_ALL; - public Match.Builder allow; - public Match.Builder drop; + public int allow = OFMatch.OFPFW_ALL; + public int drop = OFMatch.OFPFW_ALL; } diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java index 40fc77bcf04734639005148fc7c98a81ea555f9a..588e8bc3123336bf3fa7bab8a421e5ce17ff456c 100644 --- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java +++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java @@ -33,7 +33,6 @@ import net.floodlightcontroller.devicemanager.SwitchPort; import net.floodlightcontroller.core.annotations.LogMessageCategory; import net.floodlightcontroller.core.annotations.LogMessageDoc; import net.floodlightcontroller.core.annotations.LogMessageDocs; -import net.floodlightcontroller.core.internal.IOFSwitchService; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.FloodlightModuleException; import net.floodlightcontroller.core.module.IFloodlightModule; @@ -41,30 +40,20 @@ import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.core.util.AppCookie; import net.floodlightcontroller.counter.ICounterStoreService; import net.floodlightcontroller.packet.Ethernet; -import net.floodlightcontroller.packet.IPv4; import net.floodlightcontroller.routing.ForwardingBase; import net.floodlightcontroller.routing.IRoutingDecision; import net.floodlightcontroller.routing.IRoutingService; import net.floodlightcontroller.routing.Route; import net.floodlightcontroller.topology.ITopologyService; -import org.projectfloodlight.openflow.protocol.OFFlowMod; -import org.projectfloodlight.openflow.protocol.OFFlowModCommand; -import org.projectfloodlight.openflow.protocol.OFFlowModFlags; -import org.projectfloodlight.openflow.protocol.OFPacketIn; -import org.projectfloodlight.openflow.protocol.OFPacketOut; -import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.protocol.match.Match; -import org.projectfloodlight.openflow.protocol.match.MatchField; -import org.projectfloodlight.openflow.protocol.action.OFAction; -import org.projectfloodlight.openflow.protocol.action.OFActionOutput; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.IPv4Address; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.types.OFVlanVidMatch; -import org.projectfloodlight.openflow.types.VlanVid; +import org.openflow.protocol.OFFlowMod; +import org.openflow.protocol.OFMatch; +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFPacketOut; +import org.openflow.protocol.OFPort; +import org.openflow.protocol.OFType; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionOutput; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -172,29 +161,24 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { protected void doForwardFlow(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx, boolean requestFlowRemovedNotifn) { - //OFMatch match = new OFMatch(); - //match.loadFromPacket(pi.getData(), pi.getInPort()); - //Match.Builder matchBuilder = sw.getOFFactory().buildMatch(); - Match match = pi.getMatch(); + OFMatch match = new OFMatch(); + match.loadFromPacket(pi.getPacketData(), pi.getInPort()); + // Check if we have the location of the destination IDevice dstDevice = IDeviceService.fcStore. get(cntx, IDeviceService.CONTEXT_DST_DEVICE); - // DESTINATION DEVICE = KNOWN if (dstDevice != null) { IDevice srcDevice = IDeviceService.fcStore. get(cntx, IDeviceService.CONTEXT_SRC_DEVICE); - DatapathId srcIsland = topology.getL2DomainId(sw.getId()); + Long srcIsland = topology.getL2DomainId(sw.getId()); - // If we can't find the source device, that's no good if (srcDevice == null) { log.debug("No device entry found for source device"); return; } - - // If the source isn't connected to an OF island we control, then that's not good either if (srcIsland == null) { log.debug("No openflow island found for source {}/{}", sw.getStringId(), pi.getInPort()); @@ -202,24 +186,24 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { } // Validate that we have a destination known on the same island - // Validate that the source and destination are not on the same switch port + // Validate that the source and destination are not on the same switchport boolean on_same_island = false; boolean on_same_if = false; for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) { - DatapathId dstSwDpid = dstDap.getSwitchDPID(); - DatapathId dstIsland = topology.getL2DomainId(dstSwDpid); + long dstSwDpid = dstDap.getSwitchDPID(); + Long dstIsland = topology.getL2DomainId(dstSwDpid); if ((dstIsland != null) && dstIsland.equals(srcIsland)) { on_same_island = true; - if ((sw.getId().equals(dstSwDpid)) && - (pi.getInPort().equals(dstDap.getPort()))) { + if ((sw.getId() == dstSwDpid) && + (pi.getInPort() == dstDap.getPort())) { on_same_if = true; } break; } } - // If the two devices are not on the same L2 network, find out how to get to the destination via flooding if (!on_same_island) { + // Flood since we don't know the dst device if (log.isTraceEnabled()) { log.trace("No first hop island found for destination " + "device {}, Action = flooding", dstDevice); @@ -228,7 +212,6 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { return; } - // If the two devices are on the same switch port number, they should be able to communicate w/o further flows if (on_same_if) { if (log.isTraceEnabled()) { log.trace("Both source and destination are on the same " + @@ -254,9 +237,9 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { // srcCluster and dstCluster here cannot be null as // every switch will be at least in its own L2 domain. - DatapathId srcCluster = + Long srcCluster = topology.getL2DomainId(srcDap.getSwitchDPID()); - DatapathId dstCluster = + Long dstCluster = topology.getL2DomainId(dstDap.getSwitchDPID()); int srcVsDest = srcCluster.compareTo(dstCluster); @@ -264,9 +247,9 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { if (!srcDap.equals(dstDap)) { Route route = routingEngine.getRoute(srcDap.getSwitchDPID(), - srcDap.getPort(), + (short)srcDap.getPort(), dstDap.getSwitchDPID(), - dstDap.getPort(), 0); //cookie = 0, i.e., default route + (short)dstDap.getPort(), 0); //cookie = 0, i.e., default route if (route != null) { if (log.isTraceEnabled()) { log.trace("pushRoute match={} route={} " + @@ -279,7 +262,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { AppCookie.makeCookie(FORWARDING_APP_ID, 0); // if there is prior routing decision use wildcard - Match.Builder wildcard_hints; + Integer wildcard_hints = null; IRoutingDecision decision = null; if (cntx != null) { decision = IRoutingDecision.rtStore @@ -290,7 +273,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { wildcard_hints = decision.getWildcards(); } else { // L2 only wildcard if there is no prior route decision - /*wildcard_hints = ((Integer) sw + wildcard_hints = ((Integer) sw .getAttribute(IOFSwitch.PROP_FASTWILDCARDS)) .intValue() & ~OFMatch.OFPFW_IN_PORT @@ -298,21 +281,12 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { & ~OFMatch.OFPFW_DL_SRC & ~OFMatch.OFPFW_DL_DST & ~OFMatch.OFPFW_NW_SRC_MASK - & ~OFMatch.OFPFW_NW_DST_MASK;*/ - //TODO @Ryan does the use of NO_MASK here automatically assume isFullyWildcarded is true? - // dummy values set to not trigger isFullyWildcarded - wildcard_hints = sw.getOFFactory().buildMatch(); - wildcard_hints.setExact(MatchField.IN_PORT, OFPort.of(1)); - wildcard_hints.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlan(1)); - wildcard_hints.setExact(MatchField.ETH_SRC, MacAddress.BROADCAST); - wildcard_hints.setExact(MatchField.ETH_DST, MacAddress.BROADCAST); - wildcard_hints.setExact(MatchField.IPV4_SRC, IPv4Address.FULL_MASK); - wildcard_hints.setExact(MatchField.IPV4_DST, IPv4Address.FULL_MASK); + & ~OFMatch.OFPFW_NW_DST_MASK; } pushRoute(route, match, wildcard_hints, pi, sw.getId(), cookie, cntx, requestFlowRemovedNotifn, false, - OFFlowModCommand.ADD); + OFFlowMod.OFPFC_ADD); } } iSrcDaps++; @@ -323,9 +297,8 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { iDstDaps++; } } - // END DESTINATION DEVICE = KNOWN } else { - // DESTINATION DEVICE = UNKNOWN, thus flood to hopefully/eventually find out where the destination is + // Flood since we don't know the dst device doFlood(sw, pi, cntx); } } @@ -412,7 +385,6 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); l.add(IFloodlightProviderService.class); - l.add(IOFSwitchService.class); l.add(IDeviceService.class); l.add(IRoutingService.class); l.add(ITopologyService.class); @@ -440,7 +412,6 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { public void init(FloodlightModuleContext context) throws FloodlightModuleException { super.init(); this.floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class); - this.switchService = context.getServiceImpl(IOFSwitchService.class); this.deviceManager = context.getServiceImpl(IDeviceService.class); this.routingEngine = context.getServiceImpl(IRoutingService.class); this.topology = context.getServiceImpl(ITopologyService.class); diff --git a/src/main/java/net/floodlightcontroller/hub/Hub.java b/src/main/java/net/floodlightcontroller/hub/Hub.java index e683fa917443d2c7abe24a3b331f13ecbce0fbfb..4a3549ba47224cc0b8a7821798f589d61fbabb52 100644 --- a/src/main/java/net/floodlightcontroller/hub/Hub.java +++ b/src/main/java/net/floodlightcontroller/hub/Hub.java @@ -17,6 +17,7 @@ package net.floodlightcontroller.hub; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -26,20 +27,19 @@ import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.LogicalOFMessageCategory; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.FloodlightModuleException; import net.floodlightcontroller.core.module.IFloodlightModule; import net.floodlightcontroller.core.module.IFloodlightService; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPacketIn; -import org.projectfloodlight.openflow.protocol.OFPacketOut; -import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.protocol.action.OFAction; -import org.projectfloodlight.openflow.protocol.action.OFActionOutput; -import org.projectfloodlight.openflow.types.OFBufferId; -import org.projectfloodlight.openflow.types.OFPort; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFPacketOut; +import org.openflow.protocol.OFPort; +import org.openflow.protocol.OFType; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionOutput; +import org.openflow.util.U16; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,24 +64,34 @@ public class Hub implements IFloodlightModule, IOFMessageListener { return Hub.class.getPackage().getName(); } - //TODO @Ryan this is a good example (my first try) at using the builders. Might be good reference later. public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { OFPacketIn pi = (OFPacketIn) msg; - OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); - pob.setBufferId(pi.getBufferId()).setInPort(pi.getInPort()); + OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory() + .getMessage(OFType.PACKET_OUT); + po.setBufferId(pi.getBufferId()) + .setInPort(pi.getInPort()); // set actions - OFActionOutput.Builder actionBuilder = sw.getOFFactory().actions().buildOutput(); - actionBuilder.setPort(OFPort.FLOOD); - pob.setActions(Collections.singletonList((OFAction) actionBuilder.build())); - // pob.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH); @Ryan setting of lengths is taken care of now, I think + OFActionOutput action = new OFActionOutput() + .setPort(OFPort.OFPP_FLOOD.getValue()); + po.setActions(Collections.singletonList((OFAction)action)); + po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH); // set data if is is included in the packetin - if (pi.getBufferId() == OFBufferId.NO_BUFFER) { - byte[] packetData = pi.getData(); - pob.setData(packetData); + if (pi.getBufferId() == OFPacketOut.BUFFER_ID_NONE) { + byte[] packetData = pi.getPacketData(); + po.setLength(U16.t(OFPacketOut.MINIMUM_LENGTH + + po.getActionsLength() + packetData.length)); + po.setPacketData(packetData); + } else { + po.setLength(U16.t(OFPacketOut.MINIMUM_LENGTH + + po.getActionsLength())); + } + try { + sw.write(po, cntx); + } catch (IOException e) { + log.error("Failure writing PacketOut", e); } - sw.write(pob.build(), LogicalOFMessageCategory.MAIN); return Command.CONTINUE; } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java index 5025d8d6981d8c5002477a0c754de4932fb0232a..e4e109ca46fea5d8605109aea8f497c6300edcb6 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java @@ -18,9 +18,7 @@ package net.floodlightcontroller.linkdiscovery; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; - -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFPort; +import org.openflow.util.HexString; public interface ILinkDiscovery { @@ -47,16 +45,16 @@ public interface ILinkDiscovery { } public class LDUpdate { - protected DatapathId src; - protected OFPort srcPort; - protected DatapathId dst; - protected OFPort dstPort; + protected long src; + protected short srcPort; + protected long dst; + protected short dstPort; protected SwitchType srcType; protected LinkType type; protected UpdateOperation operation; - public LDUpdate(DatapathId src, OFPort srcPort, - DatapathId dst, OFPort dstPort, + public LDUpdate(long src, short srcPort, + long dst, short dstPort, ILinkDiscovery.LinkType type, UpdateOperation operation) { this.src = src; @@ -78,32 +76,32 @@ public interface ILinkDiscovery { } // For updtedSwitch(sw) - public LDUpdate(DatapathId switchId, SwitchType stype, UpdateOperation oper ){ + public LDUpdate(long switchId, SwitchType stype, UpdateOperation oper ){ this.operation = oper; this.src = switchId; this.srcType = stype; } // For port up or port down; and tunnel port added and removed. - public LDUpdate(DatapathId sw, OFPort port, UpdateOperation operation) { + public LDUpdate(long sw, short port, UpdateOperation operation) { this.src = sw; this.srcPort = port; this.operation = operation; } - public DatapathId getSrc() { + public long getSrc() { return src; } - public OFPort getSrcPort() { + public short getSrcPort() { return srcPort; } - public DatapathId getDst() { + public long getDst() { return dst; } - public OFPort getDstPort() { + public short getDstPort() { return dstPort; } @@ -129,20 +127,20 @@ public interface ILinkDiscovery { case LINK_REMOVED: case LINK_UPDATED: return "LDUpdate [operation=" + operation + - ", src=" + src.toString() - + ", srcPort=" + srcPort.toString() - + ", dst=" + dst.toString() - + ", dstPort=" + dstPort.toString() + ", src=" + HexString.toHexString(src) + + ", srcPort=" + srcPort + + ", dst=" + HexString.toHexString(dst) + + ", dstPort=" + dstPort + ", type=" + type + "]"; case PORT_DOWN: case PORT_UP: return "LDUpdate [operation=" + operation + - ", src=" + src.toString() - + ", srcPort=" + srcPort.toString() + "]"; + ", src=" + HexString.toHexString(src) + + ", srcPort=" + srcPort + "]"; case SWITCH_REMOVED: case SWITCH_UPDATED: return "LDUpdate [operation=" + operation + - ", src=" + src.toString() + "]"; + ", src=" + HexString.toHexString(src) + "]"; default: return "LDUpdate: Unknown update."; } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java index 2bb53727917ffba3b4138bf3d06a9e56b79db1f2..3773efd713b992755e093bd50dfdaf85317114a0 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java @@ -20,10 +20,7 @@ package net.floodlightcontroller.linkdiscovery; import java.util.Map; import java.util.Set; -import org.projectfloodlight.openflow.protocol.OFPacketOut; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.OFPort; +import org.openflow.protocol.OFPacketOut; import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.routing.Link; @@ -35,7 +32,7 @@ public interface ILinkDiscoveryService extends IFloodlightService { /** * Returns if a given switchport is a tunnel endpoint or not */ - public boolean isTunnelPort(DatapathId sw, OFPort port); + public boolean isTunnelPort(long sw, short port); /** * Retrieves a map of all known link connections between OpenFlow switches @@ -62,7 +59,7 @@ public interface ILinkDiscoveryService extends IFloodlightService { * to switchport (sw, port). PacketOut does not contain actions. * PacketOut length includes the minimum length and data length. */ - public OFPacketOut generateLLDPMessage(DatapathId sw, OFPort port, + public OFPacketOut generateLLDPMessage(long sw, short port, boolean isStandard, boolean isReverse); @@ -70,7 +67,7 @@ public interface ILinkDiscoveryService extends IFloodlightService { * Returns an unmodifiable map from switch id to a set of all links with it * as an endpoint. */ - public Map<DatapathId, Set<Link>> getSwitchLinks(); + public Map<Long, Set<Link>> getSwitchLinks(); /** * Adds a listener to listen for ILinkDiscoveryService messages @@ -87,17 +84,17 @@ public interface ILinkDiscoveryService extends IFloodlightService { * Adds a switch port to suppress lldp set. LLDPs and BDDPs will not be sent * out, and if any are received on this port then they will be dropped. */ - public void AddToSuppressLLDPs(DatapathId sw, OFPort port); + public void AddToSuppressLLDPs(long sw, short port); /** * Removes a switch port from suppress lldp set */ - public void RemoveFromSuppressLLDPs(DatapathId sw, OFPort port); + public void RemoveFromSuppressLLDPs(long sw, short port); /** * Get the set of quarantined ports on a switch */ - public Set<OFPort> getQuarantinedPorts(DatapathId sw); + public Set<Short> getQuarantinedPorts(long sw); /** * Get the status of auto port fast feature. @@ -129,5 +126,5 @@ public interface ILinkDiscoveryService extends IFloodlightService { * ALL MAC addresses to the ignore list. This will cause a drop of * ALL packet ins. */ - public void addMACToIgnoreList(MacAddress mac, int ignoreBits); + public void addMACToIgnoreList(long mac, int ignoreBits); } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java b/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java index 6c2d7334e436cabe3f44eca1547d4f1425c3d440..0230b3bbd31f8cb47581ff83f6b79f4632386560 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java @@ -15,17 +15,15 @@ package net.floodlightcontroller.linkdiscovery; -import java.util.Date; - import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LinkType; import com.fasterxml.jackson.annotation.JsonIgnore; public class LinkInfo { - public LinkInfo(Date firstSeenTime, - Date lastLldpReceivedTime, - Date lastBddpReceivedTime) { + public LinkInfo(Long firstSeenTime, + Long lastLldpReceivedTime, + Long lastBddpReceivedTime) { super(); this.firstSeenTime = firstSeenTime; this.lastLldpReceivedTime = lastLldpReceivedTime; @@ -48,9 +46,9 @@ public class LinkInfo { this.lastBddpReceivedTime = fromLinkInfo.getMulticastValidTime(); } - protected Date firstSeenTime; - protected Date lastLldpReceivedTime; /* Standard LLLDP received time */ - protected Date lastBddpReceivedTime; /* Modified LLDP received time */ + protected Long firstSeenTime; + protected Long lastLldpReceivedTime; /* Standard LLLDP received time */ + protected Long lastBddpReceivedTime; /* Modified LLDP received time */ /** The port states stored here are topology's last knowledge of * the state of the port. This mostly mirrors the state @@ -62,27 +60,27 @@ public class LinkInfo { * requires the new state to be written to storage. */ - public Date getFirstSeenTime() { + public Long getFirstSeenTime() { return firstSeenTime; } - public void setFirstSeenTime(Date firstSeenTime) { + public void setFirstSeenTime(Long firstSeenTime) { this.firstSeenTime = firstSeenTime; } - public Date getUnicastValidTime() { + public Long getUnicastValidTime() { return lastLldpReceivedTime; } - public void setUnicastValidTime(Date unicastValidTime) { + public void setUnicastValidTime(Long unicastValidTime) { this.lastLldpReceivedTime = unicastValidTime; } - public Date getMulticastValidTime() { + public Long getMulticastValidTime() { return lastBddpReceivedTime; } - public void setMulticastValidTime(Date multicastValidTime) { + public void setMulticastValidTime(Long multicastValidTime) { this.lastBddpReceivedTime = multicastValidTime; } @@ -149,8 +147,8 @@ public class LinkInfo { */ @Override public String toString() { - return "LinkInfo [unicastValidTime=" + ((lastLldpReceivedTime == null) ? "null" : lastLldpReceivedTime.getTime()) - + ", multicastValidTime=" + ((lastBddpReceivedTime == null) ? "null" : lastBddpReceivedTime.getTime()) + return "LinkInfo [unicastValidTime=" + ((lastLldpReceivedTime == null) ? "null" : lastLldpReceivedTime) + + ", multicastValidTime=" + ((lastBddpReceivedTime == null) ? "null" : lastBddpReceivedTime) + "]"; } } diff --git a/src/main/java/net/floodlightcontroller/packet/BSNPROBE.java b/src/main/java/net/floodlightcontroller/packet/BSNPROBE.java index 63f710e58748881ca2aabe1360e559448253fec8..c9115eacb5c22c459cbaded572a64063125118c6 100644 --- a/src/main/java/net/floodlightcontroller/packet/BSNPROBE.java +++ b/src/main/java/net/floodlightcontroller/packet/BSNPROBE.java @@ -21,7 +21,7 @@ package net.floodlightcontroller.packet; import java.nio.ByteBuffer; import java.util.Arrays; -import org.projectfloodlight.openflow.util.HexString; +import org.openflow.util.HexString; /** * @author Shudong Zhou (shudong.zhou@bigswitch.com) diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java index d29b0859c64daff72d9d503c5bb866a422c5b67a..60887b3158e27a3d714927aab4f57b7085242164 100644 --- a/src/main/java/net/floodlightcontroller/packet/Ethernet.java +++ b/src/main/java/net/floodlightcontroller/packet/Ethernet.java @@ -22,8 +22,8 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.util.HexString; +import net.floodlightcontroller.util.MACAddress; +import org.openflow.util.HexString; /** * @@ -49,8 +49,8 @@ public class Ethernet extends BasePacket { etherTypeClassMap.put(TYPE_BSN, BSN.class); } - protected MacAddress destinationMACAddress; - protected MacAddress sourceMACAddress; + protected MACAddress destinationMACAddress; + protected MACAddress sourceMACAddress; protected byte priorityCode; protected short vlanID; protected short etherType; @@ -68,13 +68,13 @@ public class Ethernet extends BasePacket { * @return the destination MAC as a byte array */ public byte[] getDestinationMACAddress() { - return destinationMACAddress.getBytes(); + return destinationMACAddress.toBytes(); } /** * @return the destination MAC */ - public MacAddress getDestinationMAC() { + public MACAddress getDestinationMAC() { return destinationMACAddress; } @@ -82,7 +82,7 @@ public class Ethernet extends BasePacket { * @param destinationMACAddress the destination MAC to set */ public Ethernet setDestinationMACAddress(byte[] destinationMACAddress) { - this.destinationMACAddress = MacAddress.of(destinationMACAddress); + this.destinationMACAddress = MACAddress.valueOf(destinationMACAddress); return this; } @@ -90,7 +90,7 @@ public class Ethernet extends BasePacket { * @param destinationMACAddress the destination MAC to set */ public Ethernet setDestinationMACAddress(String destinationMACAddress) { - this.destinationMACAddress = MacAddress.of(destinationMACAddress); + this.destinationMACAddress = MACAddress.valueOf(destinationMACAddress); return this; } @@ -98,13 +98,13 @@ public class Ethernet extends BasePacket { * @return the source MACAddress as a byte array */ public byte[] getSourceMACAddress() { - return sourceMACAddress.getBytes(); + return sourceMACAddress.toBytes(); } /** * @return the source MACAddress */ - public MacAddress getSourceMAC() { + public MACAddress getSourceMAC() { return sourceMACAddress; } @@ -112,7 +112,7 @@ public class Ethernet extends BasePacket { * @param sourceMACAddress the source MAC to set */ public Ethernet setSourceMACAddress(byte[] sourceMACAddress) { - this.sourceMACAddress = MacAddress.of(sourceMACAddress); + this.sourceMACAddress = MACAddress.valueOf(sourceMACAddress); return this; } @@ -120,7 +120,7 @@ public class Ethernet extends BasePacket { * @param sourceMACAddress the source MAC to set */ public Ethernet setSourceMACAddress(String sourceMACAddress) { - this.sourceMACAddress = MacAddress.of(sourceMACAddress); + this.sourceMACAddress = MACAddress.valueOf(sourceMACAddress); return this; } @@ -173,7 +173,7 @@ public class Ethernet extends BasePacket { * @return True if the Ethernet frame is broadcast, false otherwise */ public boolean isBroadcast() { - assert(destinationMACAddress.getLength() == 6); + assert(destinationMACAddress.length() == 6); return destinationMACAddress.isBroadcast(); } @@ -213,8 +213,8 @@ public class Ethernet extends BasePacket { } byte[] data = new byte[length]; ByteBuffer bb = ByteBuffer.wrap(data); - bb.put(destinationMACAddress.getBytes()); - bb.put(sourceMACAddress.getBytes()); + bb.put(destinationMACAddress.toBytes()); + bb.put(sourceMACAddress.toBytes()); if (vlanID != VLAN_UNTAGGED) { bb.putShort((short) 0x8100); bb.putShort((short) ((priorityCode << 13) | (vlanID & 0x0fff))); @@ -234,16 +234,16 @@ public class Ethernet extends BasePacket { return null; ByteBuffer bb = ByteBuffer.wrap(data, offset, length); if (this.destinationMACAddress == null) - this.destinationMACAddress = MacAddress.of(new byte[6]); - byte[] dstAddr = new byte[MacAddress.NONE.getLength()]; + this.destinationMACAddress = MACAddress.valueOf(new byte[6]); + byte[] dstAddr = new byte[MACAddress.MAC_ADDRESS_LENGTH]; bb.get(dstAddr); - this.destinationMACAddress = MacAddress.of(dstAddr); + this.destinationMACAddress = MACAddress.valueOf(dstAddr); if (this.sourceMACAddress == null) - this.sourceMACAddress = MacAddress.of(new byte[6]); - byte[] srcAddr = new byte[MacAddress.NONE.getLength()]; + this.sourceMACAddress = MACAddress.valueOf(new byte[6]); + byte[] srcAddr = new byte[MACAddress.MAC_ADDRESS_LENGTH]; bb.get(srcAddr); - this.sourceMACAddress = MacAddress.of(srcAddr); + this.sourceMACAddress = MACAddress.valueOf(srcAddr); short etherType = bb.getShort(); if (etherType == (short) 0x8100) { @@ -322,7 +322,7 @@ public class Ethernet extends BasePacket { * @return The macAddress as a byte array */ public static byte[] toMACAddress(String macAddress) { - return MacAddress.of(macAddress).getBytes(); + return MACAddress.valueOf(macAddress).toBytes(); } @@ -333,7 +333,7 @@ public class Ethernet extends BasePacket { * @return a long containing the mac address bytes */ public static long toLong(byte[] macAddress) { - return MacAddress.of(macAddress).getLong(); + return MACAddress.valueOf(macAddress).toLong(); } /** @@ -342,7 +342,7 @@ public class Ethernet extends BasePacket { * @return the bytes of the mac address */ public static byte[] toByteArray(long macAddress) { - return MacAddress.of(macAddress).getBytes(); + return MACAddress.valueOf(macAddress).toBytes(); } /* (non-Javadoc) diff --git a/src/main/java/net/floodlightcontroller/perfmon/IPktInProcessingTimeService.java b/src/main/java/net/floodlightcontroller/perfmon/IPktInProcessingTimeService.java index 0198d16a45863b9c89ce9b51aec59ddc4a787532..cc70487eb8497e4330b59f679196450a57e5559d 100644 --- a/src/main/java/net/floodlightcontroller/perfmon/IPktInProcessingTimeService.java +++ b/src/main/java/net/floodlightcontroller/perfmon/IPktInProcessingTimeService.java @@ -18,7 +18,7 @@ package net.floodlightcontroller.perfmon; import java.util.List; -import org.projectfloodlight.openflow.protocol.OFMessage; +import org.openflow.protocol.OFMessage; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IOFMessageListener; diff --git a/src/main/java/net/floodlightcontroller/perfmon/NullPktInProcessingTime.java b/src/main/java/net/floodlightcontroller/perfmon/NullPktInProcessingTime.java index c10bc93a67a665d270e520fcee7d0b0965335b11..51641ccc7651f80c7aec121763a3b04a7273b7b8 100644 --- a/src/main/java/net/floodlightcontroller/perfmon/NullPktInProcessingTime.java +++ b/src/main/java/net/floodlightcontroller/perfmon/NullPktInProcessingTime.java @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.projectfloodlight.openflow.protocol.OFMessage; +import org.openflow.protocol.OFMessage; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IOFMessageListener; diff --git a/src/main/java/net/floodlightcontroller/perfmon/PktInProcessingTime.java b/src/main/java/net/floodlightcontroller/perfmon/PktInProcessingTime.java index 9e754469d917481db367d2afb67e014866b28c1e..366721120e332de7eb700ed383b521c7afb1a116 100644 --- a/src/main/java/net/floodlightcontroller/perfmon/PktInProcessingTime.java +++ b/src/main/java/net/floodlightcontroller/perfmon/PktInProcessingTime.java @@ -37,8 +37,8 @@ import net.floodlightcontroller.core.module.IFloodlightModule; import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.restserver.IRestApiService; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFType; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/net/floodlightcontroller/routing/BroadcastTree.java b/src/main/java/net/floodlightcontroller/routing/BroadcastTree.java index 28ca79f843a5f4984ab25eb012e95141c062c2bb..0c3703c97f9b3f8f07002203a0a7946c776cf0e7 100644 --- a/src/main/java/net/floodlightcontroller/routing/BroadcastTree.java +++ b/src/main/java/net/floodlightcontroller/routing/BroadcastTree.java @@ -20,48 +20,48 @@ import java.util.HashMap; import net.floodlightcontroller.routing.Link; -import org.projectfloodlight.openflow.types.DatapathId; +import org.openflow.util.HexString; public class BroadcastTree { - protected HashMap<DatapathId, Link> links; - protected HashMap<DatapathId, Integer> costs; + protected HashMap<Long, Link> links; + protected HashMap<Long, Integer> costs; public BroadcastTree() { - links = new HashMap<DatapathId, Link>(); - costs = new HashMap<DatapathId, Integer>(); + links = new HashMap<Long, Link>(); + costs = new HashMap<Long, Integer>(); } - public BroadcastTree(HashMap<DatapathId, Link> links, HashMap<DatapathId, Integer> costs) { + public BroadcastTree(HashMap<Long, Link> links, HashMap<Long, Integer> costs) { this.links = links; this.costs = costs; } - public Link getTreeLink(DatapathId node) { + public Link getTreeLink(long node) { return links.get(node); } - public int getCost(DatapathId node) { + public int getCost(long node) { if (costs.get(node) == null) return -1; return (costs.get(node)); } - public HashMap<DatapathId, Link> getLinks() { + public HashMap<Long, Link> getLinks() { return links; } - public void addTreeLink(DatapathId myNode, Link link) { + public void addTreeLink(long myNode, Link link) { links.put(myNode, link); } public String toString() { StringBuffer sb = new StringBuffer(); - for(DatapathId n: links.keySet()) { - sb.append("[" + n.toString() + ": cost=" + costs.get(n) + ", " + links.get(n) + "]"); + for(long n: links.keySet()) { + sb.append("[" + HexString.toHexString(n) + ": cost=" + costs.get(n) + ", " + links.get(n) + "]"); } return sb.toString(); } - public HashMap<DatapathId, Integer> getCosts() { + public HashMap<Long, Integer> getCosts() { return costs; } } diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java index a3b22551433ab428e38a03f11881e57006412084..2ef42197bd8a3a6a42c1b76df0682e37db5bf10e 100644 --- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java +++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java @@ -32,7 +32,6 @@ import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.annotations.LogMessageCategory; import net.floodlightcontroller.core.annotations.LogMessageDoc; import net.floodlightcontroller.core.annotations.LogMessageDocs; -import net.floodlightcontroller.core.internal.IOFSwitchService; import net.floodlightcontroller.core.util.AppCookie; import net.floodlightcontroller.counter.ICounterStoreService; import net.floodlightcontroller.devicemanager.IDeviceService; @@ -47,26 +46,14 @@ import net.floodlightcontroller.topology.NodePortTuple; import net.floodlightcontroller.util.OFMessageDamper; import net.floodlightcontroller.util.TimedCache; -import org.projectfloodlight.openflow.protocol.OFFlowMod; -import org.projectfloodlight.openflow.protocol.match.Match; -import org.projectfloodlight.openflow.protocol.match.MatchField; -import org.projectfloodlight.openflow.protocol.match.MatchFields; -import org.projectfloodlight.openflow.protocol.OFFlowAdd; -import org.projectfloodlight.openflow.protocol.OFFlowModCommand; -import org.projectfloodlight.openflow.protocol.OFFlowModify; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPacketIn; -import org.projectfloodlight.openflow.protocol.OFPacketOut; -import org.projectfloodlight.openflow.protocol.OFPacketQueue; -import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.protocol.OFUint64; -import org.projectfloodlight.openflow.protocol.action.OFAction; -import org.projectfloodlight.openflow.protocol.action.OFActionOutput; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.OFBufferId; -import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.types.U64; +import org.openflow.protocol.OFFlowMod; +import org.openflow.protocol.OFMatch; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFPacketOut; +import org.openflow.protocol.OFType; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionOutput; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -96,7 +83,6 @@ public abstract class ForwardingBase protected IRoutingService routingEngine; protected ITopologyService topology; protected ICounterStoreService counterStore; - protected IOFSwitchService switchService; protected OFMessageDamper messageDamper; @@ -120,9 +106,9 @@ public abstract class ForwardingBase new Comparator<SwitchPort>() { @Override public int compare(SwitchPort d1, SwitchPort d2) { - DatapathId d1ClusterId = + Long d1ClusterId = topology.getL2DomainId(d1.getSwitchDPID()); - DatapathId d2ClusterId = + Long d2ClusterId = topology.getL2DomainId(d2.getSwitchDPID()); return d1ClusterId.compareTo(d2ClusterId); } @@ -218,35 +204,40 @@ public abstract class ForwardingBase "flow modification to a switch", recommendation=LogMessageDoc.CHECK_SWITCH) }) - public boolean pushRoute(Route route, Match match, - Match.Builder wildcard_hints, + public boolean pushRoute(Route route, OFMatch match, + Integer wildcard_hints, OFPacketIn pi, - DatapathId pinSwitch, + long pinSwitch, long cookie, FloodlightContext cntx, boolean reqeustFlowRemovedNotifn, boolean doFlush, - OFFlowModCommand flowModCommand) { + short flowModCommand) { boolean srcSwitchIncluded = false; - OFFlowAdd.Builder fmb = switchService.getSwitch(pinSwitch).getOFFactory().buildFlowAdd(); - OFActionOutput.Builder actionOutputBuilder = switchService.getSwitch(pinSwitch).getOFFactory().actions().buildOutput(); - actionOutputBuilder.setMaxLen((int) 0xffffffff); + OFFlowMod fm = + (OFFlowMod) floodlightProvider.getOFMessageFactory() + .getMessage(OFType.FLOW_MOD); + OFActionOutput action = new OFActionOutput(); + action.setMaxLength((short)0xffff); List<OFAction> actions = new ArrayList<OFAction>(); - actions.add(actionOutputBuilder.build()); - - fmb.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT) - .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT) - .setBufferId(OFBufferId.NO_BUFFER) - .setCookie(U64.of(cookie)) - .setMatch(match) - .setActions(actions); + actions.add(action); + + fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT) + .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT) + .setBufferId(OFPacketOut.BUFFER_ID_NONE) + .setCookie(cookie) + .setCommand(flowModCommand) + .setMatch(match) + .setActions(actions) + .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH); + List<NodePortTuple> switchPortList = route.getPath(); - for (int indx = switchPortList.size() - 1; indx > 0; indx -= 2) { + for (int indx = switchPortList.size()-1; indx > 0; indx -= 2) { // indx and indx-1 will always have the same switch DPID. - DatapathId switchDPID = switchPortList.get(indx).getNodeId(); - IOFSwitch sw = switchService.getSwitch(switchDPID); + long switchDPID = switchPortList.get(indx).getNodeId(); + IOFSwitch sw = floodlightProvider.getSwitch(switchDPID); if (sw == null) { if (log.isWarnEnabled()) { log.warn("Unable to push route, switch at DPID {} " + @@ -256,29 +247,26 @@ public abstract class ForwardingBase } // set the match. - fmb.setMatch(wildcard(match, sw, wildcard_hints)); + fm.setMatch(wildcard(match, sw, wildcard_hints)); // set buffer id if it is the source switch - // this only appears to set wildcards, which I think are not necessary - /*if (1 == indx) { + if (1 == indx) { // Set the flag to request flow-mod removal notifications only for the // source switch. The removal message is used to maintain the flow // cache. Don't set the flag for ARP messages - TODO generalize check if ((reqeustFlowRemovedNotifn) - && (match.get(MatchField.ARP_OP).getOpcode() != Ethernet.TYPE_ARP)) { - //with new flow cache design, we don't need the flow removal message from switch anymore - //fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM); - // - match.setWildcards(fmb.getMatch().getWildcards()); + && (match.getDataLayerType() != Ethernet.TYPE_ARP)) { + /**with new flow cache design, we don't need the flow removal message from switch anymore + fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM); + */ + match.setWildcards(fm.getMatch().getWildcards()); } - }*/ + } - OFPort outPort = switchPortList.get(indx).getPortId(); - OFPort inPort = switchPortList.get(indx-1).getPortId(); + short outPort = switchPortList.get(indx).getPortId(); + short inPort = switchPortList.get(indx-1).getPortId(); // set input and output ports on the switch - Match m = fmb.getMatch(); - m. - .setInputPort(inPort); + fm.getMatch().setInputPort(inPort); ((OFActionOutput)fm.getActions().get(0)).setPort(outPort); try { @@ -298,7 +286,7 @@ public abstract class ForwardingBase } // Push the packet out the source switch - if (sw.getId().equals(pinSwitch)) { + if (sw.getId() == pinSwitch) { // TODO: Instead of doing a packetOut here we could also // send a flowMod with bufferId set.... pushPacket(sw, pi, false, outPort, cntx); @@ -318,13 +306,13 @@ public abstract class ForwardingBase return srcSwitchIncluded; } - /*protected Match wildcard(Match match, IOFSwitch sw, + protected OFMatch wildcard(OFMatch match, IOFSwitch sw, Integer wildcard_hints) { if (wildcard_hints != null) { return match.clone().setWildcards(wildcard_hints.intValue()); } return match.clone(); - }*/ + } /** * Pushes a packet-out to a switch. If bufferId != BUFFER_ID_NONE we @@ -369,7 +357,7 @@ public abstract class ForwardingBase */ protected void pushPacket(IOFSwitch sw, OFPacketIn pi, boolean useBufferId, - OFPort outport, FloodlightContext cntx) { + short outport, FloodlightContext cntx) { if (pi == null) { return; @@ -393,35 +381,37 @@ public abstract class ForwardingBase new Object[] {sw, pi}); } - //OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT); - OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); + OFPacketOut po = + (OFPacketOut) floodlightProvider.getOFMessageFactory() + .getMessage(OFType.PACKET_OUT); // set actions List<OFAction> actions = new ArrayList<OFAction>(); - //actions.add(new OFActionOutput(outport, (short) 0xffff)); - actions.add(sw.getOFFactory().actions().output(outport, (int) 0xffffffff)); + actions.add(new OFActionOutput(outport, (short) 0xffff)); - pob.setActions(actions); - //.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH); - //short poLength = (short) (po.getActionsLength() + OFPacketOut.MINIMUM_LENGTH); + po.setActions(actions) + .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH); + short poLength = + (short) (po.getActionsLength() + OFPacketOut.MINIMUM_LENGTH); if (useBufferId) { - pob.setBufferId(pi.getBufferId()); + po.setBufferId(pi.getBufferId()); } else { - pob.setBufferId(OFBufferId.NO_BUFFER); + po.setBufferId(OFPacketOut.BUFFER_ID_NONE); } - if (pob.getBufferId() == OFBufferId.NO_BUFFER) { - //poLength += packetData.length; - pob.setData(pi.getData()); + if (po.getBufferId() == OFPacketOut.BUFFER_ID_NONE) { + byte[] packetData = pi.getPacketData(); + poLength += packetData.length; + po.setPacketData(packetData); } - pob.setInPort(pi.getInPort()); - //po.setLength(poLength); + po.setInPort(pi.getInPort()); + po.setLength(poLength); try { - counterStore.updatePktOutFMCounterStoreLocal(sw, pob.build()); - messageDamper.write(sw, pob.build(), cntx); + counterStore.updatePktOutFMCounterStoreLocal(sw, po); + messageDamper.write(sw, po, cntx); } catch (IOException e) { log.error("Failure writing packet out", e); } @@ -439,43 +429,46 @@ public abstract class ForwardingBase */ public void packetOutMultiPort(byte[] packetData, IOFSwitch sw, - OFPort inPort, - Set<OFPort> outPorts, + short inPort, + Set<Integer> outPorts, FloodlightContext cntx) { //setting actions List<OFAction> actions = new ArrayList<OFAction>(); - Iterator<OFPort> j = outPorts.iterator(); + Iterator<Integer> j = outPorts.iterator(); while (j.hasNext()) { - //actions.add(new OFActionOutput(j.next().shortValue(), (short) 0)); - actions.add(sw.getOFFactory().actions().output(j.next(), 0)); + actions.add(new OFActionOutput(j.next().shortValue(), + (short) 0)); } - //OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT); - OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); - pob.setActions(actions); - //po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH * outPorts.size())); + OFPacketOut po = + (OFPacketOut) floodlightProvider.getOFMessageFactory(). + getMessage(OFType.PACKET_OUT); + po.setActions(actions); + po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH * + outPorts.size())); // set buffer-id to BUFFER_ID_NONE, and set in-port to OFPP_NONE - pob.setBufferId(OFBufferId.NO_BUFFER); - pob.setInPort(inPort); + po.setBufferId(OFPacketOut.BUFFER_ID_NONE); + po.setInPort(inPort); // data (note buffer_id is always BUFFER_ID_NONE) and length - //short poLength = (short)(po.getActionsLength() + OFPacketOut.MINIMUM_LENGTH); - //poLength += packetData.length; - pob.setData(packetData); - //po.setLength(poLength); + short poLength = (short)(po.getActionsLength() + + OFPacketOut.MINIMUM_LENGTH); + poLength += packetData.length; + po.setPacketData(packetData); + po.setLength(poLength); try { - counterStore.updatePktOutFMCounterStoreLocal(sw, pob.build()); + counterStore.updatePktOutFMCounterStoreLocal(sw, po); if (log.isTraceEnabled()) { log.trace("write broadcast packet on switch-id={} " + "interfaces={} packet-out={}", - new Object[] {sw.getId(), outPorts, pob.build()}); + new Object[] {sw.getId(), outPorts, po}); } - messageDamper.write(sw, pob.build(), cntx); + messageDamper.write(sw, po, cntx); } catch (IOException e) { log.error("Failure writing packet out", e); @@ -489,10 +482,10 @@ public abstract class ForwardingBase */ public void packetOutMultiPort(OFPacketIn pi, IOFSwitch sw, - OFPort inPort, - Set<OFPort> outPorts, + short inPort, + Set<Integer> outPorts, FloodlightContext cntx) { - packetOutMultiPort(pi.getData(), sw, inPort, outPorts, cntx); + packetOutMultiPort(pi.getPacketData(), sw, inPort, outPorts, cntx); } /** @@ -502,8 +495,8 @@ public abstract class ForwardingBase */ public void packetOutMultiPort(IPacket packet, IOFSwitch sw, - OFPort inPort, - Set<OFPort> outPorts, + short inPort, + Set<Integer> outPorts, FloodlightContext cntx) { packetOutMultiPort(packet.serialize(), sw, inPort, outPorts, cntx); } @@ -522,8 +515,8 @@ public abstract class ForwardingBase IFloodlightProviderService.CONTEXT_PI_PAYLOAD); Long broadcastHash; - broadcastHash = topology.getL2DomainId(sw.getId()).getLong() * prime1 + - pi.getInPort().getPortNumber() * prime2 + eth.hashCode(); + broadcastHash = topology.getL2DomainId(sw.getId()) * prime1 + + pi.getInPort() * prime2 + eth.hashCode(); if (broadcastCache.update(broadcastHash)) { sw.updateBroadcastCache(broadcastHash, pi.getInPort()); return true; @@ -542,7 +535,7 @@ public abstract class ForwardingBase Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); - long hash = pi.getInPort().getPortNumber() * prime2 + eth.hashCode(); + long hash = pi.getInPort() * prime2 + eth.hashCode(); // some FORWARD_OR_FLOOD packets are unicast with unknown destination mac return sw.updateBroadcastCache(hash, pi.getInPort()); @@ -556,43 +549,53 @@ public abstract class ForwardingBase recommendation=LogMessageDoc.CHECK_SWITCH) }) public static boolean - blockHost(IOFSwitchService switchService, - SwitchPort sw_tup, MacAddress host_mac, + blockHost(IFloodlightProviderService floodlightProvider, + SwitchPort sw_tup, long host_mac, short hardTimeout, long cookie) { if (sw_tup == null) { return false; } - IOFSwitch sw = switchService.getSwitch(sw_tup.getSwitchDPID()); + IOFSwitch sw = + floodlightProvider.getSwitch(sw_tup.getSwitchDPID()); if (sw == null) return false; - OFPort inputPort = sw_tup.getPort(); + int inputPort = sw_tup.getPort(); log.debug("blockHost sw={} port={} mac={}", - new Object[] { sw, sw_tup.getPort(), host_mac.getLong() }); + new Object[] { sw, sw_tup.getPort(), Long.valueOf(host_mac) }); // Create flow-mod based on packet-in and src-switch - OFFlowMod.Builder fmb = switchService.getSwitch(sw_tup.getSwitchDPID()).getOFFactory().buildFlowAdd(); - Match.Builder mb = switchService.getSwitch(sw_tup.getSwitchDPID()).getOFFactory().buildMatch(); + OFFlowMod fm = + (OFFlowMod) floodlightProvider.getOFMessageFactory() + .getMessage(OFType.FLOW_MOD); + OFMatch match = new OFMatch(); List<OFAction> actions = new ArrayList<OFAction>(); // Set no action to // drop - mb.setExact(MatchField.IN_PORT, inputPort); - if (host_mac.getLong() != -1L) { - //match.setDataLayerSource(Ethernet.toByteArray(host_mac)).setWildcards(OFMatch.OFPFW_ALL & ~OFMatch.OFPFW_DL_SRC & ~OFMatch.OFPFW_IN_PORT); - mb.setExact(MatchField.ETH_SRC, host_mac); - } /*else { + match.setInputPort((short)inputPort); + if (host_mac != -1L) { + match.setDataLayerSource(Ethernet.toByteArray(host_mac)) + .setWildcards(OFMatch.OFPFW_ALL & ~OFMatch.OFPFW_DL_SRC + & ~OFMatch.OFPFW_IN_PORT); + } else { match.setWildcards(OFMatch.OFPFW_ALL & ~OFMatch.OFPFW_IN_PORT); - }*/ - fmb.setCookie(U64.of(cookie)) + } + fm.setCookie(cookie) .setHardTimeout(hardTimeout) .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT) - .setBufferId(OFBufferId.NO_BUFFER) - .setMatch(mb.build()) - .setActions(actions); - - log.debug("write drop flow-mod sw={} match={} flow-mod={}", - new Object[] { sw, mb.build(), fmb.build() }); - // TODO: can't use the message damper sine this method is static - sw.write(fmb.build(), null); + .setBufferId(OFPacketOut.BUFFER_ID_NONE) + .setMatch(match) + .setActions(actions) + .setLengthU(OFFlowMod.MINIMUM_LENGTH); // +OFActionOutput.MINIMUM_LENGTH); + + try { + log.debug("write drop flow-mod sw={} match={} flow-mod={}", + new Object[] { sw, match, fm }); + // TODO: can't use the message damper sine this method is static + sw.write(fm, null); + } catch (IOException e) { + log.error("Failure writing deny flow mod", e); + return false; + } return true; } diff --git a/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java b/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java index 9c7a285970819db2cd48ebe92554ce7dc6d37fe3..ab09375486b9f61b2cfa98b83a5e9eeb483bd20b 100644 --- a/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java +++ b/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java @@ -19,8 +19,6 @@ package net.floodlightcontroller.routing; import java.util.List; -import org.projectfloodlight.openflow.protocol.match.Match; - import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.FloodlightContextStore; import net.floodlightcontroller.devicemanager.IDevice; @@ -59,8 +57,8 @@ public interface IRoutingDecision { public void addDestinationDevice(IDevice d); public List<SwitchPort> getMulticastInterfaces(); public void setMulticastInterfaces(List<SwitchPort> lspt); - public Match.Builder getWildcards(); - public void setWildcards(Match.Builder wildcards); + public Integer getWildcards(); + public void setWildcards(Integer wildcards); public short getHardTimeout(); public void setHardTimeout(short hardTimeout); } diff --git a/src/main/java/net/floodlightcontroller/routing/IRoutingService.java b/src/main/java/net/floodlightcontroller/routing/IRoutingService.java index 6cff6f533a75e0a0b03cdab7a27c83fc61e00696..a3d77a83ba6309c02c6f99c19d2523eab2ecfa3b 100644 --- a/src/main/java/net/floodlightcontroller/routing/IRoutingService.java +++ b/src/main/java/net/floodlightcontroller/routing/IRoutingService.java @@ -19,9 +19,6 @@ package net.floodlightcontroller.routing; import java.util.ArrayList; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFPort; - import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.routing.Route; @@ -36,7 +33,7 @@ public interface IRoutingService extends IFloodlightService { * @param dst Destination switch DPID. * @param cookie cookie (usage determined by implementation; ignored by topology instance now). */ - public Route getRoute(DatapathId src, DatapathId dst, long cookie); + public Route getRoute(long src, long dst, long cookie); /** * Provides a route between src and dst, with option to allow or @@ -46,7 +43,7 @@ public interface IRoutingService extends IFloodlightService { * @param cookie cookie (usage determined by implementation; ignored by topology instance now). * @param tunnelEnabled boolean option. */ - public Route getRoute(DatapathId src, DatapathId dst, long cookie, boolean tunnelEnabled); + public Route getRoute(long src, long dst, long cookie, boolean tunnelEnabled); /** * Provides a route between srcPort on src and dstPort on dst. @@ -56,8 +53,8 @@ public interface IRoutingService extends IFloodlightService { * @param dstPort dstPort on Destination switch. * @param cookie cookie (usage determined by implementation; ignored by topology instance now). */ - public Route getRoute(DatapathId srcId, OFPort srcPort, - DatapathId dstId, OFPort dstPort, long cookie); + public Route getRoute(long srcId, short srcPort, + long dstId, short dstPort, long cookie); /** * Provides a route between srcPort on src and dstPort on dst. @@ -68,21 +65,21 @@ public interface IRoutingService extends IFloodlightService { * @param cookie cookie (usage determined by implementation; ignored by topology instance now). * @param tunnelEnabled boolean option. */ - public Route getRoute(DatapathId srcId, OFPort srcPort, - DatapathId dstId, OFPort dstPort, long cookie, + public Route getRoute(long srcId, short srcPort, + long dstId, short dstPort, long cookie, boolean tunnelEnabled); /** return all routes, if available */ - public ArrayList<Route> getRoutes(DatapathId longSrcDpid, DatapathId longDstDpid, boolean tunnelEnabled); + public ArrayList<Route> getRoutes(long longSrcDpid, long longDstDpid, boolean tunnelEnabled); /** Check if a route exists between src and dst, including tunnel links * in the path. */ - public boolean routeExists(DatapathId src, DatapathId dst); + public boolean routeExists(long src, long dst); /** Check if a route exists between src and dst, with option to have * or not have tunnels as part of the path. */ - public boolean routeExists(DatapathId src, DatapathId dst, boolean tunnelEnabled); + public boolean routeExists(long src, long dst, boolean tunnelEnabled); } diff --git a/src/main/java/net/floodlightcontroller/routing/Link.java b/src/main/java/net/floodlightcontroller/routing/Link.java index a2f125f5a5b2cd73c9ce6bfaa65e4b56e87d6ff6..561ae04c09ba4b4d3849763da46796dff4c03f95 100755 --- a/src/main/java/net/floodlightcontroller/routing/Link.java +++ b/src/main/java/net/floodlightcontroller/routing/Link.java @@ -18,28 +18,34 @@ package net.floodlightcontroller.routing; import com.fasterxml.jackson.annotation.JsonProperty; - -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFPort; +import org.openflow.util.HexString; public class Link implements Comparable<Link> { @JsonProperty("src-switch") - private DatapathId src; + private long src; @JsonProperty("src-port") - private OFPort srcPort; + private short srcPort; @JsonProperty("dst-switch") - private DatapathId dst; + private long dst; @JsonProperty("dst-port") - private OFPort dstPort; + private short dstPort; - public Link(DatapathId srcId, OFPort srcPort, DatapathId dstId, OFPort dstPort) { + public Link(long srcId, short srcPort, long dstId, short dstPort) { this.src = srcId; this.srcPort = srcPort; this.dst = dstId; this.dstPort = dstPort; } + // Convenience method + public Link(long srcId, int srcPort, long dstId, int dstPort) { + this.src = srcId; + this.srcPort = (short) srcPort; + this.dst = dstId; + this.dstPort = (short) dstPort; + } + /* * Do not use this constructor. Used primarily for JSON * Serialization/Deserialization @@ -48,35 +54,35 @@ public class Link implements Comparable<Link> { super(); } - public DatapathId getSrc() { + public long getSrc() { return src; } - public OFPort getSrcPort() { + public short getSrcPort() { return srcPort; } - public DatapathId getDst() { + public long getDst() { return dst; } - public OFPort getDstPort() { + public short getDstPort() { return dstPort; } - public void setSrc(DatapathId src) { + public void setSrc(long src) { this.src = src; } - public void setSrcPort(OFPort srcPort) { + public void setSrcPort(short srcPort) { this.srcPort = srcPort; } - public void setDst(DatapathId dst) { + public void setDst(long dst) { this.dst = dst; } - public void setDstPort(OFPort dstPort) { + public void setDstPort(short dstPort) { this.dstPort = dstPort; } @@ -84,10 +90,10 @@ public class Link implements Comparable<Link> { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + (int) (dst.getLong() ^ (dst.getLong() >>> 32)); - result = prime * result + dstPort.getPortNumber(); - result = prime * result + (int) (src.getLong() ^ (src.getLong() >>> 32)); - result = prime * result + srcPort.getPortNumber(); + result = prime * result + (int) (dst ^ (dst >>> 32)); + result = prime * result + dstPort; + result = prime * result + (int) (src ^ (src >>> 32)); + result = prime * result + srcPort; return result; } @@ -114,36 +120,35 @@ public class Link implements Comparable<Link> { @Override public String toString() { - return "Link [src=" + this.src.toString() + return "Link [src=" + HexString.toHexString(this.src) + " outPort=" - + srcPort.toString() - + ", dst=" + this.dst.toString() + + (srcPort & 0xffff) + + ", dst=" + HexString.toHexString(this.dst) + ", inPort=" - + dstPort.toString() + + (dstPort & 0xffff) + "]"; } - //TODO @Ryan there was some short 0xFFFF bitmasking here when ports were shorts. I don't get what that did other than just allow all bits of the short (16), so I just stringified the whole thing public String toKeyString() { - return (this.src.toString() + "|" + - this.srcPort.toString() + "|" + - this.dst.toString() + "|" + - this.dstPort.toString()); + return (HexString.toHexString(this.src) + "|" + + (this.srcPort & 0xffff) + "|" + + HexString.toHexString(this.dst) + "|" + + (this.dstPort & 0xffff) ); } @Override public int compareTo(Link a) { // compare link based on natural ordering - src id, src port, dst id, dst port if (this.getSrc() != a.getSrc()) - return (int) (this.getSrc().getLong() - a.getSrc().getLong()); + return (int) (this.getSrc() - a.getSrc()); if (this.getSrcPort() != a.getSrcPort()) - return (int) (this.getSrc().getLong() - a.getSrc().getLong()); + return (int) (this.getSrc() - a.getSrc()); if (this.getDst() != a.getDst()) - return (int) (this.getDst().getLong() - a.getDst().getLong()); + return (int) (this.getDst() - a.getDst()); - return this.getDstPort().getPortNumber() - a.getDstPort().getPortNumber(); + return this.getDstPort() - a.getDstPort(); } } diff --git a/src/main/java/net/floodlightcontroller/routing/Route.java b/src/main/java/net/floodlightcontroller/routing/Route.java index f41a9aaf1e159249e4eb831da30cf80abe993ad5..da00d50b7b82a3d3498b1523543a8558abbd579a 100755 --- a/src/main/java/net/floodlightcontroller/routing/Route.java +++ b/src/main/java/net/floodlightcontroller/routing/Route.java @@ -20,8 +20,6 @@ package net.floodlightcontroller.routing; import java.util.ArrayList; import java.util.List; -import org.projectfloodlight.openflow.types.DatapathId; - import net.floodlightcontroller.topology.NodePortTuple; /** @@ -41,7 +39,7 @@ public class Route implements Comparable<Route> { this.routeCount = 0; // useful if multipath routing available } - public Route(DatapathId src, DatapathId dst) { + public Route(Long src, Long dst) { super(); this.id = new RouteId(src, dst); this.switchPorts = new ArrayList<NodePortTuple>(); diff --git a/src/main/java/net/floodlightcontroller/routing/RouteId.java b/src/main/java/net/floodlightcontroller/routing/RouteId.java index bd80f4d2c6727ab238ff18566896db16aae8223e..511db735969aa849d619783cdd88229d1922b27a 100755 --- a/src/main/java/net/floodlightcontroller/routing/RouteId.java +++ b/src/main/java/net/floodlightcontroller/routing/RouteId.java @@ -17,7 +17,7 @@ package net.floodlightcontroller.routing; -import org.projectfloodlight.openflow.types.DatapathId; +import org.openflow.util.HexString; /** * Stores the endpoints of a route, in this case datapath ids @@ -25,37 +25,37 @@ import org.projectfloodlight.openflow.types.DatapathId; * @author David Erickson (daviderickson@cs.stanford.edu) */ public class RouteId implements Cloneable, Comparable<RouteId> { - protected DatapathId src; - protected DatapathId dst; + protected Long src; + protected Long dst; protected long cookie; - public RouteId(DatapathId src, DatapathId dst) { + public RouteId(Long src, Long dst) { super(); this.src = src; this.dst = dst; this.cookie = 0; } - public RouteId(DatapathId src, DatapathId dst, long cookie) { + public RouteId(Long src, Long dst, long cookie) { super(); this.src = src; this.dst = dst; this.cookie = cookie; } - public DatapathId getSrc() { + public Long getSrc() { return src; } - public void setSrc(DatapathId src) { + public void setSrc(Long src) { this.src = src; } - public DatapathId getDst() { + public Long getDst() { return dst; } - public void setDst(DatapathId dst) { + public void setDst(Long dst) { this.dst = dst; } @@ -103,8 +103,8 @@ public class RouteId implements Cloneable, Comparable<RouteId> { @Override public String toString() { - return "RouteId [src=" + this.src.toString() + " dst=" - + this.dst.toString() + "]"; + return "RouteId [src=" + HexString.toHexString(this.src) + " dst=" + + HexString.toHexString(this.dst) + "]"; } @Override diff --git a/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java b/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java index 2d487f409ca01c9f7d0cbe859521be2b9dcd0a29..aa4b3dde823ab75a38671e53767294d1964012f3 100644 --- a/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java +++ b/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java @@ -20,10 +20,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.projectfloodlight.openflow.protocol.match.Match; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFPort; - import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.devicemanager.IDevice; import net.floodlightcontroller.devicemanager.SwitchPort; @@ -32,15 +28,15 @@ import net.floodlightcontroller.devicemanager.SwitchPort; public class RoutingDecision implements IRoutingDecision { protected RoutingAction action; - protected Match.Builder wildcards; + protected Integer wildcards; protected short hardTimeout; protected SwitchPort srcPort; protected IDevice srcDevice; protected List<IDevice> destDevices; protected List<SwitchPort> broadcastIntertfaces; - public RoutingDecision(DatapathId swDipd, - OFPort inPort, + public RoutingDecision(long swDipd, + short inPort, IDevice srcDevice, RoutingAction action) { this.srcPort = new SwitchPort(swDipd, inPort); @@ -97,12 +93,12 @@ public class RoutingDecision implements IRoutingDecision { } @Override - public Match.Builder getWildcards() { + public Integer getWildcards() { return this.wildcards; } @Override - public void setWildcards(Match.Builder wildcards) { + public void setWildcards(Integer wildcards) { this.wildcards = wildcards; } @@ -124,6 +120,6 @@ public class RoutingDecision implements IRoutingDecision { public String toString() { return "action " + action + " wildcard " + - ((wildcards == null) ? null : wildcards.toString()); + ((wildcards == null) ? null : "0x"+Integer.toHexString(wildcards.intValue())); } } diff --git a/src/main/java/net/floodlightcontroller/topology/Cluster.java b/src/main/java/net/floodlightcontroller/topology/Cluster.java index 914740f10246698c95c7cf0085cb09465c79a709..e4ea23b8c97673f1d764b58211dd54f71f07b77c 100644 --- a/src/main/java/net/floodlightcontroller/topology/Cluster.java +++ b/src/main/java/net/floodlightcontroller/topology/Cluster.java @@ -23,37 +23,37 @@ import java.util.Set; import net.floodlightcontroller.routing.Link; -import org.projectfloodlight.openflow.types.DatapathId; +import org.openflow.util.HexString; public class Cluster { - protected DatapathId id; // the lowest id of the nodes - protected Map<DatapathId, Set<Link>> links; // set of links connected to a node. + protected long id; // the lowest id of the nodes + protected Map<Long, Set<Link>> links; // set of links connected to a node. public Cluster() { - id = DatapathId.NONE; - links = new HashMap<DatapathId, Set<Link>>(); + id = Long.MAX_VALUE; + links = new HashMap<Long, Set<Link>>(); } - public DatapathId getId() { + public long getId() { return id; } - public void setId(DatapathId id) { + public void setId(long id) { this.id = id; } - public Map<DatapathId, Set<Link>> getLinks() { + public Map<Long, Set<Link>> getLinks() { return links; } - public Set<DatapathId> getNodes() { + public Set<Long> getNodes() { return links.keySet(); } - void add(DatapathId n) { + void add(long n) { if (links.containsKey(n) == false) { links.put(n, new HashSet<Link>()); - if (n.getLong() < id.getLong()) id = n; + if (n < id) id = n; } } @@ -67,7 +67,7 @@ public class Cluster { @Override public int hashCode() { - return (int) (id.getLong() + id.getLong() >>>32); + return (int) (id + id >>>32); } @Override @@ -84,6 +84,6 @@ public class Cluster { } public String toString() { - return "[Cluster id=" + id.toString() + ", " + links.keySet() + "]"; + return "[Cluster id=" + HexString.toHexString(id) + ", " + links.keySet() + "]"; } } diff --git a/src/main/java/net/floodlightcontroller/topology/ITopologyService.java b/src/main/java/net/floodlightcontroller/topology/ITopologyService.java index d6bd177550852501cded815cc2132abc9dc868b0..d518b80d21320095b9baaa3f5223768c414a414a 100644 --- a/src/main/java/net/floodlightcontroller/topology/ITopologyService.java +++ b/src/main/java/net/floodlightcontroller/topology/ITopologyService.java @@ -19,195 +19,192 @@ package net.floodlightcontroller.topology; import java.util.Date; import java.util.Set; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFPort; - import net.floodlightcontroller.core.module.IFloodlightService; public interface ITopologyService extends IFloodlightService { - public void addListener(ITopologyListener listener); - - public Date getLastUpdateTime(); - - /** - * Query to determine if devices must be learned on a given switch port. - */ - public boolean isAttachmentPointPort(DatapathId switchid, OFPort port); - public boolean isAttachmentPointPort(DatapathId switchid, OFPort port, - boolean tunnelEnabled); - - public DatapathId getOpenflowDomainId(DatapathId switchId); - public DatapathId getOpenflowDomainId(DatapathId switchId, boolean tunnelEnabled); - - /** - * Returns the identifier of the L2 domain of a given switch. - * @param switchId The DPID of the switch in long form - * @return The DPID of the switch that is the key for the cluster - */ - public DatapathId getL2DomainId(DatapathId switchId); - public DatapathId getL2DomainId(DatapathId switchId, boolean tunnelEnabled); - - /** - * Queries whether two switches are in the same cluster. - * @param switch1 - * @param switch2 - * @return true if the switches are in the same cluster - */ - public boolean inSameOpenflowDomain(DatapathId switch1, DatapathId switch2); - public boolean inSameOpenflowDomain(DatapathId switch1, DatapathId switch2, - boolean tunnelEnabled); - - public Set<DatapathId> getSwitchesInOpenflowDomain(DatapathId switchDPID); - public Set<DatapathId> getSwitchesInOpenflowDomain(DatapathId switchDPID, - boolean tunnelEnabled); - - /** - * Queries whether two switches are in the same island. - * Currently, island and cluster are the same. In future, - * islands could be different than clusters. - * @param switch1 - * @param switch2 - * @return True of they are in the same island, false otherwise - */ - public boolean inSameL2Domain(DatapathId switch1, DatapathId switch2); - public boolean inSameL2Domain(DatapathId switch1, DatapathId switch2, - boolean tunnelEnabled); - - public boolean isBroadcastDomainPort(DatapathId sw, OFPort port); - public boolean isBroadcastDomainPort(DatapathId sw, OFPort port, - boolean tunnelEnabled); - - - public boolean isAllowed(DatapathId sw, OFPort portId); - public boolean isAllowed(DatapathId sw, OFPort portId, boolean tunnelEnabled); - - /** - * Indicates if an attachment point on the new switch port is consistent - * with the attachment point on the old switch port or not. - */ - public boolean isConsistent(DatapathId oldSw, OFPort oldPort, - DatapathId newSw, OFPort newPort); - public boolean isConsistent(DatapathId oldSw, OFPort oldPort, - DatapathId newSw, OFPort newPort, - boolean tunnelEnabled); - - /** - * Indicates if the two switch ports are connected to the same - * broadcast domain or not. - * @param s1 - * @param p1 - * @param s2 - * @param p2 - * @return - */ - public boolean isInSameBroadcastDomain(DatapathId s1, OFPort p1, - DatapathId s2, OFPort p2); - public boolean isInSameBroadcastDomain(DatapathId s1, OFPort p1, - DatapathId s2, OFPort p2, - boolean tunnelEnabled); - - /** - * Gets a list of ports on a given switch that are known to topology. - * @param sw The switch DPID in long - * @return The set of ports on this switch - */ - public Set<OFPort> getPortsWithLinks(DatapathId sw); - public Set<OFPort> getPortsWithLinks(DatapathId sw, boolean tunnelEnabled); - - /** Get broadcast ports on a target switch for a given attachmentpoint - * point port. - */ - public Set<OFPort> getBroadcastPorts(DatapathId targetSw, DatapathId src, OFPort srcPort); - - public Set<OFPort> getBroadcastPorts(DatapathId targetSw, DatapathId src, OFPort srcPort, - boolean tunnelEnabled); - - /** - * - */ - public boolean isIncomingBroadcastAllowed(DatapathId sw, OFPort portId); - public boolean isIncomingBroadcastAllowed(DatapathId sw, OFPort portId, - boolean tunnelEnabled); - - - /** Get the proper outgoing switchport for a given pair of src-dst - * switchports. - */ - public NodePortTuple getOutgoingSwitchPort(DatapathId src, OFPort srcPort, - DatapathId dst, OFPort dstPort); - - - public NodePortTuple getOutgoingSwitchPort(DatapathId src, OFPort srcPort, - DatapathId dst, OFPort dstPort, - boolean tunnelEnabled); - - - public NodePortTuple getIncomingSwitchPort(DatapathId src, OFPort srcPort, - DatapathId dst, OFPort dstPort); - public NodePortTuple getIncomingSwitchPort(DatapathId src, OFPort srcPort, - DatapathId dst, OFPort dstPort, - boolean tunnelEnabled); - - /** - * If the dst is not allowed by the higher-level topology, - * this method provides the topologically equivalent broadcast port. - * @param src - * @param dst - * @return the allowed broadcast port - */ - public NodePortTuple - getAllowedOutgoingBroadcastPort(DatapathId src, - OFPort srcPort, - DatapathId dst, - OFPort dstPort); - - public NodePortTuple - getAllowedOutgoingBroadcastPort(DatapathId src, - OFPort srcPort, - DatapathId dst, - OFPort dstPort, - boolean tunnelEnabled); - - /** - * If the src broadcast domain port is not allowed for incoming - * broadcast, this method provides the topologically equivalent - * incoming broadcast-allowed - * src port. - * @param src - * @param dst - * @return the allowed broadcast port - */ - public NodePortTuple - getAllowedIncomingBroadcastPort(DatapathId src, - OFPort srcPort); - - public NodePortTuple - getAllowedIncomingBroadcastPort(DatapathId src, - OFPort srcPort, - boolean tunnelEnabled); - - - /** - * Gets the set of ports that belong to a broadcast domain. - * @return The set of ports that belong to a broadcast domain. - */ - public Set<NodePortTuple> getBroadcastDomainPorts(); - public Set<NodePortTuple> getTunnelPorts(); - - - /** - * Returns a set of blocked ports. The set of blocked - * ports is the union of all the blocked ports across all - * instances. - * @return - */ - public Set<NodePortTuple> getBlockedPorts(); - - /** - * Returns the enabled, non quarantined ports of the given switch. Returns - * an empty set if switch doesn't exists, doesn't have any enabled port, or - * has only quarantined ports. Will never return null. - */ - public Set<OFPort> getPorts(DatapathId sw); + public void addListener(ITopologyListener listener); + + public Date getLastUpdateTime(); + + /** + * Query to determine if devices must be learned on a given switch port. + */ + public boolean isAttachmentPointPort(long switchid, short port); + public boolean isAttachmentPointPort(long switchid, short port, + boolean tunnelEnabled); + + public long getOpenflowDomainId(long switchId); + public long getOpenflowDomainId(long switchId, boolean tunnelEnabled); + + /** + * Returns the identifier of the L2 domain of a given switch. + * @param switchId The DPID of the switch in long form + * @return The DPID of the switch that is the key for the cluster + */ + public long getL2DomainId(long switchId); + public long getL2DomainId(long switchId, boolean tunnelEnabled); + + /** + * Queries whether two switches are in the same cluster. + * @param switch1 + * @param switch2 + * @return true if the switches are in the same cluster + */ + public boolean inSameOpenflowDomain(long switch1, long switch2); + public boolean inSameOpenflowDomain(long switch1, long switch2, + boolean tunnelEnabled); + + public Set<Long> getSwitchesInOpenflowDomain(long switchDPID); + public Set<Long> getSwitchesInOpenflowDomain(long switchDPID, + boolean tunnelEnabled); + + /** + * Queries whether two switches are in the same island. + * Currently, island and cluster are the same. In future, + * islands could be different than clusters. + * @param switch1 + * @param switch2 + * @return True of they are in the same island, false otherwise + */ + public boolean inSameL2Domain(long switch1, long switch2); + public boolean inSameL2Domain(long switch1, long switch2, + boolean tunnelEnabled); + + public boolean isBroadcastDomainPort(long sw, short port); + public boolean isBroadcastDomainPort(long sw, short port, + boolean tunnelEnabled); + + + public boolean isAllowed(long sw, short portId); + public boolean isAllowed(long sw, short portId, boolean tunnelEnabled); + + /** + * Indicates if an attachment point on the new switch port is consistent + * with the attachment point on the old switch port or not. + */ + public boolean isConsistent(long oldSw, short oldPort, + long newSw, short newPort); + public boolean isConsistent(long oldSw, short oldPort, + long newSw, short newPort, + boolean tunnelEnabled); + + /** + * Indicates if the two switch ports are connected to the same + * broadcast domain or not. + * @param s1 + * @param p1 + * @param s2 + * @param p2 + * @return + */ + public boolean isInSameBroadcastDomain(long s1, short p1, + long s2, short p2); + public boolean isInSameBroadcastDomain(long s1, short p1, + long s2, short p2, + boolean tunnelEnabled); + + /** + * Gets a list of ports on a given switch that are known to topology. + * @param sw The switch DPID in long + * @return The set of ports on this switch + */ + public Set<Short> getPortsWithLinks(long sw); + public Set<Short> getPortsWithLinks(long sw, boolean tunnelEnabled); + + /** Get broadcast ports on a target switch for a given attachmentpoint + * point port. + */ + public Set<Short> getBroadcastPorts(long targetSw, long src, short srcPort); + + public Set<Short> getBroadcastPorts(long targetSw, long src, short srcPort, + boolean tunnelEnabled); + + /** + * + */ + public boolean isIncomingBroadcastAllowed(long sw, short portId); + public boolean isIncomingBroadcastAllowed(long sw, short portId, + boolean tunnelEnabled); + + + /** Get the proper outgoing switchport for a given pair of src-dst + * switchports. + */ + public NodePortTuple getOutgoingSwitchPort(long src, short srcPort, + long dst, short dstPort); + + + public NodePortTuple getOutgoingSwitchPort(long src, short srcPort, + long dst, short dstPort, + boolean tunnelEnabled); + + + public NodePortTuple getIncomingSwitchPort(long src, short srcPort, + long dst, short dstPort); + public NodePortTuple getIncomingSwitchPort(long src, short srcPort, + long dst, short dstPort, + boolean tunnelEnabled); + + /** + * If the dst is not allowed by the higher-level topology, + * this method provides the topologically equivalent broadcast port. + * @param src + * @param dst + * @return the allowed broadcast port + */ + public NodePortTuple + getAllowedOutgoingBroadcastPort(long src, + short srcPort, + long dst, + short dstPort); + + public NodePortTuple + getAllowedOutgoingBroadcastPort(long src, + short srcPort, + long dst, + short dstPort, + boolean tunnelEnabled); + + /** + * If the src broadcast domain port is not allowed for incoming + * broadcast, this method provides the topologically equivalent + * incoming broadcast-allowed + * src port. + * @param src + * @param dst + * @return the allowed broadcast port + */ + public NodePortTuple + getAllowedIncomingBroadcastPort(long src, + short srcPort); + + public NodePortTuple + getAllowedIncomingBroadcastPort(long src, + short srcPort, + boolean tunnelEnabled); + + + /** + * Gets the set of ports that belong to a broadcast domain. + * @return The set of ports that belong to a broadcast domain. + */ + public Set<NodePortTuple> getBroadcastDomainPorts(); + public Set<NodePortTuple> getTunnelPorts(); + + + /** + * Returns a set of blocked ports. The set of blocked + * ports is the union of all the blocked ports across all + * instances. + * @return + */ + public Set<NodePortTuple> getBlockedPorts(); + + /** + * Returns the enabled, non quarantined ports of the given switch. Returns + * an empty set if switch doesn't exists, doesn't have any enabled port, or + * has only quarantined ports. Will never return null. + */ + public Set<Short> getPorts(long sw); } diff --git a/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java b/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java index f7b0a178ac397c9c2bc4bd9135c4053eb1882f96..8c5645143ac0edc54df86582d9b83273bd30ed56 100644 --- a/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java +++ b/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java @@ -21,10 +21,7 @@ import net.floodlightcontroller.core.web.serializers.UShortSerializer; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.util.HexString; +import org.openflow.util.HexString; /** * A NodePortTuple is similar to a SwitchPortTuple @@ -34,46 +31,51 @@ import org.projectfloodlight.openflow.util.HexString; */ public class NodePortTuple implements Comparable<NodePortTuple> { - protected DatapathId nodeId; // switch DPID - protected OFPort portId; // switch port id + protected long nodeId; // switch DPID + protected short portId; // switch port id /** * Creates a NodePortTuple * @param nodeId The DPID of the switch * @param portId The port of the switch */ - public NodePortTuple(DatapathId nodeId, OFPort portId) { + public NodePortTuple(long nodeId, short portId) { this.nodeId = nodeId; this.portId = portId; } + public NodePortTuple(long nodeId, int portId) { + this.nodeId = nodeId; + this.portId = (short) portId; + } + @JsonProperty("switch") @JsonSerialize(using=DPIDSerializer.class) - public DatapathId getNodeId() { + public long getNodeId() { return nodeId; } - public void setNodeId(DatapathId nodeId) { + public void setNodeId(long nodeId) { this.nodeId = nodeId; } @JsonProperty("port") @JsonSerialize(using=UShortSerializer.class) - public OFPort getPortId() { + public short getPortId() { return portId; } - public void setPortId(OFPort portId) { + public void setPortId(short portId) { this.portId = portId; } public String toString() { - return "[id=" + nodeId.toString() + ", port=" + portId.toString() + "]"; + return "[id=" + HexString.toHexString(nodeId) + ", port=" + new Short(portId) + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + (int) (nodeId.getLong() ^ (nodeId.getLong() >>> 32)); - result = prime * result + portId.getPortNumber(); + result = prime * result + (int) (nodeId ^ (nodeId >>> 32)); + result = prime * result + portId; return result; } @@ -100,7 +102,7 @@ public class NodePortTuple implements Comparable<NodePortTuple> { * @return */ public String toKeyString() { - return (nodeId.toString()+ "|" + portId.toString()); + return (HexString.toHexString(nodeId)+ "|" + (portId & 0xffff)); } @Override @@ -109,14 +111,14 @@ public class NodePortTuple implements Comparable<NodePortTuple> { final int EQUAL = 0; final int AFTER = 1; - if (this.getNodeId().getLong() < obj.getNodeId().getLong()) + if (this.getNodeId() < obj.getNodeId()) return BEFORE; - if (this.getNodeId().getLong() > obj.getNodeId().getLong()) + if (this.getNodeId() > obj.getNodeId()) return AFTER; - if (this.getPortId().getPortNumber() < obj.getPortId().getPortNumber()) + if (this.getPortId() < obj.getPortId()) return BEFORE; - if (this.getPortId().getPortNumber() > obj.getPortId().getPortNumber()) + if (this.getPortId() > obj.getPortId()) return AFTER; return EQUAL; diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java index bc5d626783048f653c6c9ba63ac14f6a459709f0..a143d45e73e6bff892766999926487fb6fcb2f03 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java @@ -25,8 +25,7 @@ import java.util.Map; import java.util.PriorityQueue; import java.util.Set; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFPort; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,7 +59,7 @@ public class TopologyInstance { protected static Logger log = LoggerFactory.getLogger(TopologyInstance.class); - protected Map<DatapathId, Set<OFPort>> switchPorts; // Set of ports for each switch + protected Map<Long, Set<Short>> switchPorts; // Set of ports for each switch /** Set of switch ports that are marked as blocked. A set of blocked * switch ports may be provided at the time of instantiation. In addition, * we may add additional ports to this set. @@ -70,17 +69,17 @@ public class TopologyInstance { /** Set of links that are blocked. */ protected Set<Link> blockedLinks; - protected Set<DatapathId> switches; + protected Set<Long> switches; protected Set<NodePortTuple> broadcastDomainPorts; protected Set<NodePortTuple> tunnelPorts; protected Set<Cluster> clusters; // set of openflow domains - protected Map<DatapathId, Cluster> switchClusterMap; // switch to OF domain map + protected Map<Long, Cluster> switchClusterMap; // switch to OF domain map // States for routing - protected Map<DatapathId, BroadcastTree> destinationRootedTrees; - protected Map<DatapathId, Set<NodePortTuple>> clusterBroadcastNodePorts; - protected Map<DatapathId, BroadcastTree> clusterBroadcastTrees; + protected Map<Long, BroadcastTree> destinationRootedTrees; + protected Map<Long, Set<NodePortTuple>> clusterBroadcastNodePorts; + protected Map<Long, BroadcastTree> clusterBroadcastTrees; protected class PathCacheLoader extends CacheLoader<RouteId, Route> { TopologyInstance ti; @@ -100,8 +99,8 @@ public class TopologyInstance { protected LoadingCache<RouteId, Route> pathcache; public TopologyInstance() { - this.switches = new HashSet<DatapathId>(); - this.switchPorts = new HashMap<DatapathId, Set<OFPort>>(); + this.switches = new HashSet<Long>(); + this.switchPorts = new HashMap<Long, Set<Short>>(); this.switchPortLinks = new HashMap<NodePortTuple, Set<Link>>(); this.broadcastDomainPorts = new HashSet<NodePortTuple>(); this.tunnelPorts = new HashSet<NodePortTuple>(); @@ -109,12 +108,12 @@ public class TopologyInstance { this.blockedLinks = new HashSet<Link>(); } - public TopologyInstance(Map<DatapathId, Set<OFPort>> switchPorts, + public TopologyInstance(Map<Long, Set<Short>> switchPorts, Map<NodePortTuple, Set<Link>> switchPortLinks, Set<NodePortTuple> broadcastDomainPorts) { - this.switches = new HashSet<DatapathId>(switchPorts.keySet()); - this.switchPorts = new HashMap<DatapathId, Set<OFPort>>(switchPorts); + this.switches = new HashSet<Long>(switchPorts.keySet()); + this.switchPorts = new HashMap<Long, Set<Short>>(switchPorts); this.switchPortLinks = new HashMap<NodePortTuple, Set<Link>>(switchPortLinks); this.broadcastDomainPorts = new HashSet<NodePortTuple>(broadcastDomainPorts); @@ -123,19 +122,19 @@ public class TopologyInstance { this.blockedLinks = new HashSet<Link>(); clusters = new HashSet<Cluster>(); - switchClusterMap = new HashMap<DatapathId, Cluster>(); + switchClusterMap = new HashMap<Long, Cluster>(); } - public TopologyInstance(Map<DatapathId, Set<OFPort>> switchPorts, + public TopologyInstance(Map<Long, Set<Short>> switchPorts, Set<NodePortTuple> blockedPorts, Map<NodePortTuple, Set<Link>> switchPortLinks, Set<NodePortTuple> broadcastDomainPorts, Set<NodePortTuple> tunnelPorts){ // copy these structures - this.switches = new HashSet<DatapathId>(switchPorts.keySet()); - this.switchPorts = new HashMap<DatapathId, Set<OFPort>>(); - for(DatapathId sw: switchPorts.keySet()) { - this.switchPorts.put(sw, new HashSet<OFPort>(switchPorts.get(sw))); + this.switches = new HashSet<Long>(switchPorts.keySet()); + this.switchPorts = new HashMap<Long, Set<Short>>(); + for(long sw: switchPorts.keySet()) { + this.switchPorts.put(sw, new HashSet<Short>(switchPorts.get(sw))); } this.blockedPorts = new HashSet<NodePortTuple>(blockedPorts); @@ -149,10 +148,10 @@ public class TopologyInstance { blockedLinks = new HashSet<Link>(); clusters = new HashSet<Cluster>(); - switchClusterMap = new HashMap<DatapathId, Cluster>(); - destinationRootedTrees = new HashMap<DatapathId, BroadcastTree>(); - clusterBroadcastTrees = new HashMap<DatapathId, BroadcastTree>(); - clusterBroadcastNodePorts = new HashMap<DatapathId, Set<NodePortTuple>>(); + switchClusterMap = new HashMap<Long, Cluster>(); + destinationRootedTrees = new HashMap<Long, BroadcastTree>(); + clusterBroadcastTrees = new HashMap<Long, BroadcastTree>(); + clusterBroadcastNodePorts = new HashMap<Long, Set<NodePortTuple>>(); pathcache = CacheBuilder.newBuilder().concurrencyLevel(4) .maximumSize(1000L) @@ -205,9 +204,9 @@ public class TopologyInstance { } protected void addLinksToOpenflowDomains() { - for(DatapathId s: switches) { + for(long s: switches) { if (switchPorts.get(s) == null) continue; - for (OFPort p: switchPorts.get(s)) { + for (short p: switchPorts.get(s)) { NodePortTuple np = new NodePortTuple(s, p); if (switchPortLinks.get(np) == null) continue; if (isBroadcastDomainPort(np)) continue; @@ -243,17 +242,17 @@ public class TopologyInstance { explanation="The internal state of the topology module is corrupt", recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) public void identifyOpenflowDomains() { - Map<DatapathId, ClusterDFS> dfsList = new HashMap<DatapathId, ClusterDFS>(); + Map<Long, ClusterDFS> dfsList = new HashMap<Long, ClusterDFS>(); if (switches == null) return; - for (DatapathId key: switches) { + for (Long key: switches) { ClusterDFS cdfs = new ClusterDFS(); dfsList.put(key, cdfs); } - Set<DatapathId> currSet = new HashSet<DatapathId>(); + Set<Long> currSet = new HashSet<Long>(); - for (DatapathId sw: switches) { + for (Long sw: switches) { ClusterDFS cdfs = dfsList.get(sw); if (cdfs == null) { log.error("No DFS object for switch {} found.", sw); @@ -292,15 +291,15 @@ public class TopologyInstance { * @param currSet: Set of nodes in the current cluster in formation * @return long: DSF index to be used when a new node is visited */ - private long dfsTraverse (long parentIndex, long currIndex, DatapathId currSw, - Map<DatapathId, ClusterDFS> dfsList, Set <DatapathId> currSet) { + private long dfsTraverse (long parentIndex, long currIndex, long currSw, + Map<Long, ClusterDFS> dfsList, Set <Long> currSet) { //Get the DFS object corresponding to the current switch ClusterDFS currDFS = dfsList.get(currSw); // Get all the links corresponding to this switch - Set<DatapathId> nodesInMyCluster = new HashSet<DatapathId>(); - Set<DatapathId> myCurrSet = new HashSet<DatapathId>(); + Set<Long> nodesInMyCluster = new HashSet<Long>(); + Set<Long> myCurrSet = new HashSet<Long>(); //Assign the DFS object with right values. currDFS.setVisited(true); @@ -310,14 +309,14 @@ public class TopologyInstance { // Traverse the graph through every outgoing link. if (switchPorts.get(currSw) != null){ - for(OFPort p: switchPorts.get(currSw)) { + for(Short p: switchPorts.get(currSw)) { Set<Link> lset = switchPortLinks.get(new NodePortTuple(currSw, p)); if (lset == null) continue; for(Link l:lset) { - DatapathId dstSw = l.getDst(); + long dstSw = l.getDst(); // ignore incoming links. - if (dstSw.equals(currSw)) continue; + if (dstSw == currSw) continue; // ignore if the destination is already added to // another cluster @@ -369,7 +368,7 @@ public class TopologyInstance { // create a new switch cluster and the switches in the current // set to the switch cluster. Cluster sc = new Cluster(); - for(DatapathId sw: currSet){ + for(long sw: currSet){ sc.add(sw); switchClusterMap.put(sw, sc); } @@ -426,8 +425,8 @@ public class TopologyInstance { } protected class NodeDist implements Comparable<NodeDist> { - private final DatapathId node; - public DatapathId getNode() { + private final Long node; + public Long getNode() { return node; } @@ -436,7 +435,7 @@ public class TopologyInstance { return dist; } - public NodeDist(DatapathId node, int dist) { + public NodeDist(Long node, int dist) { this.node = node; this.dist = dist; } @@ -444,7 +443,7 @@ public class TopologyInstance { @Override public int compareTo(NodeDist o) { if (o.dist == this.dist) { - return (int)(this.node.getLong() - o.node.getLong()); + return (int)(this.node - o.node); } return this.dist - o.dist; } @@ -479,34 +478,34 @@ public class TopologyInstance { } } - protected BroadcastTree dijkstra(Cluster c, DatapathId root, + protected BroadcastTree dijkstra(Cluster c, Long root, Map<Link, Integer> linkCost, boolean isDstRooted) { - HashMap<DatapathId, Link> nexthoplinks = new HashMap<DatapathId, Link>(); + HashMap<Long, Link> nexthoplinks = new HashMap<Long, Link>(); //HashMap<Long, Long> nexthopnodes = new HashMap<Long, Long>(); - HashMap<DatapathId, Integer> cost = new HashMap<DatapathId, Integer>(); + HashMap<Long, Integer> cost = new HashMap<Long, Integer>(); int w; - for (DatapathId node: c.links.keySet()) { + for (Long node: c.links.keySet()) { nexthoplinks.put(node, null); //nexthopnodes.put(node, null); cost.put(node, MAX_PATH_WEIGHT); } - HashMap<DatapathId, Boolean> seen = new HashMap<DatapathId, Boolean>(); + HashMap<Long, Boolean> seen = new HashMap<Long, Boolean>(); PriorityQueue<NodeDist> nodeq = new PriorityQueue<NodeDist>(); nodeq.add(new NodeDist(root, 0)); cost.put(root, 0); while (nodeq.peek() != null) { NodeDist n = nodeq.poll(); - DatapathId cnode = n.getNode(); + Long cnode = n.getNode(); int cdist = n.getDist(); if (cdist >= MAX_PATH_WEIGHT) break; if (seen.containsKey(cnode)) continue; seen.put(cnode, true); for (Link link: c.links.get(cnode)) { - DatapathId neighbor; + Long neighbor; if (isDstRooted == true) neighbor = link.getSrc(); else neighbor = link.getDst(); @@ -555,7 +554,7 @@ public class TopologyInstance { } for(Cluster c: clusters) { - for (DatapathId node : c.links.keySet()) { + for (Long node : c.links.keySet()) { BroadcastTree tree = dijkstra(c, node, linkCost, true); destinationRootedTrees.put(node, tree); } @@ -582,9 +581,9 @@ public class TopologyInstance { //log.info("Broadcast Tree {}", tree); Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>(); - Map<DatapathId, Link> links = tree.getLinks(); + Map<Long, Link> links = tree.getLinks(); if (links == null) continue; - for(DatapathId nodeId: links.keySet()) { + for(long nodeId: links.keySet()) { Link l = links.get(nodeId); if (l == null) continue; NodePortTuple npt1 = new NodePortTuple(l.getSrc(), l.getSrcPort()); @@ -598,8 +597,8 @@ public class TopologyInstance { protected Route buildroute(RouteId id) { NodePortTuple npt; - DatapathId srcId = id.getSrc(); - DatapathId dstId = id.getDst(); + long srcId = id.getSrc(); + long dstId = id.getDst(); LinkedList<NodePortTuple> switchPorts = new LinkedList<NodePortTuple>(); @@ -607,7 +606,7 @@ public class TopologyInstance { if (destinationRootedTrees == null) return null; if (destinationRootedTrees.get(dstId) == null) return null; - Map<DatapathId, Link> nexthoplinks = + Map<Long, Link> nexthoplinks = destinationRootedTrees.get(dstId).getLinks(); if (!switches.contains(srcId) || !switches.contains(dstId)) { @@ -641,7 +640,7 @@ public class TopologyInstance { return result; } - protected int getCost(DatapathId srcId, DatapathId dstId) { + protected int getCost(long srcId, long dstId) { BroadcastTree bt = destinationRootedTrees.get(dstId); if (bt == null) return -1; return (bt.getCost(srcId)); @@ -656,7 +655,7 @@ public class TopologyInstance { } // IRoutingEngineService interfaces - protected boolean routeExists(DatapathId srcId, DatapathId dstId) { + protected boolean routeExists(long srcId, long dstId) { BroadcastTree bt = destinationRootedTrees.get(dstId); if (bt == null) return false; Link link = bt.getLinks().get(srcId); @@ -664,19 +663,19 @@ public class TopologyInstance { return true; } - protected Route getRoute(ServiceChain sc, DatapathId srcId, OFPort srcPort, - DatapathId dstId, OFPort dstPort, long cookie) { + protected Route getRoute(ServiceChain sc, long srcId, short srcPort, + long dstId, short dstPort, long cookie) { // Return null the route source and desitnation are the // same switchports. - if (srcId.equals(dstId) && srcPort.equals(dstPort)) + if (srcId == dstId && srcPort == dstPort) return null; List<NodePortTuple> nptList; NodePortTuple npt; Route r = getRoute(srcId, dstId, 0); - if (r == null && !srcId.equals(dstId)) return null; + if (r == null && srcId != dstId) return null; if (r != null) { nptList= new ArrayList<NodePortTuple>(r.getPath()); @@ -696,9 +695,9 @@ public class TopologyInstance { // NOTE: Return a null route if srcId equals dstId. The null route // need not be stored in the cache. Moreover, the LoadingCache will // throw an exception if null route is returned. - protected Route getRoute(DatapathId srcId, DatapathId dstId, long cookie) { + protected Route getRoute(long srcId, long dstId, long cookie) { // Return null route if srcId equals dstId - if (srcId.equals(dstId)) return null; + if (srcId == dstId) return null; RouteId id = new RouteId(srcId, dstId); @@ -726,54 +725,54 @@ public class TopologyInstance { // ITopologyService interface method helpers. // - protected boolean isInternalToOpenflowDomain(DatapathId switchid, OFPort port) { + protected boolean isInternalToOpenflowDomain(long switchid, short port) { return !isAttachmentPointPort(switchid, port); } - public boolean isAttachmentPointPort(DatapathId switchid, OFPort port) { + public boolean isAttachmentPointPort(long switchid, short port) { NodePortTuple npt = new NodePortTuple(switchid, port); if (switchPortLinks.containsKey(npt)) return false; return true; } - protected DatapathId getOpenflowDomainId(DatapathId switchId) { + protected long getOpenflowDomainId(long switchId) { Cluster c = switchClusterMap.get(switchId); if (c == null) return switchId; return c.getId(); } - protected DatapathId getL2DomainId(DatapathId switchId) { + protected long getL2DomainId(long switchId) { return getOpenflowDomainId(switchId); } - protected Set<DatapathId> getSwitchesInOpenflowDomain(DatapathId switchId) { + protected Set<Long> getSwitchesInOpenflowDomain(long switchId) { Cluster c = switchClusterMap.get(switchId); if (c == null) { // The switch is not known to topology as there // are no links connected to it. - Set<DatapathId> nodes = new HashSet<DatapathId>(); + Set<Long> nodes = new HashSet<Long>(); nodes.add(switchId); return nodes; } return (c.getNodes()); } - protected boolean inSameOpenflowDomain(DatapathId switch1, DatapathId switch2) { + protected boolean inSameOpenflowDomain(long switch1, long switch2) { Cluster c1 = switchClusterMap.get(switch1); Cluster c2 = switchClusterMap.get(switch2); if (c1 != null && c2 != null) - return (c1.getId().equals(c2.getId())); - return (switch1.equals(switch2)); + return (c1.getId() == c2.getId()); + return (switch1 == switch2); } - public boolean isAllowed(DatapathId sw, OFPort portId) { + public boolean isAllowed(long sw, short portId) { return true; } protected boolean - isIncomingBroadcastAllowedOnSwitchPort(DatapathId sw, OFPort portId) { + isIncomingBroadcastAllowedOnSwitchPort(long sw, short portId) { if (isInternalToOpenflowDomain(sw, portId)) { - DatapathId clusterId = getOpenflowDomainId(sw); + long clusterId = getOpenflowDomainId(sw); NodePortTuple npt = new NodePortTuple(sw, portId); if (clusterBroadcastNodePorts.get(clusterId).contains(npt)) return true; @@ -782,52 +781,52 @@ public class TopologyInstance { return true; } - public boolean isConsistent(DatapathId oldSw, OFPort oldPort, DatapathId newSw, - OFPort newPort) { + public boolean isConsistent(long oldSw, short oldPort, long newSw, + short newPort) { if (isInternalToOpenflowDomain(newSw, newPort)) return true; - return (oldSw.equals(newSw) && oldPort.equals(newPort)); + return (oldSw == newSw && oldPort == newPort); } protected Set<NodePortTuple> - getBroadcastNodePortsInCluster(DatapathId sw) { - DatapathId clusterId = getOpenflowDomainId(sw); + getBroadcastNodePortsInCluster(long sw) { + long clusterId = getOpenflowDomainId(sw); return clusterBroadcastNodePorts.get(clusterId); } - public boolean inSameBroadcastDomain(DatapathId s1, OFPort p1, DatapathId s2, OFPort p2) { + public boolean inSameBroadcastDomain(long s1, short p1, long s2, short p2) { return false; } - public boolean inSameL2Domain(DatapathId switch1, DatapathId switch2) { + public boolean inSameL2Domain(long switch1, long switch2) { return inSameOpenflowDomain(switch1, switch2); } - public NodePortTuple getOutgoingSwitchPort(DatapathId src, OFPort srcPort, - DatapathId dst, OFPort dstPort) { + public NodePortTuple getOutgoingSwitchPort(long src, short srcPort, + long dst, short dstPort) { // Use this function to redirect traffic if needed. return new NodePortTuple(dst, dstPort); } - public NodePortTuple getIncomingSwitchPort(DatapathId src, OFPort srcPort, - DatapathId dst, OFPort dstPort) { + public NodePortTuple getIncomingSwitchPort(long src, short srcPort, + long dst, short dstPort) { // Use this function to reinject traffic from a // different port if needed. return new NodePortTuple(src, srcPort); } - public Set<DatapathId> getSwitches() { + public Set<Long> getSwitches() { return switches; } - public Set<OFPort> getPortsWithLinks(DatapathId sw) { + public Set<Short> getPortsWithLinks(long sw) { return switchPorts.get(sw); } - public Set<OFPort> getBroadcastPorts(DatapathId targetSw, DatapathId src, OFPort srcPort) { - Set<OFPort> result = new HashSet<OFPort>(); - DatapathId clusterId = getOpenflowDomainId(targetSw); + public Set<Short> getBroadcastPorts(long targetSw, long src, short srcPort) { + Set<Short> result = new HashSet<Short>(); + long clusterId = getOpenflowDomainId(targetSw); for(NodePortTuple npt: clusterBroadcastNodePorts.get(clusterId)) { - if (npt.getNodeId().equals(targetSw)) { + if (npt.getNodeId() == targetSw) { result.add(npt.getPortId()); } } @@ -835,13 +834,15 @@ public class TopologyInstance { } public NodePortTuple - getAllowedOutgoingBroadcastPort(DatapathId src, OFPort srcPort, DatapathId dst, - OFPort dstPort) { + getAllowedOutgoingBroadcastPort(long src, short srcPort, long dst, + short dstPort) { + // TODO Auto-generated method stub return null; } public NodePortTuple - getAllowedIncomingBroadcastPort(DatapathId src, OFPort srcPort) { + getAllowedIncomingBroadcastPort(long src, short srcPort) { + // TODO Auto-generated method stub return null; } } diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java index dce7d28b92a0928d211a245ad49f678a1af87685..25b91464a1dd7aa8213e4987a0d0bf77abc6d39a 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java @@ -33,15 +33,13 @@ import java.util.concurrent.TimeUnit; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.HAListenerTypeMarker; -import net.floodlightcontroller.core.HARole; import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IFloodlightProviderService.Role; import net.floodlightcontroller.core.IHAListener; import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.LogicalOFMessageCategory; import net.floodlightcontroller.core.annotations.LogMessageCategory; import net.floodlightcontroller.core.annotations.LogMessageDoc; -import net.floodlightcontroller.core.internal.IOFSwitchService; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.FloodlightModuleException; import net.floodlightcontroller.core.module.IFloodlightModule; @@ -52,7 +50,7 @@ import net.floodlightcontroller.debugcounter.IDebugCounter; import net.floodlightcontroller.debugcounter.IDebugCounterService; import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException; import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType; -import net.floodlightcontroller.debugcounter.MockDebugCounterService; +import net.floodlightcontroller.debugcounter.NullDebugCounter; import net.floodlightcontroller.debugevent.IDebugEventService; import net.floodlightcontroller.debugevent.IEventUpdater; import net.floodlightcontroller.debugevent.NullDebugEvent; @@ -72,16 +70,13 @@ import net.floodlightcontroller.routing.Route; import net.floodlightcontroller.threadpool.IThreadPoolService; import net.floodlightcontroller.topology.web.TopologyWebRoutable; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPacketIn; -import org.projectfloodlight.openflow.protocol.OFPacketOut; -import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.protocol.action.OFAction; -import org.projectfloodlight.openflow.protocol.action.OFActionOutput; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.OFBufferId; -import org.projectfloodlight.openflow.types.OFPort; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFPacketOut; +import org.openflow.protocol.OFPort; +import org.openflow.protocol.OFType; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionOutput; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -106,12 +101,12 @@ public class TopologyManager implements /** * Role of the controller. */ - private HARole role; + private Role role; /** * Set of ports for each switch */ - protected Map<DatapathId, Set<OFPort>> switchPorts; + protected Map<Long, Set<Short>> switchPorts; /** * Set of links organized by node port tuple @@ -136,7 +131,6 @@ public class TopologyManager implements protected ILinkDiscoveryService linkDiscovery; protected IThreadPoolService threadPool; protected IFloodlightProviderService floodlightProvider; - protected IOFSwitchService switchService; protected IRestApiService restApi; protected IDebugCounterService debugCounters; @@ -194,11 +188,11 @@ public class TopologyManager implements protected class TopologyEventInfo { private final int numOpenflowClustersWithTunnels; private final int numOpenflowClustersWithoutTunnels; - private final Map<DatapathId, List<NodePortTuple>> externalPortsMap; + private final Map<Long, List<NodePortTuple>> externalPortsMap; private final int numTunnelPorts; public TopologyEventInfo(int numOpenflowClustersWithTunnels, int numOpenflowClustersWithoutTunnels, - Map<DatapathId, List<NodePortTuple>> externalPortsMap, + Map<Long, List<NodePortTuple>> externalPortsMap, int numTunnelPorts) { super(); this.numOpenflowClustersWithTunnels = numOpenflowClustersWithTunnels; @@ -221,7 +215,7 @@ public class TopologyManager implements if (numExternalClusters > 0) { builder.append(" { "); int count = 0; - for (DatapathId extCluster : externalPortsMap.keySet()) { + for (Long extCluster : externalPortsMap.keySet()) { builder.append("#" + extCluster + ":Ext Ports: "); builder.append(externalPortsMap.get(extCluster).size()); if (++count < numExternalClusters) { @@ -293,7 +287,7 @@ public class TopologyManager implements catch (Exception e) { log.error("Error in topology instance task thread", e); } finally { - if (floodlightProvider.getRole() != HARole.STANDBY) + if (floodlightProvider.getRole() != Role.SLAVE) newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, TimeUnit.MILLISECONDS); } @@ -354,12 +348,12 @@ public class TopologyManager implements } @Override - public boolean isAttachmentPointPort(DatapathId switchid, OFPort port) { + public boolean isAttachmentPointPort(long switchid, short port) { return isAttachmentPointPort(switchid, port, true); } @Override - public boolean isAttachmentPointPort(DatapathId switchid, OFPort port, + public boolean isAttachmentPointPort(long switchid, short port, boolean tunnelEnabled) { // If the switch port is 'tun-bsn' port, it is not @@ -377,56 +371,55 @@ public class TopologyManager implements // Check whether the port is a physical port. We should not learn // attachment points on "special" ports. - //TODO @Ryan port numbers should be handled as ints now, not shorts. I suppose anything above 65280 up to 65533 is a "special" non-physical port. - if ((port.getShortPortNumber() & 0xff00) == 0xff00 && port.getShortPortNumber() != (short)0xfffe) return false; + if ((port & 0xff00) == 0xff00 && port != (short)0xfffe) return false; // Make sure that the port is enabled. - IOFSwitch sw = switchService.getActiveSwitch(switchid); + IOFSwitch sw = floodlightProvider.getSwitch(switchid); if (sw == null) return false; return (sw.portEnabled(port)); } @Override - public DatapathId getOpenflowDomainId(DatapathId switchId) { + public long getOpenflowDomainId(long switchId) { return getOpenflowDomainId(switchId, true); } @Override - public DatapathId getOpenflowDomainId(DatapathId switchId, boolean tunnelEnabled) { + public long getOpenflowDomainId(long switchId, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.getOpenflowDomainId(switchId); } @Override - public DatapathId getL2DomainId(DatapathId switchId) { + public long getL2DomainId(long switchId) { return getL2DomainId(switchId, true); } @Override - public DatapathId getL2DomainId(DatapathId switchId, boolean tunnelEnabled) { + public long getL2DomainId(long switchId, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.getL2DomainId(switchId); } @Override - public boolean inSameOpenflowDomain(DatapathId switch1, DatapathId switch2) { + public boolean inSameOpenflowDomain(long switch1, long switch2) { return inSameOpenflowDomain(switch1, switch2, true); } @Override - public boolean inSameOpenflowDomain(DatapathId switch1, DatapathId switch2, + public boolean inSameOpenflowDomain(long switch1, long switch2, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.inSameOpenflowDomain(switch1, switch2); } @Override - public boolean isAllowed(DatapathId sw, OFPort portId) { + public boolean isAllowed(long sw, short portId) { return isAllowed(sw, portId, true); } @Override - public boolean isAllowed(DatapathId sw, OFPort portId, boolean tunnelEnabled) { + public boolean isAllowed(long sw, short portId, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.isAllowed(sw, portId); } @@ -434,12 +427,12 @@ public class TopologyManager implements //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// @Override - public boolean isIncomingBroadcastAllowed(DatapathId sw, OFPort portId) { + public boolean isIncomingBroadcastAllowed(long sw, short portId) { return isIncomingBroadcastAllowed(sw, portId, true); } @Override - public boolean isIncomingBroadcastAllowed(DatapathId sw, OFPort portId, + public boolean isIncomingBroadcastAllowed(long sw, short portId, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.isIncomingBroadcastAllowedOnSwitchPort(sw, portId); @@ -449,13 +442,13 @@ public class TopologyManager implements //////////////////////////////////////////////////////////////////////// /** Get all the ports connected to the switch */ @Override - public Set<OFPort> getPortsWithLinks(DatapathId sw) { + public Set<Short> getPortsWithLinks(long sw) { return getPortsWithLinks(sw, true); } /** Get all the ports connected to the switch */ @Override - public Set<OFPort> getPortsWithLinks(DatapathId sw, boolean tunnelEnabled) { + public Set<Short> getPortsWithLinks(long sw, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.getPortsWithLinks(sw); } @@ -467,8 +460,8 @@ public class TopologyManager implements * is on switch port (src, srcPort). */ @Override - public Set<OFPort> getBroadcastPorts(DatapathId targetSw, - DatapathId src, OFPort srcPort) { + public Set<Short> getBroadcastPorts(long targetSw, + long src, short srcPort) { return getBroadcastPorts(targetSw, src, srcPort, true); } @@ -477,8 +470,8 @@ public class TopologyManager implements * is on switch port (src, srcPort). */ @Override - public Set<OFPort> getBroadcastPorts(DatapathId targetSw, - DatapathId src, OFPort srcPort, + public Set<Short> getBroadcastPorts(long targetSw, + long src, short srcPort, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.getBroadcastPorts(targetSw, src, srcPort); @@ -487,15 +480,15 @@ public class TopologyManager implements //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// @Override - public NodePortTuple getOutgoingSwitchPort(DatapathId src, OFPort srcPort, - DatapathId dst, OFPort dstPort) { + public NodePortTuple getOutgoingSwitchPort(long src, short srcPort, + long dst, short dstPort) { // Use this function to redirect traffic if needed. return getOutgoingSwitchPort(src, srcPort, dst, dstPort, true); } @Override - public NodePortTuple getOutgoingSwitchPort(DatapathId src, OFPort srcPort, - DatapathId dst, OFPort dstPort, + public NodePortTuple getOutgoingSwitchPort(long src, short srcPort, + long dst, short dstPort, boolean tunnelEnabled) { // Use this function to redirect traffic if needed. TopologyInstance ti = getCurrentInstance(tunnelEnabled); @@ -506,14 +499,14 @@ public class TopologyManager implements //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// @Override - public NodePortTuple getIncomingSwitchPort(DatapathId src, OFPort srcPort, - DatapathId dst, OFPort dstPort) { + public NodePortTuple getIncomingSwitchPort(long src, short srcPort, + long dst, short dstPort) { return getIncomingSwitchPort(src, srcPort, dst, dstPort, true); } @Override - public NodePortTuple getIncomingSwitchPort(DatapathId src, OFPort srcPort, - DatapathId dst, OFPort dstPort, + public NodePortTuple getIncomingSwitchPort(long src, short srcPort, + long dst, short dstPort, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.getIncomingSwitchPort(src, srcPort, @@ -526,15 +519,15 @@ public class TopologyManager implements * Checks if the two switchports belong to the same broadcast domain. */ @Override - public boolean isInSameBroadcastDomain(DatapathId s1, OFPort p1, DatapathId s2, - OFPort p2) { + public boolean isInSameBroadcastDomain(long s1, short p1, long s2, + short p2) { return isInSameBroadcastDomain(s1, p1, s2, p2, true); } @Override - public boolean isInSameBroadcastDomain(DatapathId s1, OFPort p1, - DatapathId s2, OFPort p2, + public boolean isInSameBroadcastDomain(long s1, short p1, + long s2, short p2, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.inSameBroadcastDomain(s1, p1, s2, p2); @@ -547,12 +540,12 @@ public class TopologyManager implements * Checks if the switchport is a broadcast domain port or not. */ @Override - public boolean isBroadcastDomainPort(DatapathId sw, OFPort port) { + public boolean isBroadcastDomainPort(long sw, short port) { return isBroadcastDomainPort(sw, port, true); } @Override - public boolean isBroadcastDomainPort(DatapathId sw, OFPort port, + public boolean isBroadcastDomainPort(long sw, short port, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.isBroadcastDomainPort(new NodePortTuple(sw, port)); @@ -565,15 +558,15 @@ public class TopologyManager implements * old attachment point port. */ @Override - public boolean isConsistent(DatapathId oldSw, OFPort oldPort, - DatapathId newSw, OFPort newPort) { + public boolean isConsistent(long oldSw, short oldPort, + long newSw, short newPort) { return isConsistent(oldSw, oldPort, newSw, newPort, true); } @Override - public boolean isConsistent(DatapathId oldSw, OFPort oldPort, - DatapathId newSw, OFPort newPort, + public boolean isConsistent(long oldSw, short oldPort, + long newSw, short newPort, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.isConsistent(oldSw, oldPort, newSw, newPort); @@ -585,12 +578,12 @@ public class TopologyManager implements * Checks if the two switches are in the same Layer 2 domain. */ @Override - public boolean inSameL2Domain(DatapathId switch1, DatapathId switch2) { + public boolean inSameL2Domain(long switch1, long switch2) { return inSameL2Domain(switch1, switch2, true); } @Override - public boolean inSameL2Domain(DatapathId switch1, DatapathId switch2, + public boolean inSameL2Domain(long switch1, long switch2, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.inSameL2Domain(switch1, switch2); @@ -599,19 +592,19 @@ public class TopologyManager implements //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// @Override - public NodePortTuple getAllowedOutgoingBroadcastPort(DatapathId src, - OFPort srcPort, - DatapathId dst, - OFPort dstPort) { + public NodePortTuple getAllowedOutgoingBroadcastPort(long src, + short srcPort, + long dst, + short dstPort) { return getAllowedOutgoingBroadcastPort(src, srcPort, dst, dstPort, true); } @Override - public NodePortTuple getAllowedOutgoingBroadcastPort(DatapathId src, - OFPort srcPort, - DatapathId dst, - OFPort dstPort, + public NodePortTuple getAllowedOutgoingBroadcastPort(long src, + short srcPort, + long dst, + short dstPort, boolean tunnelEnabled){ TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.getAllowedOutgoingBroadcastPort(src, srcPort, @@ -621,13 +614,13 @@ public class TopologyManager implements //////////////////////////////////////////////////////////////////////// @Override public NodePortTuple - getAllowedIncomingBroadcastPort(DatapathId src, OFPort srcPort) { + getAllowedIncomingBroadcastPort(long src, short srcPort) { return getAllowedIncomingBroadcastPort(src,srcPort, true); } @Override public NodePortTuple - getAllowedIncomingBroadcastPort(DatapathId src, OFPort srcPort, + getAllowedIncomingBroadcastPort(long src, short srcPort, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.getAllowedIncomingBroadcastPort(src,srcPort); @@ -636,12 +629,12 @@ public class TopologyManager implements //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// @Override - public Set<DatapathId> getSwitchesInOpenflowDomain(DatapathId switchDPID) { + public Set<Long> getSwitchesInOpenflowDomain(long switchDPID) { return getSwitchesInOpenflowDomain(switchDPID, true); } @Override - public Set<DatapathId> getSwitchesInOpenflowDomain(DatapathId switchDPID, + public Set<Long> getSwitchesInOpenflowDomain(long switchDPID, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.getSwitchesInOpenflowDomain(switchDPID); @@ -685,41 +678,41 @@ public class TopologyManager implements // *************** @Override - public Route getRoute(DatapathId src, DatapathId dst, long cookie) { + public Route getRoute(long src, long dst, long cookie) { return getRoute(src, dst, cookie, true); } @Override - public Route getRoute(DatapathId src, DatapathId dst, long cookie, boolean tunnelEnabled) { + public Route getRoute(long src, long dst, long cookie, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.getRoute(src, dst, cookie); } @Override - public Route getRoute(DatapathId src, OFPort srcPort, DatapathId dst, OFPort dstPort, long cookie) { + public Route getRoute(long src, short srcPort, long dst, short dstPort, long cookie) { return getRoute(src, srcPort, dst, dstPort, cookie, true); } @Override - public Route getRoute(DatapathId src, OFPort srcPort, DatapathId dst, OFPort dstPort, long cookie, + public Route getRoute(long src, short srcPort, long dst, short dstPort, long cookie, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.getRoute(null, src, srcPort, dst, dstPort, cookie); } @Override - public boolean routeExists(DatapathId src, DatapathId dst) { + public boolean routeExists(long src, long dst) { return routeExists(src, dst, true); } @Override - public boolean routeExists(DatapathId src, DatapathId dst, boolean tunnelEnabled) { + public boolean routeExists(long src, long dst, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.routeExists(src, dst); } @Override - public ArrayList<Route> getRoutes(DatapathId srcDpid, DatapathId dstDpid, + public ArrayList<Route> getRoutes(long srcDpid, long dstDpid, boolean tunnelEnabled) { // Floodlight supports single path routing now @@ -753,7 +746,7 @@ public class TopologyManager implements FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: - ctrIncoming.increment(); + ctrIncoming.updateCounterNoFlush(); return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); default: @@ -769,10 +762,10 @@ public class TopologyManager implements private class HAListenerDelegate implements IHAListener { @Override - public void transitionToActive() { - role = HARole.ACTIVE; + public void transitionToMaster() { + role = Role.MASTER; log.debug("Re-computing topology due " + - "to HA change from STANDBY->ACTIVE"); + "to HA change from SLAVE->MASTER"); newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, TimeUnit.MILLISECONDS); } @@ -803,12 +796,6 @@ public class TopologyManager implements // TODO Auto-generated method stub return false; } - - @Override - public void transitionToStandby() { - // TODO Auto-generated method stub - - } } // ***************** @@ -845,7 +832,6 @@ public class TopologyManager implements l.add(ILinkDiscoveryService.class); l.add(IThreadPoolService.class); l.add(IFloodlightProviderService.class); - l.add(IOFSwitchService.class); l.add(ICounterStoreService.class); l.add(IRestApiService.class); return l; @@ -858,12 +844,11 @@ public class TopologyManager implements threadPool = context.getServiceImpl(IThreadPoolService.class); floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class); - switchService = context.getServiceImpl(IOFSwitchService.class); restApi = context.getServiceImpl(IRestApiService.class); debugCounters = context.getServiceImpl(IDebugCounterService.class); debugEvents = context.getServiceImpl(IDebugEventService.class); - switchPorts = new HashMap<DatapathId, Set<OFPort>>(); + switchPorts = new HashMap<Long,Set<Short>>(); switchPortLinks = new HashMap<NodePortTuple, Set<Link>>(); directLinks = new HashMap<NodePortTuple, Set<Link>>(); portBroadcastDomainLinks = new HashMap<NodePortTuple, Set<Link>>(); @@ -899,7 +884,7 @@ public class TopologyManager implements ScheduledExecutorService ses = threadPool.getScheduledExecutor(); newInstanceTask = new SingletonTask(ses, new UpdateTopologyWorker()); - if (role != HARole.STANDBY) + if (role != Role.SLAVE) newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, TimeUnit.MILLISECONDS); @@ -939,17 +924,17 @@ public class TopologyManager implements * @param cntx * @return */ - protected Command dropFilter(DatapathId sw, OFPacketIn pi, + protected Command dropFilter(long sw, OFPacketIn pi, FloodlightContext cntx) { Command result = Command.CONTINUE; - OFPort port = pi.getInPort(); + short port = pi.getInPort(); // If the input port is not allowed for data traffic, drop everything. // BDDP packets will not reach this stage. if (isAllowed(sw, port) == false) { if (log.isTraceEnabled()) { log.trace("Ignoring packet because of topology " + - "restriction on switch={}, port={}", sw.getLong(), port.getPortNumber()); + "restriction on switch={}, port={}", sw, port); result = Command.STOP; } } @@ -970,45 +955,49 @@ public class TopologyManager implements "topology discovery packet", recommendation=LogMessageDoc.CHECK_SWITCH) public void doMultiActionPacketOut(byte[] packetData, IOFSwitch sw, - Set<OFPort> ports, + Set<Short> ports, FloodlightContext cntx) { if (ports == null) return; if (packetData == null || packetData.length <= 0) return; - //OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT); - OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); + OFPacketOut po = + (OFPacketOut) floodlightProvider.getOFMessageFactory(). + getMessage(OFType.PACKET_OUT); + List<OFAction> actions = new ArrayList<OFAction>(); - for(OFPort p: ports) { - //actions.add(new OFActionOutput(p, (short) 0)); - actions.add(sw.getOFFactory().actions().output(p, 0)); + for(short p: ports) { + actions.add(new OFActionOutput(p, (short) 0)); } // set actions - pob.setActions(actions); + po.setActions(actions); // set action length - //po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH * ports.size())); + po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH * + ports.size())); // set buffer-id to BUFFER_ID_NONE - pob.setBufferId(OFBufferId.NO_BUFFER); + po.setBufferId(OFPacketOut.BUFFER_ID_NONE); // set in-port to OFPP_NONE - pob.setInPort(OFPort.ZERO); + po.setInPort(OFPort.OFPP_NONE.getValue()); // set packet data - pob.setData(packetData); + po.setPacketData(packetData); // compute and set packet length. - //short poLength = (short)(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + packetData.length); + short poLength = (short)(OFPacketOut.MINIMUM_LENGTH + + po.getActionsLength() + + packetData.length); - //po.setLength(poLength); + po.setLength(poLength); try { //counterStore.updatePktOutFMCounterStore(sw, po); if (log.isTraceEnabled()) { log.trace("write broadcast packet on switch-id={} " + "interaces={} packet-data={} packet-out={}", - new Object[] {sw.getId(), ports, packetData, pob.build()}); + new Object[] {sw.getId(), ports, packetData, po}); } - sw.write(pob.build(), LogicalOFMessageCategory.MAIN); + sw.write(po, cntx); } catch (IOException e) { log.error("Failure writing packet out", e); @@ -1023,11 +1012,11 @@ public class TopologyManager implements * @param sid * @return */ - protected Set<OFPort> getPortsToEliminateForBDDP(DatapathId sid) { + protected Set<Short> getPortsToEliminateForBDDP(long sid) { Set<NodePortTuple> suppressedNptList = linkDiscovery.getSuppressLLDPsInfo(); if (suppressedNptList == null) return null; - Set<OFPort> resultPorts = new HashSet<OFPort>(); + Set<Short> resultPorts = new HashSet<Short>(); for(NodePortTuple npt: suppressedNptList) { if (npt.getNodeId() == sid) { resultPorts.add(npt.getPortId()); @@ -1046,36 +1035,36 @@ public class TopologyManager implements * @param pi * @param cntx */ - protected void doFloodBDDP(DatapathId pinSwitch, OFPacketIn pi, + protected void doFloodBDDP(long pinSwitch, OFPacketIn pi, FloodlightContext cntx) { TopologyInstance ti = getCurrentInstance(false); - Set<DatapathId> switches = ti.getSwitchesInOpenflowDomain(pinSwitch); + Set<Long> switches = ti.getSwitchesInOpenflowDomain(pinSwitch); if (switches == null) { // indicates no links are connected to the switches - switches = new HashSet<DatapathId>(); + switches = new HashSet<Long>(); switches.add(pinSwitch); } - for(DatapathId sid: switches) { - IOFSwitch sw = switchService.getSwitch(sid); + for(long sid: switches) { + IOFSwitch sw = floodlightProvider.getSwitch(sid); if (sw == null) continue; - Collection<OFPort> enabledPorts = sw.getEnabledPortNumbers(); + Collection<Short> enabledPorts = sw.getEnabledPortNumbers(); if (enabledPorts == null) continue; - Set<OFPort> ports = new HashSet<OFPort>(); + Set<Short> ports = new HashSet<Short>(); ports.addAll(enabledPorts); // all the ports known to topology // without tunnels. // out of these, we need to choose only those that are // broadcast port, otherwise, we should eliminate. - Set<OFPort> portsKnownToTopo = ti.getPortsWithLinks(sid); + Set<Short> portsKnownToTopo = ti.getPortsWithLinks(sid); if (portsKnownToTopo != null) { - for(OFPort p: portsKnownToTopo) { + for(short p: portsKnownToTopo) { NodePortTuple npt = new NodePortTuple(sid, p); if (ti.isBroadcastDomainPort(npt) == false) { @@ -1084,7 +1073,7 @@ public class TopologyManager implements } } - Set<OFPort> portsToEliminate = getPortsToEliminateForBDDP(sid); + Set<Short> portsToEliminate = getPortsToEliminateForBDDP(sid); if (portsToEliminate != null) { ports.removeAll(portsToEliminate); } @@ -1095,7 +1084,7 @@ public class TopologyManager implements } // we have all the switch ports to which we need to broadcast. - doMultiActionPacketOut(pi.getData(), sw, ports, cntx); + doMultiActionPacketOut(pi.getPacketData(), sw, ports, cntx); } } @@ -1179,18 +1168,18 @@ public class TopologyManager implements return (Collections.unmodifiableList(appliedUpdates)); } - protected void addOrUpdateSwitch(DatapathId sw) { + protected void addOrUpdateSwitch(long sw) { // nothing to do here for the time being. return; } - public void addTunnelPort(DatapathId sw, OFPort port) { + public void addTunnelPort(long sw, short port) { NodePortTuple npt = new NodePortTuple(sw, port); tunnelPorts.add(npt); tunnelPortsUpdated = true; } - public void removeTunnelPort(DatapathId sw, OFPort port) { + public void removeTunnelPort(long sw, short port) { NodePortTuple npt = new NodePortTuple(sw, port); tunnelPorts.remove(npt); tunnelPortsUpdated = true; @@ -1259,7 +1248,7 @@ public class TopologyManager implements TopologyEventInfo topologyInfo = new TopologyEventInfo(0, nt.getClusters().size(), - new HashMap<DatapathId, List<NodePortTuple>>(), + new HashMap<Long, List<NodePortTuple>>(), 0); evTopology.updateEventWithFlush(new TopologyEvent(reason, topologyInfo)); @@ -1329,27 +1318,27 @@ public class TopologyManager implements public void informListeners(List<LDUpdate> linkUpdates) { - if (role != null && role != HARole.ACTIVE) + if (role != null && role != Role.MASTER) return; - for(int i=0; i < topologyAware.size(); ++i) { + for(int i=0; i<topologyAware.size(); ++i) { ITopologyListener listener = topologyAware.get(i); listener.topologyChanged(linkUpdates); } } - public void addSwitch(DatapathId sid) { + public void addSwitch(long sid) { if (switchPorts.containsKey(sid) == false) { - switchPorts.put(sid, new HashSet<OFPort>()); + switchPorts.put(sid, new HashSet<Short>()); } } - private void addPortToSwitch(DatapathId s, OFPort p) { + private void addPortToSwitch(long s, short p) { addSwitch(s); switchPorts.get(s).add(p); } - public void removeSwitch(DatapathId sid) { + public void removeSwitch(long sid) { // Delete all the links in the switch, switch and all // associated data should be deleted. if (switchPorts.containsKey(sid) == false) return; @@ -1362,7 +1351,7 @@ public class TopologyManager implements } Set<Link> linksToRemove = new HashSet<Link>(); - for(OFPort p: switchPorts.get(sid)) { + for(Short p: switchPorts.get(sid)) { NodePortTuple n1 = new NodePortTuple(sid, p); linksToRemove.addAll(switchPortLinks.get(n1)); } @@ -1425,13 +1414,13 @@ public class TopologyManager implements return result1 || result2; } - protected void addOrUpdateTunnelLink(DatapathId srcId, OFPort srcPort, DatapathId dstId, - OFPort dstPort) { + protected void addOrUpdateTunnelLink(long srcId, short srcPort, long dstId, + short dstPort) { // If you need to handle tunnel links, this is a placeholder. } - public void addOrUpdateLink(DatapathId srcId, OFPort srcPort, DatapathId dstId, - OFPort dstPort, LinkType type) { + public void addOrUpdateLink(long srcId, short srcPort, long dstId, + short dstPort, LinkType type) { Link link = new Link(srcId, srcPort, dstId, dstPort); if (type.equals(LinkType.MULTIHOP_LINK)) { @@ -1488,8 +1477,8 @@ public class TopologyManager implements } } - public void removeLink(DatapathId srcId, OFPort srcPort, - DatapathId dstId, OFPort dstPort) { + public void removeLink(long srcId, short srcPort, + long dstId, short dstPort) { Link link = new Link(srcId, srcPort, dstId, dstPort); removeLink(link); } @@ -1518,7 +1507,7 @@ public class TopologyManager implements /** * Getters. No Setters. */ - public Map<DatapathId, Set<OFPort>> getSwitchPorts() { + public Map<Long, Set<Short>> getSwitchPorts() { return switchPorts; } @@ -1544,15 +1533,15 @@ public class TopologyManager implements * Switch methods */ @Override - public Set<OFPort> getPorts(DatapathId sw) { - IOFSwitch iofSwitch = switchService.getSwitch(sw); + public Set<Short> getPorts(long sw) { + IOFSwitch iofSwitch = floodlightProvider.getSwitch(sw); if (iofSwitch == null) return Collections.emptySet(); - Collection<OFPort> ofpList = iofSwitch.getEnabledPortNumbers(); + Collection<Short> ofpList = iofSwitch.getEnabledPortNumbers(); if (ofpList == null) return Collections.emptySet(); - Set<OFPort> ports = new HashSet<OFPort>(ofpList); - Set<OFPort> qPorts = linkDiscovery.getQuarantinedPorts(sw); + Set<Short> ports = new HashSet<Short>(ofpList); + Set<Short> qPorts = linkDiscovery.getQuarantinedPorts(sw); if (qPorts != null) ports.removeAll(qPorts); diff --git a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java index eb4c368e80b4131d8eff36879cbb5b86564f2ae9..75b0b00d3a9fb082e14a18e3da34712fcdee4e36 100644 --- a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java +++ b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java @@ -22,10 +22,9 @@ import java.util.Set; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.LogicalOFMessageCategory; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFType; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFType; /** * Dampens OFMessages sent to an OF switch. A message is only written to @@ -135,7 +134,7 @@ public class OFMessageDamper { FloodlightContext cntx, boolean flush) throws IOException { if (! msgTypesToCache.contains(msg.getType())) { - sw.write(msg, LogicalOFMessageCategory.MAIN); + sw.writeThrottled(msg, cntx); if (flush) { sw.flush(); } @@ -147,7 +146,7 @@ public class OFMessageDamper { // entry exists in cache. Dampening. return false; } else { - sw.write(msg, LogicalOFMessageCategory.MAIN); + sw.writeThrottled(msg, cntx); if (flush) { sw.flush(); } diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java index 258368329d1a5c71023c73c2037e7d59c23c31ab..1d64144dcf19a3d9a73fce36b3976f8b7d4cbaec 100644 --- a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java +++ b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java @@ -28,14 +28,14 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.projectfloodlight.openflow.protocol.OFFlowMod; -import org.projectfloodlight.openflow.protocol.OFMatch; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPacketIn; -import org.projectfloodlight.openflow.protocol.OFPacketOut; -import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.protocol.action.OFAction; -import org.projectfloodlight.util.HexString; +import org.openflow.protocol.OFFlowMod; +import org.openflow.protocol.OFMatch; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFPacketOut; +import org.openflow.protocol.OFType; +import org.openflow.protocol.action.OFAction; +import org.openflow.util.HexString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/openflow/protocol/Instantiable.java b/src/main/java/org/openflow/protocol/Instantiable.java new file mode 100644 index 0000000000000000000000000000000000000000..1358ba7526f2b59d7f37ea23e94a723c8e2d2e50 --- /dev/null +++ b/src/main/java/org/openflow/protocol/Instantiable.java @@ -0,0 +1,31 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public interface Instantiable<E> { + + /** + * Create a new instance of a given subclass. + * @return the new instance. + */ + public E instantiate(); +} diff --git a/src/main/java/org/openflow/protocol/OFBarrierReply.java b/src/main/java/org/openflow/protocol/OFBarrierReply.java new file mode 100644 index 0000000000000000000000000000000000000000..a79a15f07918f4db09db438f68f03b048cfc4bf1 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFBarrierReply.java @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import org.openflow.util.U16; + +/** + * Represents an OFPT_BARRIER_REPLY message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFBarrierReply extends OFMessage { + public OFBarrierReply() { + super(); + this.type = OFType.BARRIER_REPLY; + this.length = U16.t(OFMessage.MINIMUM_LENGTH); + } +} diff --git a/src/main/java/org/openflow/protocol/OFBarrierRequest.java b/src/main/java/org/openflow/protocol/OFBarrierRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..999218636550dac8af837f95a69480a3b596aa02 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFBarrierRequest.java @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import org.openflow.util.U16; + +/** + * Represents an OFPT_BARRIER_REQUEST message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFBarrierRequest extends OFMessage { + public OFBarrierRequest() { + super(); + this.type = OFType.BARRIER_REQUEST; + this.length = U16.t(OFMessage.MINIMUM_LENGTH); + } +} diff --git a/src/main/java/org/openflow/protocol/OFEchoReply.java b/src/main/java/org/openflow/protocol/OFEchoReply.java new file mode 100644 index 0000000000000000000000000000000000000000..3e282a74a052e5dcb1792e40d2402e1352555244 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFEchoReply.java @@ -0,0 +1,36 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import org.openflow.util.U16; + +/** + * Represents an ofp_echo_reply message + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + */ + +public class OFEchoReply extends OFEchoRequest { + public static int MINIMUM_LENGTH = 8; + + public OFEchoReply() { + super(); + this.type = OFType.ECHO_REPLY; + this.length = U16.t(MINIMUM_LENGTH); + } +} diff --git a/src/main/java/org/openflow/protocol/OFEchoRequest.java b/src/main/java/org/openflow/protocol/OFEchoRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..295a3972e8a29d5ed928150d2076ac6d6e16fd52 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFEchoRequest.java @@ -0,0 +1,101 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + + +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents an ofp_echo_request message + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + */ + +public class OFEchoRequest extends OFMessage { + public static int MINIMUM_LENGTH = 8; + byte[] payload; + + public OFEchoRequest() { + super(); + this.type = OFType.ECHO_REQUEST; + this.length = U16.t(MINIMUM_LENGTH); + } + + @Override + public void readFrom(ChannelBuffer bb) { + super.readFrom(bb); + int datalen = this.getLengthU() - MINIMUM_LENGTH; + if (datalen > 0) { + this.payload = new byte[datalen]; + bb.readBytes(payload); + } + } + + /** + * @return the payload + */ + public byte[] getPayload() { + return payload; + } + + /** + * @param payload + * the payload to set + */ + public void setPayload(byte[] payload) { + this.payload = payload; + } + + @Override + public void writeTo(ChannelBuffer bb) { + super.writeTo(bb); + if (payload != null) + bb.writeBytes(payload); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Arrays.hashCode(payload); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + OFEchoRequest other = (OFEchoRequest) obj; + if (!Arrays.equals(payload, other.payload)) + return false; + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/OFError.java b/src/main/java/org/openflow/protocol/OFError.java new file mode 100644 index 0000000000000000000000000000000000000000..df7b236024c9370d43c5f28392548319cd83f7ce --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFError.java @@ -0,0 +1,325 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.util.Arrays; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.factory.MessageParseException; +import org.openflow.protocol.factory.OFMessageFactory; +import org.openflow.protocol.factory.OFMessageFactoryAware; +import org.openflow.util.U16; + +/** + * Represents an ofp_error_msg + * + * @author David Erickson (daviderickson@cs.stanford.edu) + * @author Rob Sherwood (rob.sherwood@stanford.edu) + */ +public class OFError extends OFMessage implements OFMessageFactoryAware { + public static int MINIMUM_LENGTH = 12; + + public enum OFErrorType { + // OFPET_VENDOR_ERROR is an extension that was added in Open vSwitch and isn't + // in the OF 1.0 spec, but it was easier to add it here instead of adding + // generic support for extensible vendor-defined error messages. + // It uses the random value 0xb0c2 to avoid conflicts with other possible new + // error types. Support for vendor-defined extended errors has been standardized + // in the OF 1.2 spec, so this workaround is only needed for 1.0. + OFPET_HELLO_FAILED, OFPET_BAD_REQUEST, OFPET_BAD_ACTION, OFPET_FLOW_MOD_FAILED, OFPET_PORT_MOD_FAILED, OFPET_QUEUE_OP_FAILED, OFPET_VENDOR_ERROR((short)0xb0c2); + + protected short value; + + private OFErrorType() { + this.value = (short) this.ordinal(); + } + + private OFErrorType(short value) { + this.value = value; + } + + public short getValue() { + return value; + } + } + + public enum OFHelloFailedCode { + OFPHFC_INCOMPATIBLE, OFPHFC_EPERM + } + + public enum OFBadRequestCode { + OFPBRC_BAD_VERSION, OFPBRC_BAD_TYPE, OFPBRC_BAD_STAT, OFPBRC_BAD_VENDOR, OFPBRC_BAD_SUBTYPE, OFPBRC_EPERM, OFPBRC_BAD_LEN, OFPBRC_BUFFER_EMPTY, OFPBRC_BUFFER_UNKNOWN + } + + public enum OFBadActionCode { + OFPBAC_BAD_TYPE, OFPBAC_BAD_LEN, OFPBAC_BAD_VENDOR, OFPBAC_BAD_VENDOR_TYPE, OFPBAC_BAD_OUT_PORT, OFPBAC_BAD_ARGUMENT, OFPBAC_EPERM, OFPBAC_TOO_MANY, OFPBAC_BAD_QUEUE + } + + public enum OFFlowModFailedCode { + OFPFMFC_ALL_TABLES_FULL, OFPFMFC_OVERLAP, OFPFMFC_EPERM, OFPFMFC_BAD_EMERG_TIMEOUT, OFPFMFC_BAD_COMMAND, OFPFMFC_UNSUPPORTED + } + + public enum OFPortModFailedCode { + OFPPMFC_BAD_PORT, OFPPMFC_BAD_HW_ADDR + } + + public enum OFQueueOpFailedCode { + OFPQOFC_BAD_PORT, OFPQOFC_BAD_QUEUE, OFPQOFC_EPERM + } + + protected short errorType; + protected short errorCode; + protected int vendor; + protected int vendorErrorType; + protected short vendorErrorCode; + protected OFMessageFactory factory; + protected byte[] error; + protected boolean errorIsAscii; + + public OFError() { + super(); + this.type = OFType.ERROR; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** convenience constructor */ + public OFError(OFErrorType errorType) { + this(); + setErrorType(errorType); + } + + /** + * @return the errorType + */ + public short getErrorType() { + return errorType; + } + + /** + * @param errorType + * the errorType to set + */ + public void setErrorType(short errorType) { + this.errorType = errorType; + } + + public void setErrorType(OFErrorType type) { + this.errorType = type.getValue(); + } + + /** + * @return true if the error is an extended vendor error + */ + public boolean isVendorError() { + return errorType == OFErrorType.OFPET_VENDOR_ERROR.getValue(); + } + + /** + * @return the errorCode + */ + public short getErrorCode() { + return errorCode; + } + + /** + * @param errorCode + * the errorCode to set + */ + public void setErrorCode(OFHelloFailedCode code) { + this.errorCode = (short) code.ordinal(); + } + + public void setErrorCode(short errorCode) { + this.errorCode = errorCode; + } + + public void setErrorCode(OFBadRequestCode code) { + this.errorCode = (short) code.ordinal(); + } + + public void setErrorCode(OFBadActionCode code) { + this.errorCode = (short) code.ordinal(); + } + + public void setErrorCode(OFFlowModFailedCode code) { + this.errorCode = (short) code.ordinal(); + } + + public void setErrorCode(OFPortModFailedCode code) { + this.errorCode = (short) code.ordinal(); + } + + public void setErrorCode(OFQueueOpFailedCode code) { + this.errorCode = (short) code.ordinal(); + } + + public int getVendorErrorType() { + return vendorErrorType; + } + + public void setVendorErrorType(int vendorErrorType) { + this.vendorErrorType = vendorErrorType; + } + + public short getVendorErrorCode() { + return vendorErrorCode; + } + + public void setVendorErrorCode(short vendorErrorCode) { + this.vendorErrorCode = vendorErrorCode; + } + + public OFMessage getOffendingMsg() throws MessageParseException { + // should only have one message embedded; if more than one, just + // grab first + if (this.error == null) + return null; + ChannelBuffer errorMsg = ChannelBuffers.wrappedBuffer(this.error); + if (factory == null) + throw new RuntimeException("MessageFactory not set"); + + List<OFMessage> msglist = this.factory.parseMessage(errorMsg); + if (msglist == null) + return null; + return msglist.get(0); + } + + /** + * Write this offending message into the payload of the Error message + * + * @param offendingMsg + */ + + public void setOffendingMsg(OFMessage offendingMsg) { + if (offendingMsg == null) { + super.setLengthU(MINIMUM_LENGTH); + } else { + this.error = new byte[offendingMsg.getLengthU()]; + ChannelBuffer data = ChannelBuffers.wrappedBuffer(this.error); + data.writerIndex(0); + offendingMsg.writeTo(data); + super.setLengthU(MINIMUM_LENGTH + offendingMsg.getLengthU()); + } + } + + public OFMessageFactory getFactory() { + return factory; + } + + @Override + public void setMessageFactory(OFMessageFactory factory) { + this.factory = factory; + } + + /** + * @return the error + */ + public byte[] getError() { + return error; + } + + /** + * @param error + * the error to set + */ + public void setError(byte[] error) { + this.error = error; + } + + /** + * @return the errorIsAscii + */ + public boolean isErrorIsAscii() { + return errorIsAscii; + } + + /** + * @param errorIsAscii + * the errorIsAscii to set + */ + public void setErrorIsAscii(boolean errorIsAscii) { + this.errorIsAscii = errorIsAscii; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.errorType = data.readShort(); + this.errorCode = data.readShort(); + int dataLength = this.getLengthU() - MINIMUM_LENGTH; + if (dataLength > 0) { + this.error = new byte[dataLength]; + data.readBytes(this.error); + if (this.errorType == OFErrorType.OFPET_HELLO_FAILED.getValue()) + this.errorIsAscii = true; + } + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(errorType); + data.writeShort(errorCode); + if (error != null) + data.writeBytes(error); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Arrays.hashCode(error); + result = prime * result + errorCode; + result = prime * result + (errorIsAscii ? 1231 : 1237); + result = prime * result + errorType; + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + OFError other = (OFError) obj; + if (!Arrays.equals(error, other.error)) + return false; + if (errorCode != other.errorCode) + return false; + if (errorIsAscii != other.errorIsAscii) + return false; + if (errorType != other.errorType) + return false; + return true; + } + +} diff --git a/src/main/java/org/openflow/protocol/OFFeaturesReply.java b/src/main/java/org/openflow/protocol/OFFeaturesReply.java new file mode 100644 index 0000000000000000000000000000000000000000..7c2cc51cf21dca6c83978dc1d995deb5ba0d4421 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFFeaturesReply.java @@ -0,0 +1,261 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.util.ArrayList; +import java.util.List; + + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.serializers.OFFeaturesReplyJSONSerializer; +import org.openflow.protocol.serializers.StringDpidToLongJSONDeserializer; +import org.openflow.util.U16; + + +/** + * Represents a features reply message + * @author David Erickson (daviderickson@cs.stanford.edu) + * + */ +@JsonSerialize(using=OFFeaturesReplyJSONSerializer.class) +public class OFFeaturesReply extends OFMessage { + public static int MINIMUM_LENGTH = 32; + + /** + * Corresponds to bits on the capabilities field + */ + public enum OFCapabilities { + OFPC_FLOW_STATS (1 << 0), + OFPC_TABLE_STATS (1 << 1), + OFPC_PORT_STATS (1 << 2), + OFPC_STP (1 << 3), + OFPC_RESERVED (1 << 4), + OFPC_IP_REASM (1 << 5), + OFPC_QUEUE_STATS (1 << 6), + OFPC_ARP_MATCH_IP (1 << 7); + + protected int value; + + private OFCapabilities(int value) { + this.value = value; + } + + /** + * @return the value + */ + public int getValue() { + return value; + } + } + + protected long datapathId; + protected int buffers; + protected byte tables; + protected int capabilities; + protected int actions; + protected List<OFPhysicalPort> ports; + + public OFFeaturesReply() { + super(); + this.type = OFType.FEATURES_REPLY; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * @return the datapathId + */ + public long getDatapathId() { + return datapathId; + } + + /** + * @param datapathId the datapathId to set + */ + @JsonDeserialize(using=StringDpidToLongJSONDeserializer.class) + public void setDatapathId(long datapathId) { + this.datapathId = datapathId; + } + + /** + * @return the buffers + */ + public int getBuffers() { + return buffers; + } + + /** + * @param buffers the buffers to set + */ + public void setBuffers(int buffers) { + this.buffers = buffers; + } + + /** + * @return the tables + */ + public byte getTables() { + return tables; + } + + /** + * @param tables the tables to set + */ + public void setTables(byte tables) { + this.tables = tables; + } + + /** + * @return the capabilities + */ + public int getCapabilities() { + return capabilities; + } + + /** + * @param capabilities the capabilities to set + */ + public void setCapabilities(int capabilities) { + this.capabilities = capabilities; + } + + /** + * @return the actions + */ + public int getActions() { + return actions; + } + + /** + * @param actions the actions to set + */ + public void setActions(int actions) { + this.actions = actions; + } + + /** + * @return the ports + */ + public List<OFPhysicalPort> getPorts() { + return ports; + } + + /** + * @param ports the ports to set + */ + public void setPorts(List<OFPhysicalPort> ports) { + this.ports = ports; + if (ports == null) { + this.setLengthU(MINIMUM_LENGTH); + } else { + this.setLengthU(MINIMUM_LENGTH + ports.size() + * OFPhysicalPort.MINIMUM_LENGTH); + } + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.datapathId = data.readLong(); + this.buffers = data.readInt(); + this.tables = data.readByte(); + data.readerIndex(data.readerIndex() + 3); // pad + this.capabilities = data.readInt(); + this.actions = data.readInt(); + if (this.ports == null) { + this.ports = new ArrayList<OFPhysicalPort>(); + } else { + this.ports.clear(); + } + int portCount = (super.getLengthU() - 32) + / OFPhysicalPort.MINIMUM_LENGTH; + OFPhysicalPort port; + for (int i = 0; i < portCount; ++i) { + port = new OFPhysicalPort(); + port.readFrom(data); + this.ports.add(port); + } + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeLong(this.datapathId); + data.writeInt(this.buffers); + data.writeByte(this.tables); + data.writeShort((short) 0); // pad + data.writeByte((byte) 0); // pad + data.writeInt(this.capabilities); + data.writeInt(this.actions); + if (this.ports != null) + for (OFPhysicalPort port : this.ports) { + port.writeTo(data); + } + } + + @Override + public int hashCode() { + final int prime = 139; + int result = super.hashCode(); + result = prime * result + actions; + result = prime * result + buffers; + result = prime * result + capabilities; + result = prime * result + (int) (datapathId ^ (datapathId >>> 32)); + result = prime * result + ((ports == null) ? 0 : ports.hashCode()); + result = prime * result + tables; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFFeaturesReply)) { + return false; + } + OFFeaturesReply other = (OFFeaturesReply) obj; + if (actions != other.actions) { + return false; + } + if (buffers != other.buffers) { + return false; + } + if (capabilities != other.capabilities) { + return false; + } + if (datapathId != other.datapathId) { + return false; + } + if (ports == null) { + if (other.ports != null) { + return false; + } + } else if (!ports.equals(other.ports)) { + return false; + } + if (tables != other.tables) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/OFFeaturesRequest.java b/src/main/java/org/openflow/protocol/OFFeaturesRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..0a89e4f57f1559afcbc751cb80d9f9fb10700a93 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFFeaturesRequest.java @@ -0,0 +1,36 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import org.openflow.util.U16; + + +/** + * Represents a features request message + * @author David Erickson (daviderickson@cs.stanford.edu) + * + */ +public class OFFeaturesRequest extends OFMessage { + public static int MINIMUM_LENGTH = 8; + + public OFFeaturesRequest() { + super(); + this.type = OFType.FEATURES_REQUEST; + this.length = U16.t(MINIMUM_LENGTH); + } +} diff --git a/src/main/java/org/openflow/protocol/OFFlowMod.java b/src/main/java/org/openflow/protocol/OFFlowMod.java new file mode 100644 index 0000000000000000000000000000000000000000..0d2aad2157d4bfcbf19572bb943d5fb026c6cbb7 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFFlowMod.java @@ -0,0 +1,389 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.util.LinkedList; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.factory.OFActionFactory; +import org.openflow.protocol.factory.OFActionFactoryAware; +import org.openflow.util.U16; + +/** + * Represents an ofp_flow_mod message + * @author David Erickson (daviderickson@cs.stanford.edu) + * + */ +public class OFFlowMod extends OFMessage implements OFActionFactoryAware, Cloneable { + public static int MINIMUM_LENGTH = 72; + + public static final short OFPFC_ADD = 0; /* New flow. */ + public static final short OFPFC_MODIFY = 1; /* Modify all matching flows. */ + public static final short OFPFC_MODIFY_STRICT = 2; /* Modify entry strictly matching wildcards */ + public static final short OFPFC_DELETE=3; /* Delete all matching flows. */ + public static final short OFPFC_DELETE_STRICT =4; /* Strictly match wildcards and priority. */ + + // Open Flow Flow Mod Flags. Use "or" operation to set multiple flags + public static final short OFPFF_SEND_FLOW_REM = 0x1; // 1 << 0 + public static final short OFPFF_CHECK_OVERLAP = 0x2; // 1 << 1 + public static final short OFPFF_EMERG = 0x4; // 1 << 2 + + protected OFActionFactory actionFactory; + protected OFMatch match; + protected long cookie; + protected short command; + protected short idleTimeout; + protected short hardTimeout; + protected short priority; + protected int bufferId; + protected short outPort; + protected short flags; + protected List<OFAction> actions; + + public OFFlowMod() { + super(); + this.outPort = OFPort.OFPP_NONE.getValue(); + this.type = OFType.FLOW_MOD; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * Get buffer_id + * @return + */ + public int getBufferId() { + return this.bufferId; + } + + /** + * Set buffer_id + * @param bufferId + */ + public OFFlowMod setBufferId(int bufferId) { + this.bufferId = bufferId; + return this; + } + + /** + * Get cookie + * @return + */ + public long getCookie() { + return this.cookie; + } + + /** + * Set cookie + * @param cookie + */ + public OFFlowMod setCookie(long cookie) { + this.cookie = cookie; + return this; + } + + /** + * Get command + * @return + */ + public short getCommand() { + return this.command; + } + + /** + * Set command + * @param command + */ + public OFFlowMod setCommand(short command) { + this.command = command; + return this; + } + + /** + * Get flags + * @return + */ + public short getFlags() { + return this.flags; + } + + /** + * Set flags + * @param flags + */ + public OFFlowMod setFlags(short flags) { + this.flags = flags; + return this; + } + + /** + * Get hard_timeout + * @return + */ + public short getHardTimeout() { + return this.hardTimeout; + } + + /** + * Set hard_timeout + * @param hardTimeout + */ + public OFFlowMod setHardTimeout(short hardTimeout) { + this.hardTimeout = hardTimeout; + return this; + } + + /** + * Get idle_timeout + * @return + */ + public short getIdleTimeout() { + return this.idleTimeout; + } + + /** + * Set idle_timeout + * @param idleTimeout + */ + public OFFlowMod setIdleTimeout(short idleTimeout) { + this.idleTimeout = idleTimeout; + return this; + } + + /** + * Gets a copy of the OFMatch object for this FlowMod, changes to this + * object do not modify the FlowMod + * @return + */ + public OFMatch getMatch() { + return this.match; + } + + /** + * Set match + * @param match + */ + public OFFlowMod setMatch(OFMatch match) { + this.match = match; + return this; + } + + /** + * Get out_port + * @return + */ + public short getOutPort() { + return this.outPort; + } + + /** + * Set out_port + * @param outPort + */ + public OFFlowMod setOutPort(short outPort) { + this.outPort = outPort; + return this; + } + + /** + * Set out_port + * @param port + */ + public OFFlowMod setOutPort(OFPort port) { + this.outPort = port.getValue(); + return this; + } + + /** + * Get priority + * @return + */ + public short getPriority() { + return this.priority; + } + + /** + * Set priority + * @param priority + */ + public OFFlowMod setPriority(short priority) { + this.priority = priority; + return this; + } + + /** + * Returns read-only copies of the actions contained in this Flow Mod + * @return a list of ordered OFAction objects + */ + public List<OFAction> getActions() { + return this.actions; + } + + /** + * Sets the list of actions this Flow Mod contains + * @param actions a list of ordered OFAction objects + */ + public OFFlowMod setActions(List<OFAction> actions) { + this.actions = actions; + return this; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + if (this.match == null) + this.match = new OFMatch(); + this.match.readFrom(data); + this.cookie = data.readLong(); + this.command = data.readShort(); + this.idleTimeout = data.readShort(); + this.hardTimeout = data.readShort(); + this.priority = data.readShort(); + this.bufferId = data.readInt(); + this.outPort = data.readShort(); + this.flags = data.readShort(); + if (this.actionFactory == null) + throw new RuntimeException("OFActionFactory not set"); + this.actions = this.actionFactory.parseActions(data, getLengthU() - + MINIMUM_LENGTH); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + this.match.writeTo(data); + data.writeLong(cookie); + data.writeShort(command); + data.writeShort(idleTimeout); + data.writeShort(hardTimeout); + data.writeShort(priority); + data.writeInt(bufferId); + data.writeShort(outPort); + data.writeShort(flags); + if (actions != null) { + for (OFAction action : actions) { + action.writeTo(data); + } + } + } + + @Override + public void setActionFactory(OFActionFactory actionFactory) { + this.actionFactory = actionFactory; + } + + @Override + public int hashCode() { + final int prime = 227; + int result = super.hashCode(); + result = prime * result + ((actions == null) ? 0 : actions.hashCode()); + result = prime * result + bufferId; + result = prime * result + command; + result = prime * result + (int) (cookie ^ (cookie >>> 32)); + result = prime * result + flags; + result = prime * result + hardTimeout; + result = prime * result + idleTimeout; + result = prime * result + ((match == null) ? 0 : match.hashCode()); + result = prime * result + outPort; + result = prime * result + priority; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFFlowMod)) { + return false; + } + OFFlowMod other = (OFFlowMod) obj; + if (actions == null) { + if (other.actions != null) { + return false; + } + } else if (!actions.equals(other.actions)) { + return false; + } + if (bufferId != other.bufferId) { + return false; + } + if (command != other.command) { + return false; + } + if (cookie != other.cookie) { + return false; + } + if (flags != other.flags) { + return false; + } + if (hardTimeout != other.hardTimeout) { + return false; + } + if (idleTimeout != other.idleTimeout) { + return false; + } + if (match == null) { + if (other.match != null) { + return false; + } + } else if (!match.equals(other.match)) { + return false; + } + if (outPort != other.outPort) { + return false; + } + if (priority != other.priority) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#clone() + */ + @Override + public OFFlowMod clone() throws CloneNotSupportedException { + OFMatch neoMatch = match.clone(); + OFFlowMod flowMod= (OFFlowMod) super.clone(); + flowMod.setMatch(neoMatch); + List<OFAction> neoActions = new LinkedList<OFAction>(); + for(OFAction action: this.actions) + neoActions.add(action.clone()); + flowMod.setActions(neoActions); + return flowMod; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "OFFlowMod [actionFactory=" + actionFactory + ", actions=" + + actions + ", bufferId=" + bufferId + ", command=" + command + + ", cookie=" + Long.toHexString(cookie) + ", flags=" + flags + ", hardTimeout=" + + hardTimeout + ", idleTimeout=" + idleTimeout + ", match=" + + match + ", outPort=" + outPort + ", priority=" + priority + + ", length=" + length + ", type=" + type + ", version=" + + version + ", xid=" + xid + "]"; + } +} diff --git a/src/main/java/org/openflow/protocol/OFFlowRemoved.java b/src/main/java/org/openflow/protocol/OFFlowRemoved.java new file mode 100644 index 0000000000000000000000000000000000000000..cfe2e14ce34691a6b6b096c181cfd67fa5c214aa --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFFlowRemoved.java @@ -0,0 +1,294 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents an ofp_flow_removed message + * @author David Erickson (daviderickson@cs.stanford.edu) + * + */ +public class OFFlowRemoved extends OFMessage { + public static int MINIMUM_LENGTH = 88; + + public enum OFFlowRemovedReason { + OFPRR_IDLE_TIMEOUT, + OFPRR_HARD_TIMEOUT, + OFPRR_DELETE + } + + protected OFMatch match; + protected long cookie; + protected short priority; + protected OFFlowRemovedReason reason; + protected int durationSeconds; + protected int durationNanoseconds; + protected short idleTimeout; + protected long packetCount; + protected long byteCount; + + public OFFlowRemoved() { + super(); + this.type = OFType.FLOW_REMOVED; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * Get cookie + * @return + */ + public long getCookie() { + return this.cookie; + } + + /** + * Set cookie + * @param cookie + */ + public void setCookie(long cookie) { + this.cookie = cookie; + } + + /** + * Get idle_timeout + * @return + */ + public short getIdleTimeout() { + return this.idleTimeout; + } + + /** + * Set idle_timeout + * @param idleTimeout + */ + public void setIdleTimeout(short idleTimeout) { + this.idleTimeout = idleTimeout; + } + + /** + * Gets a copy of the OFMatch object for this FlowMod, changes to this + * object do not modify the FlowMod + * @return + */ + public OFMatch getMatch() { + return this.match; + } + + /** + * Set match + * @param match + */ + public void setMatch(OFMatch match) { + this.match = match; + } + + /** + * Get priority + * @return + */ + public short getPriority() { + return this.priority; + } + + /** + * Set priority + * @param priority + */ + public void setPriority(short priority) { + this.priority = priority; + } + + /** + * @return the reason + */ + public OFFlowRemovedReason getReason() { + return reason; + } + + /** + * @param reason the reason to set + */ + public void setReason(OFFlowRemovedReason reason) { + this.reason = reason; + } + + /** + * @return the durationSeconds + */ + public int getDurationSeconds() { + return durationSeconds; + } + + /** + * @param durationSeconds the durationSeconds to set + */ + public void setDurationSeconds(int durationSeconds) { + this.durationSeconds = durationSeconds; + } + + /** + * @return the durationNanoseconds + */ + public int getDurationNanoseconds() { + return durationNanoseconds; + } + + /** + * @param durationNanoseconds the durationNanoseconds to set + */ + public void setDurationNanoseconds(int durationNanoseconds) { + this.durationNanoseconds = durationNanoseconds; + } + + /** + * @return the packetCount + */ + public long getPacketCount() { + return packetCount; + } + + /** + * @param packetCount the packetCount to set + */ + public void setPacketCount(long packetCount) { + this.packetCount = packetCount; + } + + /** + * @return the byteCount + */ + public long getByteCount() { + return byteCount; + } + + /** + * @param byteCount the byteCount to set + */ + public void setByteCount(long byteCount) { + this.byteCount = byteCount; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + if (this.match == null) + this.match = new OFMatch(); + this.match.readFrom(data); + this.cookie = data.readLong(); + this.priority = data.readShort(); + int reasonIndex = 0xff & data.readByte(); + if (reasonIndex >= OFFlowRemovedReason.values().length) { + reasonIndex = OFFlowRemovedReason.values().length - 1; + } + this.reason = OFFlowRemovedReason.values()[reasonIndex]; + data.readByte(); // pad + this.durationSeconds = data.readInt(); + this.durationNanoseconds = data.readInt(); + this.idleTimeout = data.readShort(); + data.readByte(); // pad + data.readByte(); // pad + this.packetCount = data.readLong(); + this.byteCount = data.readLong(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + this.match.writeTo(data); + data.writeLong(cookie); + data.writeShort(priority); + data.writeByte((byte) this.reason.ordinal()); + data.writeByte((byte) 0); + data.writeInt(this.durationSeconds); + data.writeInt(this.durationNanoseconds); + data.writeShort(idleTimeout); + data.writeByte((byte) 0); // pad + data.writeByte((byte) 0); // pad + data.writeLong(this.packetCount); + data.writeLong(this.byteCount); + } + + @Override + public int hashCode() { + final int prime = 271; + int result = super.hashCode(); + result = prime * result + (int) (byteCount ^ (byteCount >>> 32)); + result = prime * result + (int) (cookie ^ (cookie >>> 32)); + result = prime * result + durationNanoseconds; + result = prime * result + durationSeconds; + result = prime * result + idleTimeout; + result = prime * result + ((match == null) ? 0 : match.hashCode()); + result = prime * result + (int) (packetCount ^ (packetCount >>> 32)); + result = prime * result + priority; + result = prime * result + ((reason == null) ? 0 : reason.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFFlowRemoved)) { + return false; + } + OFFlowRemoved other = (OFFlowRemoved) obj; + if (byteCount != other.byteCount) { + return false; + } + if (cookie != other.cookie) { + return false; + } + if (durationNanoseconds != other.durationNanoseconds) { + return false; + } + if (durationSeconds != other.durationSeconds) { + return false; + } + if (idleTimeout != other.idleTimeout) { + return false; + } + if (match == null) { + if (other.match != null) { + return false; + } + } else if (!match.equals(other.match)) { + return false; + } + if (packetCount != other.packetCount) { + return false; + } + if (priority != other.priority) { + return false; + } + if (reason == null) { + if (other.reason != null) { + return false; + } + } else if (!reason.equals(other.reason)) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/OFGetConfigReply.java b/src/main/java/org/openflow/protocol/OFGetConfigReply.java new file mode 100644 index 0000000000000000000000000000000000000000..257867afa521eb4b1c753c1e98c03e5433e8cdab --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFGetConfigReply.java @@ -0,0 +1,29 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +/** + * Represents an OFPT_GET_CONFIG_REPLY type message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFGetConfigReply extends OFSwitchConfig { + public OFGetConfigReply() { + super(); + this.type = OFType.GET_CONFIG_REPLY; + } +} diff --git a/src/main/java/org/openflow/protocol/OFGetConfigRequest.java b/src/main/java/org/openflow/protocol/OFGetConfigRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..85c7499243fc73d4c31aa4b0035e9a3353662f89 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFGetConfigRequest.java @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import org.openflow.util.U16; + +/** + * Represents an OFPT_GET_CONFIG_REQUEST type message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFGetConfigRequest extends OFMessage { + public OFGetConfigRequest() { + super(); + this.type = OFType.GET_CONFIG_REQUEST; + this.length = U16.t(OFMessage.MINIMUM_LENGTH); + } +} diff --git a/src/main/java/org/openflow/protocol/OFHello.java b/src/main/java/org/openflow/protocol/OFHello.java new file mode 100644 index 0000000000000000000000000000000000000000..e702ca4d98d986da7b3176bda07abaefe07e2145 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFHello.java @@ -0,0 +1,39 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import org.openflow.util.U16; + + +/** + * Represents an ofp_hello message + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Feb 8, 2010 + */ +public class OFHello extends OFMessage { + public static int MINIMUM_LENGTH = 8; + + /** + * Construct a ofp_hello message + */ + public OFHello() { + super(); + this.type = OFType.HELLO; + this.length = U16.t(MINIMUM_LENGTH); + } +} diff --git a/src/main/java/org/openflow/protocol/OFMatch.java b/src/main/java/org/openflow/protocol/OFMatch.java new file mode 100644 index 0000000000000000000000000000000000000000..8c3c9f495bb300567d2fac10ad76e8d3fe097df6 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFMatch.java @@ -0,0 +1,1145 @@ +/** + * Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior + * University + * + * 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.openflow.protocol; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.util.Arrays; + +import net.floodlightcontroller.packet.Ethernet; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.serializers.OFMatchJSONSerializer; +import org.openflow.util.HexString; +import org.openflow.util.U16; +import org.openflow.util.U8; + +/** + * Represents an ofp_match structure + * + * @author David Erickson (daviderickson@cs.stanford.edu) + * @author Rob Sherwood (rob.sherwood@stanford.edu) + */ +@JsonSerialize(using = OFMatchJSONSerializer.class) +public class OFMatch implements Cloneable, Serializable { + + /** + * + */ + private static final long serialVersionUID = 1L; + public static int MINIMUM_LENGTH = 40; + final public static int OFPFW_ALL = ((1 << 22) - 1); + + final public static int OFPFW_IN_PORT = 1 << 0; /* Switch input port. */ + final public static int OFPFW_DL_VLAN = 1 << 1; /* VLAN id. */ + final public static int OFPFW_DL_SRC = 1 << 2; /* Ethernet source address. */ + final public static int OFPFW_DL_DST = 1 << 3; /* + * Ethernet destination + * address. + */ + final public static int OFPFW_DL_TYPE = 1 << 4; /* Ethernet frame type. */ + final public static int OFPFW_NW_PROTO = 1 << 5; /* IP protocol. */ + final public static int OFPFW_TP_SRC = 1 << 6; /* TCP/UDP source port. */ + final public static int OFPFW_TP_DST = 1 << 7; /* TCP/UDP destination port. */ + + /* + * IP source address wildcard bit count. 0 is exact match, 1 ignores the + * LSB, 2 ignores the 2 least-significant bits, ..., 32 and higher wildcard + * the entire field. This is the *opposite* of the usual convention where + * e.g. /24 indicates that 8 bits (not 24 bits) are wildcarded. + */ + final public static int OFPFW_NW_SRC_SHIFT = 8; + final public static int OFPFW_NW_SRC_BITS = 6; + final public static int OFPFW_NW_SRC_MASK = ((1 << OFPFW_NW_SRC_BITS) - 1) << OFPFW_NW_SRC_SHIFT; + final public static int OFPFW_NW_SRC_ALL = 32 << OFPFW_NW_SRC_SHIFT; + + /* IP destination address wildcard bit count. Same format as source. */ + final public static int OFPFW_NW_DST_SHIFT = 14; + final public static int OFPFW_NW_DST_BITS = 6; + final public static int OFPFW_NW_DST_MASK = ((1 << OFPFW_NW_DST_BITS) - 1) << OFPFW_NW_DST_SHIFT; + final public static int OFPFW_NW_DST_ALL = 32 << OFPFW_NW_DST_SHIFT; + + final public static int OFPFW_DL_VLAN_PCP = 1 << 20; /* VLAN priority. */ + final public static int OFPFW_NW_TOS = 1 << 21; /* + * IP ToS (DSCP field, 6 + * bits). + */ + + final public static int OFPFW_ALL_SANITIZED = (((1 << 22) - 1) + & ~OFPFW_NW_SRC_MASK & ~OFPFW_NW_DST_MASK) + | OFPFW_NW_SRC_ALL + | OFPFW_NW_DST_ALL; + + /* List of Strings for marshalling and unmarshalling to human readable forms */ + final public static String STR_IN_PORT = "in_port"; + final public static String STR_DL_DST = "dl_dst"; + final public static String STR_DL_SRC = "dl_src"; + final public static String STR_DL_TYPE = "dl_type"; + final public static String STR_DL_VLAN = "dl_vlan"; + final public static String STR_DL_VLAN_PCP = "dl_vlan_pcp"; + final public static String STR_NW_DST = "nw_dst"; + final public static String STR_NW_SRC = "nw_src"; + final public static String STR_NW_PROTO = "nw_proto"; + final public static String STR_NW_TOS = "nw_tos"; + final public static String STR_TP_DST = "tp_dst"; + final public static String STR_TP_SRC = "tp_src"; + + protected int wildcards; + protected short inputPort; + protected byte[] dataLayerSource; + protected byte[] dataLayerDestination; + protected short dataLayerVirtualLan; + protected byte dataLayerVirtualLanPriorityCodePoint; + protected short dataLayerType; + protected byte networkTypeOfService; + protected byte networkProtocol; + protected int networkSource; + protected int networkDestination; + protected short transportSource; + protected short transportDestination; + + /** + * By default, create a OFMatch that matches everything (mostly because it's + * the least amount of work to make a valid OFMatch) + */ + public OFMatch() { + this.wildcards = OFPFW_ALL; + this.dataLayerDestination = new byte[] { 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0 }; + this.dataLayerSource = new byte[] { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; + this.dataLayerVirtualLan = Ethernet.VLAN_UNTAGGED; + this.dataLayerVirtualLanPriorityCodePoint = 0; + this.dataLayerType = 0; + this.inputPort = 0; + this.networkProtocol = 0; + this.networkTypeOfService = 0; + this.networkSource = 0; + this.networkDestination = 0; + this.transportDestination = 0; + this.transportSource = 0; + } + + /** + * Get dl_dst + * + * @return an arrays of bytes + */ + public byte[] getDataLayerDestination() { + return this.dataLayerDestination; + } + + /** + * Set dl_dst + * + * @param dataLayerDestination + */ + public OFMatch setDataLayerDestination(byte[] dataLayerDestination) { + this.dataLayerDestination = dataLayerDestination; + return this; + } + + /** + * Set dl_dst, but first translate to byte[] using HexString + * + * @param mac + * A colon separated string of 6 pairs of octets, e..g., + * "00:17:42:EF:CD:8D" + */ + public OFMatch setDataLayerDestination(String mac) { + byte bytes[] = HexString.fromHexString(mac); + if (bytes.length != 6) + throw new IllegalArgumentException( + "expected string with 6 octets, got '" + + mac + + "'"); + this.dataLayerDestination = bytes; + return this; + } + + /** + * Get dl_src + * + * @return an array of bytes + */ + public byte[] getDataLayerSource() { + return this.dataLayerSource; + } + + /** + * Set dl_src + * + * @param dataLayerSource + */ + public OFMatch setDataLayerSource(byte[] dataLayerSource) { + this.dataLayerSource = dataLayerSource; + return this; + } + + /** + * Set dl_src, but first translate to byte[] using HexString + * + * @param mac + * A colon separated string of 6 pairs of octets, e..g., + * "00:17:42:EF:CD:8D" + */ + public OFMatch setDataLayerSource(String mac) { + byte bytes[] = HexString.fromHexString(mac); + if (bytes.length != 6) + throw new IllegalArgumentException( + "expected string with 6 octets, got '" + + mac + + "'"); + this.dataLayerSource = bytes; + return this; + } + + /** + * Get dl_type + * + * @return ether_type + */ + public short getDataLayerType() { + return this.dataLayerType; + } + + /** + * Set dl_type + * + * @param dataLayerType + */ + public OFMatch setDataLayerType(short dataLayerType) { + this.dataLayerType = dataLayerType; + return this; + } + + /** + * Get dl_vlan + * + * @return vlan tag; VLAN_NONE == no tag + */ + public short getDataLayerVirtualLan() { + return this.dataLayerVirtualLan; + } + + /** + * Set dl_vlan + * + * @param dataLayerVirtualLan + */ + public OFMatch setDataLayerVirtualLan(short dataLayerVirtualLan) { + this.dataLayerVirtualLan = dataLayerVirtualLan; + return this; + } + + /** + * Get dl_vlan_pcp + * + * @return + */ + public byte getDataLayerVirtualLanPriorityCodePoint() { + return this.dataLayerVirtualLanPriorityCodePoint; + } + + /** + * Set dl_vlan_pcp + * + * @param pcp + */ + public OFMatch setDataLayerVirtualLanPriorityCodePoint(byte pcp) { + this.dataLayerVirtualLanPriorityCodePoint = pcp; + return this; + } + + /** + * Get in_port + * + * @return + */ + public short getInputPort() { + return this.inputPort; + } + + /** + * Set in_port + * + * @param inputPort + */ + public OFMatch setInputPort(short inputPort) { + this.inputPort = inputPort; + return this; + } + + /** + * Get nw_dst + * + * @return + */ + public int getNetworkDestination() { + return this.networkDestination; + } + + /** + * Set nw_dst + * + * @param networkDestination + */ + public OFMatch setNetworkDestination(int networkDestination) { + this.networkDestination = networkDestination; + return this; + } + + /** + * Parse this match's wildcard fields and return the number of significant + * bits in the IP destination field. NOTE: this returns the number of bits + * that are fixed, i.e., like CIDR, not the number of bits that are free + * like OpenFlow encodes. + * + * @return a number between 0 (matches all IPs) and 63 ( 32>= implies exact + * match) + */ + public int getNetworkDestinationMaskLen() { + return Math.max(32 - ((wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT), + 0); + } + + /** + * Parse this match's wildcard fields and return the number of significant + * bits in the IP destination field. NOTE: this returns the number of bits + * that are fixed, i.e., like CIDR, not the number of bits that are free + * like OpenFlow encodes. + * + * @return a number between 0 (matches all IPs) and 32 (exact match) + */ + public int getNetworkSourceMaskLen() { + return Math.max(32 - ((wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT), + 0); + } + + /** + * Get nw_proto + * + * @return + */ + public byte getNetworkProtocol() { + return this.networkProtocol; + } + + /** + * Set nw_proto + * + * @param networkProtocol + */ + public OFMatch setNetworkProtocol(byte networkProtocol) { + this.networkProtocol = networkProtocol; + return this; + } + + /** + * Get nw_src + * + * @return + */ + public int getNetworkSource() { + return this.networkSource; + } + + /** + * Set nw_src + * + * @param networkSource + */ + public OFMatch setNetworkSource(int networkSource) { + this.networkSource = networkSource; + return this; + } + + /** + * Get nw_tos OFMatch stores the ToS bits as top 6-bits, so right shift by 2 + * bits before returning the value + * + * @return : 6-bit DSCP value (0-63) + */ + public byte getNetworkTypeOfService() { + return (byte) ((this.networkTypeOfService >> 2) & 0x3f); + } + + /** + * Set nw_tos OFMatch stores the ToS bits as top 6-bits, so left shift by 2 + * bits before storing the value + * + * @param networkTypeOfService + * : 6-bit DSCP value (0-63) + */ + public OFMatch setNetworkTypeOfService(byte networkTypeOfService) { + this.networkTypeOfService = (byte) (networkTypeOfService << 2); + return this; + } + + /** + * Get tp_dst + * + * @return + */ + public short getTransportDestination() { + return this.transportDestination; + } + + /** + * Set tp_dst + * + * @param transportDestination + */ + public OFMatch setTransportDestination(short transportDestination) { + this.transportDestination = transportDestination; + return this; + } + + /** + * Get tp_src + * + * @return + */ + public short getTransportSource() { + return this.transportSource; + } + + /** + * Set tp_src + * + * @param transportSource + */ + public OFMatch setTransportSource(short transportSource) { + this.transportSource = transportSource; + return this; + } + + /** + * Get wildcards + * + * @return + */ + public int getWildcards() { + return this.wildcards; + } + + /** + * Get wildcards + * + * @return + */ + public Wildcards getWildcardObj() { + return Wildcards.of(wildcards); + } + + /** + * Set wildcards + * + * @param wildcards + */ + public OFMatch setWildcards(int wildcards) { + this.wildcards = wildcards; + return this; + } + + /** set the wildcard using the Wildcards convenience object */ + public OFMatch setWildcards(Wildcards wildcards) { + this.wildcards = wildcards.getInt(); + return this; + } + + /** + * Initializes this OFMatch structure with the corresponding data from the + * specified packet. Must specify the input port, to ensure that + * this.in_port is set correctly. Specify OFPort.NONE or OFPort.ANY if input + * port not applicable or available + * + * @param packetData + * The packet's data + * @param inputPort + * the port the packet arrived on + */ + public OFMatch loadFromPacket(byte[] packetData, short inputPort) { + short scratch; + int transportOffset = 34; + ByteBuffer packetDataBB = ByteBuffer.wrap(packetData); + int limit = packetDataBB.limit(); + + this.wildcards = 0; // all fields have explicit entries + + this.inputPort = inputPort; + + if (inputPort == OFPort.OFPP_ALL.getValue()) + this.wildcards |= OFPFW_IN_PORT; + + assert (limit >= 14); + // dl dst + this.dataLayerDestination = new byte[6]; + packetDataBB.get(this.dataLayerDestination); + // dl src + this.dataLayerSource = new byte[6]; + packetDataBB.get(this.dataLayerSource); + // dl type + this.dataLayerType = packetDataBB.getShort(); + + if (getDataLayerType() != (short) 0x8100) { // need cast to avoid signed + // bug + setDataLayerVirtualLan((short) 0xffff); + setDataLayerVirtualLanPriorityCodePoint((byte) 0); + } else { + // has vlan tag + scratch = packetDataBB.getShort(); + setDataLayerVirtualLan((short) (0xfff & scratch)); + setDataLayerVirtualLanPriorityCodePoint((byte) ((0xe000 & scratch) >> 13)); + this.dataLayerType = packetDataBB.getShort(); + } + + switch (getDataLayerType()) { + case 0x0800: + // ipv4 + // check packet length + scratch = packetDataBB.get(); + scratch = (short) (0xf & scratch); + transportOffset = (packetDataBB.position() - 1) + + (scratch * 4); + // nw tos (dscp) + scratch = packetDataBB.get(); + setNetworkTypeOfService((byte) ((0xfc & scratch) >> 2)); + // nw protocol + packetDataBB.position(packetDataBB.position() + 7); + this.networkProtocol = packetDataBB.get(); + // nw src + packetDataBB.position(packetDataBB.position() + 2); + this.networkSource = packetDataBB.getInt(); + // nw dst + this.networkDestination = packetDataBB.getInt(); + packetDataBB.position(transportOffset); + break; + case 0x0806: + // arp + int arpPos = packetDataBB.position(); + // opcode + scratch = packetDataBB.getShort(arpPos + 6); + setNetworkProtocol((byte) (0xff & scratch)); + + scratch = packetDataBB.getShort(arpPos + 2); + // if ipv4 and addr len is 4 + if (scratch == 0x800 && packetDataBB.get(arpPos + 5) == 4) { + // nw src + this.networkSource = packetDataBB.getInt(arpPos + 14); + // nw dst + this.networkDestination = packetDataBB.getInt(arpPos + 24); + } else { + setNetworkSource(0); + setNetworkDestination(0); + } + break; + default: + // Not ARP or IP. Wildcard NW_DST and NW_SRC + this.wildcards |= OFPFW_NW_DST_ALL | + OFPFW_NW_SRC_ALL | + OFPFW_NW_PROTO | + OFPFW_NW_TOS; + setNetworkTypeOfService((byte) 0); + setNetworkProtocol((byte) 0); + setNetworkSource(0); + setNetworkDestination(0); + break; + } + + switch (getNetworkProtocol()) { + case 0x01: + // icmp + // type + this.transportSource = U8.f(packetDataBB.get()); + // code + this.transportDestination = U8.f(packetDataBB.get()); + break; + case 0x06: + // tcp + // tcp src + this.transportSource = packetDataBB.getShort(); + // tcp dest + this.transportDestination = packetDataBB.getShort(); + break; + case 0x11: + // udp + // udp src + this.transportSource = packetDataBB.getShort(); + // udp dest + this.transportDestination = packetDataBB.getShort(); + break; + default: + // Unknown network proto. + this.wildcards |= OFPFW_TP_DST | OFPFW_TP_SRC; + setTransportDestination((short) 0); + setTransportSource((short) 0); + break; + } + return this; + } + + /** + * Read this message off the wire from the specified ByteBuffer + * + * @param data + */ + public void readFrom(ChannelBuffer data) { + this.wildcards = data.readInt(); + this.inputPort = data.readShort(); + this.dataLayerSource = new byte[6]; + data.readBytes(this.dataLayerSource); + this.dataLayerDestination = new byte[6]; + data.readBytes(this.dataLayerDestination); + this.dataLayerVirtualLan = data.readShort(); + this.dataLayerVirtualLanPriorityCodePoint = data.readByte(); + data.readByte(); // pad + this.dataLayerType = data.readShort(); + this.networkTypeOfService = data.readByte(); + this.networkProtocol = data.readByte(); + data.readByte(); // pad + data.readByte(); // pad + this.networkSource = data.readInt(); + this.networkDestination = data.readInt(); + this.transportSource = data.readShort(); + this.transportDestination = data.readShort(); + } + + /** + * Write this message's binary format to the specified ByteBuffer + * + * @param data + */ + public void writeTo(ChannelBuffer data) { + data.writeInt(wildcards); + data.writeShort(inputPort); + data.writeBytes(this.dataLayerSource); + data.writeBytes(this.dataLayerDestination); + data.writeShort(dataLayerVirtualLan); + data.writeByte(dataLayerVirtualLanPriorityCodePoint); + data.writeByte((byte) 0x0); // pad + data.writeShort(dataLayerType); + data.writeByte(networkTypeOfService); + data.writeByte(networkProtocol); + data.writeByte((byte) 0x0); // pad + data.writeByte((byte) 0x0); // pad + data.writeInt(networkSource); + data.writeInt(networkDestination); + data.writeShort(transportSource); + data.writeShort(transportDestination); + } + + @Override + public int hashCode() { + final int prime = 131; + int result = 1; + result = prime * result + Arrays.hashCode(dataLayerDestination); + result = prime * result + Arrays.hashCode(dataLayerSource); + result = prime * result + dataLayerType; + result = prime * result + dataLayerVirtualLan; + result = prime * result + dataLayerVirtualLanPriorityCodePoint; + result = prime * result + inputPort; + result = prime * result + networkDestination; + result = prime * result + networkProtocol; + result = prime * result + networkSource; + result = prime * result + networkTypeOfService; + result = prime * result + transportDestination; + result = prime * result + transportSource; + result = prime * result + wildcards; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFMatch)) { + return false; + } + OFMatch other = (OFMatch) obj; + if (!Arrays.equals(dataLayerDestination, other.dataLayerDestination)) { + return false; + } + if (!Arrays.equals(dataLayerSource, other.dataLayerSource)) { + return false; + } + if (dataLayerType != other.dataLayerType) { + return false; + } + if (dataLayerVirtualLan != other.dataLayerVirtualLan) { + return false; + } + if (dataLayerVirtualLanPriorityCodePoint != other.dataLayerVirtualLanPriorityCodePoint) { + return false; + } + if (inputPort != other.inputPort) { + return false; + } + if (networkDestination != other.networkDestination) { + return false; + } + if (networkProtocol != other.networkProtocol) { + return false; + } + if (networkSource != other.networkSource) { + return false; + } + if (networkTypeOfService != other.networkTypeOfService) { + return false; + } + if (transportDestination != other.transportDestination) { + return false; + } + if (transportSource != other.transportSource) { + return false; + } + if ((wildcards & OFMatch.OFPFW_ALL) != (other.wildcards & OFPFW_ALL)) { // only + // consider + // allocated + // part + // of + // wildcards + return false; + } + return true; + } + + /** + * Implement clonable interface + */ + @Override + public OFMatch clone() { + try { + OFMatch ret = (OFMatch) super.clone(); + ret.dataLayerDestination = this.dataLayerDestination.clone(); + ret.dataLayerSource = this.dataLayerSource.clone(); + return ret; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + /** + * matching two OFMatch + * @param toCompare + * @return + */ + public boolean match(OFMatch toCompare) { + if ((wildcards & OFPFW_IN_PORT) == 0 && + this.inputPort != toCompare.getInputPort()) + return false; + if ((wildcards & OFPFW_DL_DST) == 0 && + !Arrays.equals(this.dataLayerDestination, toCompare.getDataLayerDestination())) + return false; + if ((wildcards & OFPFW_DL_SRC) == 0 && + !Arrays.equals(this.dataLayerSource, toCompare.getDataLayerSource())) + return false; + if ((wildcards & OFPFW_DL_TYPE) == 0 + && this.dataLayerType != toCompare.getDataLayerType()) + return false; + if ((wildcards & OFPFW_DL_VLAN) == 0 && + this.dataLayerVirtualLan != toCompare.getDataLayerVirtualLan()) + return false; + if ((wildcards & OFPFW_DL_VLAN_PCP) == 0 && + this.dataLayerVirtualLanPriorityCodePoint != toCompare.getDataLayerVirtualLanPriorityCodePoint()) + return false; + if ((wildcards & OFPFW_NW_PROTO) == 0 && + this.networkProtocol != toCompare.getNetworkProtocol()) + return false; + if ((wildcards & OFPFW_NW_TOS) == 0 && + this.networkTypeOfService != toCompare.getNetworkTypeOfService()) + return false; + //compare network layer src/dst + + int dstmasklen = getNetworkDestinationMaskLen(); + int srcmasklen = getNetworkSourceMaskLen(); + if (dstmasklen >= 32 && networkDestination != toCompare.getNetworkDestination()) + return false; + if (srcmasklen >= 32 && networkSource != toCompare.getNetworkSource()) + return false; + int dstmask = ~((1 << (32 - dstmasklen)) - 1); + int srcmask = ~((1 << (32 - srcmasklen)) - 1); + if (dstmasklen < 32 && + (networkDestination & dstmask) != (toCompare.getNetworkDestination() & dstmask)) + return false; + if (srcmasklen < 32 && + (networkSource & srcmask) != (toCompare.getNetworkSource() & srcmask)) + return false; + //layer 4 + if ((wildcards & OFPFW_TP_DST) == 0 && + this.transportDestination != toCompare.getTransportDestination()) + return false; + if ((wildcards & OFPFW_TP_SRC) == 0 && + this.transportSource != toCompare.getTransportSource()) + return false; + return true; + } + + /** + * Output a dpctl-styled string, i.e., only list the elements that are not + * wildcarded A match-everything OFMatch outputs "OFMatch[]" + * + * @return + * "OFMatch[dl_src:00:20:01:11:22:33,nw_src:192.168.0.0/24,tp_dst:80]" + */ + @Override + public String toString() { + String str = ""; + + // l1 + if ((wildcards & OFPFW_IN_PORT) == 0) + str += "," + STR_IN_PORT + "=" + + U16.f(this.inputPort); + + // l2 + if ((wildcards & OFPFW_DL_DST) == 0) + str += "," + + STR_DL_DST + + "=" + + HexString.toHexString(this.dataLayerDestination); + if ((wildcards & OFPFW_DL_SRC) == 0) + str += "," + + STR_DL_SRC + + "=" + + HexString.toHexString(this.dataLayerSource); + if ((wildcards & OFPFW_DL_TYPE) == 0) + str += "," + + STR_DL_TYPE + + "=0x" + + Integer.toHexString(U16.f(this.dataLayerType)); + if ((wildcards & OFPFW_DL_VLAN) == 0) + str += "," + + STR_DL_VLAN + + "=0x" + + Integer.toHexString(U16.f(this.dataLayerVirtualLan)); + if ((wildcards & OFPFW_DL_VLAN_PCP) == 0) + str += "," + + STR_DL_VLAN_PCP + + "=" + + Integer.toHexString(U8.f(this.dataLayerVirtualLanPriorityCodePoint)); + + // l3 + if (getNetworkDestinationMaskLen() > 0) + str += "," + + STR_NW_DST + + "=" + + cidrToString(networkDestination, + getNetworkDestinationMaskLen()); + if (getNetworkSourceMaskLen() > 0) + str += "," + + STR_NW_SRC + + "=" + + cidrToString(networkSource, + getNetworkSourceMaskLen()); + if ((wildcards & OFPFW_NW_PROTO) == 0) + str += "," + STR_NW_PROTO + + "=" + + this.networkProtocol; + if ((wildcards & OFPFW_NW_TOS) == 0) + str += "," + + STR_NW_TOS + + "=" + + this.getNetworkTypeOfService(); + + // l4 + if ((wildcards & OFPFW_TP_DST) == 0) + str += "," + + STR_TP_DST + + "=" + + this.transportDestination; + if ((wildcards & OFPFW_TP_SRC) == 0) + str += "," + STR_TP_SRC + "=" + + this.transportSource; + if ((str.length() > 0) && (str.charAt(0) == ',')) + str = str.substring(1); // trim + // the + // leading + // "," + // done + return "OFMatch[" + str + "]"; + } + + /** + * Return a string including all match fields, regardless whether they + * are wildcarded or not. + */ + public String toStringUnmasked() { + String str = ""; + + // l1 + str += STR_IN_PORT + "=" + U16.f(this.inputPort); + + // l2 + str += "," + STR_DL_DST + "=" + + HexString.toHexString(this.dataLayerDestination); + str += "," + STR_DL_SRC + "=" + + HexString.toHexString(this.dataLayerSource); + str += "," + STR_DL_TYPE + "=0x" + + Integer.toHexString(U16.f(this.dataLayerType)); + str += "," + STR_DL_VLAN + "=0x" + + Integer.toHexString(U16.f(this.dataLayerVirtualLan)); + str += "," + STR_DL_VLAN_PCP + "=" + + Integer.toHexString(U8.f(this.dataLayerVirtualLanPriorityCodePoint)); + + // l3 + str += "," + STR_NW_DST + "=" + + cidrToString(networkDestination, + getNetworkDestinationMaskLen()); + str += "," + STR_NW_SRC + "=" + + cidrToString(networkSource, + getNetworkSourceMaskLen()); + str += "," + STR_NW_PROTO + "=" + this.networkProtocol; + str += "," + STR_NW_TOS + "=" + this.getNetworkTypeOfService(); + + // l4 + str += "," + STR_TP_DST + "=" + this.transportDestination; + str += "," + STR_TP_SRC + "=" + this.transportSource; + + // wildcards + str += ", wildcards=" + debugWildCards(wildcards); + return "OFMatch[" + str + "]"; + } + + /** + * debug a set of wildcards + */ + public static String debugWildCards(int wildcards) { + String str = ""; + + // l1 + if ((wildcards & OFPFW_IN_PORT) != 0) str += "|" + STR_IN_PORT; + + // l2 + if ((wildcards & OFPFW_DL_DST) != 0) str += "|" + STR_DL_DST; + if ((wildcards & OFPFW_DL_SRC) != 0) str += "|" + STR_DL_SRC; + if ((wildcards & OFPFW_DL_TYPE) != 0) str += "|" + STR_DL_TYPE; + if ((wildcards & OFPFW_DL_VLAN) != 0) str += "|" + STR_DL_VLAN; + if ((wildcards & OFPFW_DL_VLAN_PCP) != 0) + str += "|" + + STR_DL_VLAN_PCP; + + int nwDstMask = Math.max(32 - ((wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT), + 0); + int nwSrcMask = Math.max(32 - ((wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT), + 0); + + // l3 + if (nwDstMask < 32) + str += "|" + STR_NW_DST + "(/" + nwDstMask + ")"; + + if (nwSrcMask < 32) + str += "|" + STR_NW_SRC + "(/" + nwSrcMask + ")"; + + if ((wildcards & OFPFW_NW_PROTO) != 0) str += "|" + STR_NW_PROTO; + if ((wildcards & OFPFW_NW_TOS) != 0) str += "|" + STR_NW_TOS; + + // l4 + if ((wildcards & OFPFW_TP_DST) != 0) str += "|" + STR_TP_DST; + if ((wildcards & OFPFW_TP_SRC) != 0) str += "|" + STR_TP_SRC; + if ((str.length() > 0) && (str.charAt(0) == '|')) + str = str.substring(1); // trim + // the + // leading + // "," + // done + return str; + } + + private String cidrToString(int ip, int prefix) { + String str; + if (prefix >= 32) { + str = ipToString(ip); + } else { + // use the negation of mask to fake endian magic + int mask = ~((1 << (32 - prefix)) - 1); + str = ipToString(ip & mask) + "/" + prefix; + } + + return str; + } + + /** + * Set this OFMatch's parameters based on a comma-separated key=value pair + * dpctl-style string, e.g., from the output of OFMatch.toString() <br> + * <p> + * Supported keys/values include <br> + * <p> + * <TABLE border=1> + * <TR> + * <TD>KEY(s) + * <TD>VALUE + * </TR> + * <TR> + * <TD>"in_port","input_port" + * <TD>integer + * </TR> + * <TR> + * <TD>"dl_src","eth_src", "dl_dst","eth_dst" + * <TD>hex-string + * </TR> + * <TR> + * <TD>"dl_type", "dl_vlan", "dl_vlan_pcp" + * <TD>integer + * </TR> + * <TR> + * <TD>"nw_src", "nw_dst", "ip_src", "ip_dst" + * <TD>CIDR-style netmask + * </TR> + * <TR> + * <TD>"tp_src","tp_dst" + * <TD>integer (max 64k) + * </TR> + * </TABLE> + * <p> + * The CIDR-style netmasks assume 32 netmask if none given, so: + * "128.8.128.118/32" is the same as "128.8.128.118" + * + * @param match + * a key=value comma separated string, e.g. + * "in_port=5,ip_dst=192.168.0.0/16,tp_src=80" + * @throws IllegalArgumentException + * on unexpected key or value + */ + + public void fromString(String match) throws IllegalArgumentException { + if (match.equals("") || match.equalsIgnoreCase("any") + || match.equalsIgnoreCase("all") || match.equals("[]")) + match = "OFMatch[]"; + String[] tokens = match.split("[\\[,\\]]"); + String[] values; + int initArg = 0; + if (tokens[0].equals("OFMatch")) initArg = 1; + this.wildcards = OFPFW_ALL; + int i; + for (i = initArg; i < tokens.length; i++) { + values = tokens[i].split("="); + if (values.length != 2) + throw new IllegalArgumentException( + "Token " + + tokens[i] + + " does not have form 'key=value' parsing " + + match); + values[0] = values[0].toLowerCase(); // try to make this case insens + if (values[0].equals(STR_IN_PORT) + || values[0].equals("input_port")) { + this.inputPort = U16.t(Integer.valueOf(values[1])); + this.wildcards &= ~OFPFW_IN_PORT; + } else if (values[0].equals(STR_DL_DST) + || values[0].equals("eth_dst")) { + this.dataLayerDestination = HexString.fromHexString(values[1]); + this.wildcards &= ~OFPFW_DL_DST; + } else if (values[0].equals(STR_DL_SRC) + || values[0].equals("eth_src")) { + this.dataLayerSource = HexString.fromHexString(values[1]); + this.wildcards &= ~OFPFW_DL_SRC; + } else if (values[0].equals(STR_DL_TYPE) + || values[0].equals("eth_type")) { + if (values[1].startsWith("0x")) + this.dataLayerType = U16.t(Integer.valueOf(values[1].replaceFirst("0x", + ""), + 16)); + else + this.dataLayerType = U16.t(Integer.valueOf(values[1])); + this.wildcards &= ~OFPFW_DL_TYPE; + } else if (values[0].equals(STR_DL_VLAN)) { + if (values[1].startsWith("0x")) + this.dataLayerVirtualLan = U16.t(Integer.valueOf(values[1].replaceFirst("0x", + ""), + 16)); + else + this.dataLayerVirtualLan = U16.t(Integer.valueOf(values[1])); + this.wildcards &= ~OFPFW_DL_VLAN; + } else if (values[0].equals(STR_DL_VLAN_PCP)) { + this.dataLayerVirtualLanPriorityCodePoint = U8.t(Short.valueOf(values[1])); + this.wildcards &= ~OFPFW_DL_VLAN_PCP; + } else if (values[0].equals(STR_NW_DST) + || values[0].equals("ip_dst")) { + setFromCIDR(values[1], STR_NW_DST); + } else if (values[0].equals(STR_NW_SRC) + || values[0].equals("ip_src")) { + setFromCIDR(values[1], STR_NW_SRC); + } else if (values[0].equals(STR_NW_PROTO)) { + if (values[1].startsWith("0x")) + this.networkProtocol = U8.t(Short.valueOf(values[1].replaceFirst("0x",""),16)); + else + this.networkProtocol = U8.t(Short.valueOf(values[1])); + this.wildcards &= ~OFPFW_NW_PROTO; + } else if (values[0].equals(STR_NW_TOS)) { + this.setNetworkTypeOfService(U8.t(Short.valueOf(values[1]))); + this.wildcards &= ~OFPFW_NW_TOS; + } else if (values[0].equals(STR_TP_DST)) { + this.transportDestination = U16.t(Integer.valueOf(values[1])); + this.wildcards &= ~OFPFW_TP_DST; + } else if (values[0].equals(STR_TP_SRC)) { + this.transportSource = U16.t(Integer.valueOf(values[1])); + this.wildcards &= ~OFPFW_TP_SRC; + } else { + throw new IllegalArgumentException("unknown token " + + tokens[i] + " parsing " + + match); + } + } + } + + /** + * Set the networkSource or networkDestionation address and their wildcards + * from the CIDR string + * + * @param cidr + * "192.168.0.0/16" or "172.16.1.5" + * @param which + * one of STR_NW_DST or STR_NW_SRC + * @throws IllegalArgumentException + */ + private + void + setFromCIDR(String cidr, String which) + throws IllegalArgumentException { + String values[] = cidr.split("/"); + String[] ip_str = values[0].split("\\."); + int ip = 0; + ip += Integer.valueOf(ip_str[0]) << 24; + ip += Integer.valueOf(ip_str[1]) << 16; + ip += Integer.valueOf(ip_str[2]) << 8; + ip += Integer.valueOf(ip_str[3]); + int prefix = 32; // all bits are fixed, by default + + if (values.length >= 2) prefix = Integer.valueOf(values[1]); + int mask = 32 - prefix; + if (which.equals(STR_NW_DST)) { + this.networkDestination = ip; + this.wildcards = (wildcards & ~OFPFW_NW_DST_MASK) + | (mask << OFPFW_NW_DST_SHIFT); + } else if (which.equals(STR_NW_SRC)) { + this.networkSource = ip; + this.wildcards = (wildcards & ~OFPFW_NW_SRC_MASK) + | (mask << OFPFW_NW_SRC_SHIFT); + } + } + + protected static String ipToString(int ip) { + return Integer.toString(U8.f((byte) ((ip & 0xff000000) >> 24))) + + "." + Integer.toString((ip & 0x00ff0000) >> 16) + "." + + Integer.toString((ip & 0x0000ff00) >> 8) + "." + + Integer.toString(ip & 0x000000ff); + } +} diff --git a/src/main/java/org/openflow/protocol/OFMatchBeanInfo.java b/src/main/java/org/openflow/protocol/OFMatchBeanInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..97e14a5592b90062153acd0d77b28168250edb38 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFMatchBeanInfo.java @@ -0,0 +1,101 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.beans.SimpleBeanInfo; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.LinkedList; +import java.util.List; + +/** + * Extra info for how to treat OFMatch as a JavaBean + * + * For some (inane!) reason, using chained setters in OFMatch breaks a lot of the JavaBean defaults. + * + * We don't really use OFMatch as a java bean, but there are a lot of nice XML utils that work for + * free if OFMatch follows the java bean paradigm. + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * + */ + +public class OFMatchBeanInfo extends SimpleBeanInfo { + + @Override + public PropertyDescriptor[] getPropertyDescriptors() { + List<PropertyDescriptor> descs = new LinkedList<PropertyDescriptor>(); + Field[] fields = OFMatch.class.getDeclaredFields(); + String name; + for (int i=0; i< fields.length; i++) { + int mod = fields[i].getModifiers(); + if(Modifier.isFinal(mod) || // don't expose static or final fields + Modifier.isStatic(mod)) + continue; + + name = fields[i].getName(); + Class<?> type = fields[i].getType(); + + try { + descs.add(new PropertyDescriptor(name, + name2getter(OFMatch.class, name), + name2setter(OFMatch.class, name, type))); + } catch (IntrospectionException e) { + throw new RuntimeException(e); + } + } + + return descs.toArray(new PropertyDescriptor[0]); + } + + + private Method name2setter(Class<OFMatch> c, String name, Class<?> type) { + String mName = "set" + toLeadingCaps(name); + Method m = null; + try { + m = c.getMethod(mName, new Class[]{ type}); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + return m; + } + + private Method name2getter(Class<OFMatch> c, String name) { + String mName= "get" + toLeadingCaps(name); + Method m = null; + try { + m = c.getMethod(mName, new Class[]{}); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + return m; + } + + private String toLeadingCaps(String s) { + char[] array = s.toCharArray(); + array[0] = Character.toUpperCase(array[0]); + return String.valueOf(array, 0, array.length); + } +} diff --git a/src/main/java/org/openflow/protocol/OFMatchWithSwDpid.java b/src/main/java/org/openflow/protocol/OFMatchWithSwDpid.java new file mode 100644 index 0000000000000000000000000000000000000000..04ecf5e5edea269c912af7b0f5d8b65b17ebaa80 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFMatchWithSwDpid.java @@ -0,0 +1,92 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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.openflow.protocol; + +import org.openflow.util.HexString; + +public class OFMatchWithSwDpid { + protected OFMatch ofMatch; + protected long switchDataPathId; + + public OFMatchWithSwDpid() { + this.ofMatch = new OFMatch(); + this.switchDataPathId = 0; + } + + public OFMatchWithSwDpid(OFMatch ofm, long swDpid) { + this.ofMatch = ofm.clone(); + this.switchDataPathId = swDpid; + } + public OFMatch getOfMatch() { + return ofMatch; + } + + public void setOfMatch(OFMatch ofMatch) { + this.ofMatch = ofMatch.clone(); + } + + public long getSwitchDataPathId() { + return this.switchDataPathId; + } + + public OFMatchWithSwDpid setSwitchDataPathId(long dpid) { + this.switchDataPathId = dpid; + return this; + } + + @Override + public String toString() { + return "OFMatchWithSwDpid [" + HexString.toHexString(switchDataPathId) + + " " + ofMatch + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((ofMatch == null) ? 0 : ofMatch.hashCode()); + result = prime * result + + (int) (switchDataPathId ^ (switchDataPathId >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFMatchWithSwDpid)) { + return false; + } + OFMatchWithSwDpid other = (OFMatchWithSwDpid) obj; + if (ofMatch == null) { + if (other.ofMatch != null) { + return false; + } + } else if (!ofMatch.equals(other.ofMatch)) { + return false; + } + if (switchDataPathId != other.switchDataPathId) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/OFMessage.java b/src/main/java/org/openflow/protocol/OFMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..38f4e477317e79682416b2145985bd4fa3948f5f --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFMessage.java @@ -0,0 +1,336 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.ConcurrentHashMap; + +import net.floodlightcontroller.core.FloodlightContext; +import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.packet.Ethernet; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.HexString; +import org.openflow.util.U16; +import org.openflow.util.U32; +import org.openflow.util.U8; + +/** + * The base class for all OpenFlow protocol messages. This class contains the + * equivalent of the ofp_header which is present in all OpenFlow messages. + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Feb 3, 2010 + * @author Rob Sherwood (rob.sherwood@stanford.edu) - Feb 3, 2010 + */ +public class OFMessage { + public static final int MAXIMUM_LENGTH = (1 << 16) - 1; + public static byte OFP_VERSION = 0x01; + public static int MINIMUM_LENGTH = 8; + + protected byte version; + protected OFType type; + protected short length; + protected int xid; + + private ConcurrentHashMap<String, Object> storage; + + public OFMessage() { + storage = null; + this.version = OFP_VERSION; + } + + protected synchronized ConcurrentHashMap<String, Object> getMessageStore() { + if (storage == null) { + storage = new ConcurrentHashMap<String, Object>();; + } + return storage; + } + + /** + * Get the length of this message + * + * @return + */ + public short getLength() { + return length; + } + + /** + * Get the length of this message, unsigned + * + * @return + */ + public int getLengthU() { + return U16.f(length); + } + + /** + * Set the length of this message + * + * @param length + */ + public OFMessage setLength(short length) { + this.length = length; + return this; + } + + /** + * Set the length of this message, unsigned + * + * @param length + */ + public OFMessage setLengthU(int length) { + this.length = U16.t(length); + return this; + } + + /** + * Get the type of this message + * + * @return + */ + public OFType getType() { + return type; + } + + /** + * Set the type of this message + * + * @param type + */ + public void setType(OFType type) { + this.type = type; + } + + /** + * Get the OpenFlow version of this message + * + * @return + */ + public byte getVersion() { + return version; + } + + /** + * Set the OpenFlow version of this message + * + * @param version + */ + public void setVersion(byte version) { + this.version = version; + } + + /** + * Get the transaction id of this message + * + * @return + */ + public int getXid() { + return xid; + } + + /** + * Set the transaction id of this message + * + * @param xid + */ + public void setXid(int xid) { + this.xid = xid; + } + + /** + * Read this message off the wire from the specified ByteBuffer + * @param data + */ + public void readFrom(ChannelBuffer data) { + this.version = data.readByte(); + this.type = OFType.valueOf(data.readByte()); + this.length = data.readShort(); + this.xid = data.readInt(); + } + + /** + * Write this message's binary format to the specified ByteBuffer + * @param data + */ + public void writeTo(ChannelBuffer data) { + data.writeByte(version); + data.writeByte(type.getTypeValue()); + data.writeShort(length); + data.writeInt(xid); + } + + /** + * Returns a summary of the message + * @return "ofmsg=v=$version;t=$type:l=$len:xid=$xid" + */ + @Override + public String toString() { + return "ofmsg" + + ":v=" + U8.f(this.getVersion()) + + ";t=" + this.getType() + + ";l=" + this.getLengthU() + + ";x=" + U32.f(this.getXid()); + } + + @Override + public int hashCode() { + final int prime = 97; + int result = 1; + result = prime * result + length; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + version; + result = prime * result + xid; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFMessage)) { + return false; + } + OFMessage other = (OFMessage) obj; + if (length != other.length) { + return false; + } + if (type == null) { + if (other.type != null) { + return false; + } + } else if (!type.equals(other.type)) { + return false; + } + if (version != other.version) { + return false; + } + if (xid != other.xid) { + return false; + } + return true; + } + + public static String getDataAsString(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { + + Ethernet eth; + StringBuffer sb = new StringBuffer(""); + + DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS"); + Date date = new Date(); + + sb.append(dateFormat.format(date)); + sb.append(" "); + + switch (msg.getType()) { + case PACKET_IN: + OFPacketIn pktIn = (OFPacketIn) msg; + sb.append("packet_in [ "); + sb.append(sw.getStringId()); + sb.append(" -> Controller"); + sb.append(" ]"); + + sb.append("\ntotal length: "); + sb.append(pktIn.getTotalLength()); + sb.append("\nin_port: "); + sb.append(pktIn.getInPort()); + sb.append("\ndata_length: "); + sb.append(pktIn.getTotalLength() - OFPacketIn.MINIMUM_LENGTH); + sb.append("\nbuffer: "); + sb.append(pktIn.getBufferId()); + + // If the conext is not set by floodlight, then ignore. + if (cntx != null) { + // packet type icmp, arp, etc. + eth = IFloodlightProviderService.bcStore.get(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD); + if (eth != null) + sb.append(eth.toString()); + } + break; + + case PACKET_OUT: + OFPacketOut pktOut = (OFPacketOut) msg; + sb.append("packet_out [ "); + sb.append("Controller -> "); + sb.append(HexString.toHexString(sw.getId())); + sb.append(" ]"); + + sb.append("\nin_port: "); + sb.append(pktOut.getInPort()); + sb.append("\nactions_len: "); + sb.append(pktOut.getActionsLength()); + if (pktOut.getActions() != null) { + sb.append("\nactions: "); + sb.append(pktOut.getActions().toString()); + } + break; + + case FLOW_MOD: + OFFlowMod fm = (OFFlowMod) msg; + sb.append("flow_mod [ "); + sb.append("Controller -> "); + sb.append(HexString.toHexString(sw.getId())); + sb.append(" ]"); + + // If the conext is not set by floodlight, then ignore. + if (cntx != null) { + eth = IFloodlightProviderService.bcStore.get(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD); + if (eth != null) + sb.append(eth.toString()); + } + + sb.append("\nADD: cookie: "); + sb.append(fm.getCookie()); + sb.append(" idle: "); + sb.append(fm.getIdleTimeout()); + sb.append(" hard: "); + sb.append(fm.getHardTimeout()); + sb.append(" pri: "); + sb.append(fm.getPriority()); + sb.append(" buf: "); + sb.append(fm.getBufferId()); + sb.append(" flg: "); + sb.append(fm.getFlags()); + if (fm.getActions() != null) { + sb.append("\nactions: "); + sb.append(fm.getActions().toString()); + } + break; + + default: + sb.append("[Unknown Packet]"); + } + + sb.append("\n\n"); + return sb.toString(); + + } + + public static byte[] getData(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { + return OFMessage.getDataAsString(sw, msg, cntx).getBytes(); + } +} diff --git a/src/main/java/org/openflow/protocol/OFMessageContextStore.java b/src/main/java/org/openflow/protocol/OFMessageContextStore.java new file mode 100644 index 0000000000000000000000000000000000000000..b60aa1cd4b981ef94335bf1eb67447894de2cf15 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFMessageContextStore.java @@ -0,0 +1,39 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import org.openflow.protocol.OFMessage; + +public class OFMessageContextStore<V> { + protected OFMessage msg; + String namespace; + + public OFMessageContextStore(OFMessage msg, String namespace) { + this.msg = msg; + this.namespace = namespace; + } + + @SuppressWarnings("unchecked") + public V get(String key) { + return (V)msg.getMessageStore().get(namespace + "|" + key); + } + + public void put(String key, V value) { + msg.getMessageStore().put(namespace + "|" + key, value); + } +} diff --git a/src/main/java/org/openflow/protocol/OFPacketIn.java b/src/main/java/org/openflow/protocol/OFPacketIn.java new file mode 100644 index 0000000000000000000000000000000000000000..c37c9184cc0bd710989a0b8197aab5146b6d8ad2 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFPacketIn.java @@ -0,0 +1,211 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; +import org.openflow.util.U32; +import org.openflow.util.U8; + +/** + * Represents an ofp_packet_in + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Feb 8, 2010 + */ +public class OFPacketIn extends OFMessage { + public static short MINIMUM_LENGTH = 18; + + public enum OFPacketInReason { + NO_MATCH, ACTION + } + + protected int bufferId; + protected short totalLength; + protected short inPort; + protected OFPacketInReason reason; + protected byte[] packetData; + + public OFPacketIn() { + super(); + this.type = OFType.PACKET_IN; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * Get buffer_id + * @return + */ + public int getBufferId() { + return this.bufferId; + } + + /** + * Set buffer_id + * @param bufferId + */ + public OFPacketIn setBufferId(int bufferId) { + this.bufferId = bufferId; + return this; + } + + /** + * Returns the packet data + * @return + */ + public byte[] getPacketData() { + return this.packetData; + } + + /** + * Sets the packet data, and updates the length of this message + * @param packetData + */ + public OFPacketIn setPacketData(byte[] packetData) { + this.packetData = packetData; + this.length = U16.t(OFPacketIn.MINIMUM_LENGTH + packetData.length); + return this; + } + + /** + * Get in_port + * @return + */ + public short getInPort() { + return this.inPort; + } + + /** + * Set in_port + * @param inPort + */ + public OFPacketIn setInPort(short inPort) { + this.inPort = inPort; + return this; + } + + /** + * Get reason + * @return + */ + public OFPacketInReason getReason() { + return this.reason; + } + + /** + * Set reason + * @param reason + */ + public OFPacketIn setReason(OFPacketInReason reason) { + this.reason = reason; + return this; + } + + /** + * Get total_len + * @return + */ + public short getTotalLength() { + return this.totalLength; + } + + /** + * Set total_len + * @param totalLength + */ + public OFPacketIn setTotalLength(short totalLength) { + this.totalLength = totalLength; + return this; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.bufferId = data.readInt(); + this.totalLength = data.readShort(); + this.inPort = data.readShort(); + this.reason = OFPacketInReason.values()[U8.f(data.readByte())]; + data.readByte(); // pad + this.packetData = new byte[getLengthU() - MINIMUM_LENGTH]; + data.readBytes(this.packetData); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(bufferId); + data.writeShort(totalLength); + data.writeShort(inPort); + data.writeByte((byte) reason.ordinal()); + data.writeByte((byte) 0x0); // pad + data.writeBytes(this.packetData); + } + + @Override + public int hashCode() { + final int prime = 283; + int result = super.hashCode(); + result = prime * result + bufferId; + result = prime * result + inPort; + result = prime * result + Arrays.hashCode(packetData); + result = prime * result + ((reason == null) ? 0 : reason.hashCode()); + result = prime * result + totalLength; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFPacketIn)) { + return false; + } + OFPacketIn other = (OFPacketIn) obj; + if (bufferId != other.bufferId) { + return false; + } + if (inPort != other.inPort) { + return false; + } + if (!Arrays.equals(packetData, other.packetData)) { + return false; + } + if (reason == null) { + if (other.reason != null) { + return false; + } + } else if (!reason.equals(other.reason)) { + return false; + } + if (totalLength != other.totalLength) { + return false; + } + return true; + } + + public String toString() { + String myStr = super.toString(); + return "packetIn" + + ":bufferId=" + U32.f(this.bufferId) + myStr; + } +} diff --git a/src/main/java/org/openflow/protocol/OFPacketOut.java b/src/main/java/org/openflow/protocol/OFPacketOut.java new file mode 100644 index 0000000000000000000000000000000000000000..ef4aa61f1b0e86a245bc6dd5ba9aa55c0ba2557d --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFPacketOut.java @@ -0,0 +1,260 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.util.Arrays; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.factory.OFActionFactory; +import org.openflow.protocol.factory.OFActionFactoryAware; +import org.openflow.util.HexString; +import org.openflow.util.U16; + +/** + * Represents an ofp_packet_out message + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 12, 2010 + */ +public class OFPacketOut extends OFMessage implements OFActionFactoryAware { + public static int MINIMUM_LENGTH = 16; + public static int BUFFER_ID_NONE = 0xffffffff; + + protected OFActionFactory actionFactory; + protected int bufferId; + protected short inPort; + protected short actionsLength; + protected List<OFAction> actions; + protected byte[] packetData; + + public OFPacketOut() { + super(); + this.type = OFType.PACKET_OUT; + this.length = U16.t(MINIMUM_LENGTH); + this.bufferId = BUFFER_ID_NONE; + } + + /** + * Get buffer_id + * @return + */ + public int getBufferId() { + return this.bufferId; + } + + /** + * Set buffer_id + * @param bufferId + */ + public OFPacketOut setBufferId(int bufferId) { + if (packetData != null && packetData.length > 0 && bufferId != BUFFER_ID_NONE) { + throw new IllegalArgumentException( + "PacketOut should not have both bufferId and packetData set"); + } + this.bufferId = bufferId; + return this; + } + + /** + * Returns the packet data + * @return + */ + public byte[] getPacketData() { + return this.packetData; + } + + /** + * Sets the packet data + * @param packetData + */ + public OFPacketOut setPacketData(byte[] packetData) { + if (packetData != null && packetData.length > 0 && bufferId != BUFFER_ID_NONE) { + throw new IllegalArgumentException( + "PacketOut should not have both bufferId and packetData set"); + } + this.packetData = packetData; + return this; + } + + /** + * Get in_port + * @return + */ + public short getInPort() { + return this.inPort; + } + + /** + * Set in_port + * @param inPort + */ + public OFPacketOut setInPort(short inPort) { + this.inPort = inPort; + return this; + } + + /** + * Set in_port. Convenience method using OFPort enum. + * @param inPort + */ + public OFPacketOut setInPort(OFPort inPort) { + this.inPort = inPort.getValue(); + return this; + } + + /** + * Get actions_len + * @return + */ + public short getActionsLength() { + return this.actionsLength; + } + + /** + * Get actions_len, unsigned + * @return + */ + public int getActionsLengthU() { + return U16.f(this.actionsLength); + } + + /** + * Set actions_len + * @param actionsLength + */ + public OFPacketOut setActionsLength(short actionsLength) { + this.actionsLength = actionsLength; + return this; + } + + /** + * Returns the actions contained in this message + * @return a list of ordered OFAction objects + */ + public List<OFAction> getActions() { + return this.actions; + } + + /** + * Sets the list of actions on this message + * @param actions a list of ordered OFAction objects + */ + public OFPacketOut setActions(List<OFAction> actions) { + this.actions = actions; + return this; + } + + @Override + public void setActionFactory(OFActionFactory actionFactory) { + this.actionFactory = actionFactory; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.bufferId = data.readInt(); + this.inPort = data.readShort(); + this.actionsLength = data.readShort(); + if ( this.actionFactory == null) + throw new RuntimeException("ActionFactory not set"); + this.actions = this.actionFactory.parseActions(data, getActionsLengthU()); + this.packetData = new byte[getLengthU() - MINIMUM_LENGTH - getActionsLengthU()]; + data.readBytes(this.packetData); + validate(); + } + + @Override + public void writeTo(ChannelBuffer data) { + validate(); + super.writeTo(data); + data.writeInt(bufferId); + data.writeShort(inPort); + data.writeShort(actionsLength); + for (OFAction action : actions) { + action.writeTo(data); + } + if (this.packetData != null) + data.writeBytes(this.packetData); + } + + /** validate the invariants of this OFMessage hold */ + public void validate() { + if (!((bufferId != BUFFER_ID_NONE) ^ (packetData != null && packetData.length > 0))) { + throw new IllegalStateException( + "OFPacketOut must have exactly one of (bufferId, packetData) set (not one, not both)"); + } + } + + @Override + public int hashCode() { + final int prime = 293; + int result = super.hashCode(); + result = prime * result + ((actions == null) ? 0 : actions.hashCode()); + result = prime * result + actionsLength; + result = prime * result + bufferId; + result = prime * result + inPort; + result = prime * result + Arrays.hashCode(packetData); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFPacketOut)) { + return false; + } + OFPacketOut other = (OFPacketOut) obj; + if (actions == null) { + if (other.actions != null) { + return false; + } + } else if (!actions.equals(other.actions)) { + return false; + } + if (actionsLength != other.actionsLength) { + return false; + } + if (bufferId != other.bufferId) { + return false; + } + if (inPort != other.inPort) { + return false; + } + if (!Arrays.equals(packetData, other.packetData)) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "OFPacketOut [actionFactory=" + actionFactory + ", actions=" + + actions + ", actionsLength=" + actionsLength + ", bufferId=0x" + + Integer.toHexString(bufferId) + ", inPort=" + inPort + ", packetData=" + + HexString.toHexString(packetData) + "]"; + } +} diff --git a/src/main/java/org/openflow/protocol/OFPacketQueue.java b/src/main/java/org/openflow/protocol/OFPacketQueue.java new file mode 100644 index 0000000000000000000000000000000000000000..e8de1af56b950465c23cc140ad4991e0922aa34f --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFPacketQueue.java @@ -0,0 +1,142 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* 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.openflow.protocol; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents ofp_packet_queue + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFPacketQueue { + public static int MINIMUM_LENGTH = 8; + + protected int queueId; + protected short length; + protected List<OFQueueProp> properties = new ArrayList<OFQueueProp>(); + + public OFPacketQueue() { + this.queueId = -1; + this.length = U16.t(MINIMUM_LENGTH); + } + + public OFPacketQueue(int queueId) { + this.queueId = queueId; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * @return the queueId + */ + public long getQueueId() { + return queueId; + } + + /** + * @param queueId the queueId to set + */ + public void setQueueId(int queueId) { + this.queueId = queueId; + } + + /** + * @return the queue's properties + */ + public List<OFQueueProp> getProperties() { + return properties; + } + + /** + * @param properties the properties to set + */ + public void setProperties(List<OFQueueProp> properties) { + this.properties = properties; + + this.length = U16.t(MINIMUM_LENGTH); + for (OFQueueProp prop : properties) { + this.length += prop.getLength(); + } + } + + /** + * @return the length + */ + public short getLength() { + return length; + } + + public void readFrom(ChannelBuffer data) { + this.queueId = data.readInt(); + this.length = data.readShort(); + data.readShort(); // pad + + int availLength = (this.length - MINIMUM_LENGTH); + this.properties.clear(); + + while (availLength > 0) { + OFQueueProp prop = new OFQueueProp(); + prop.readFrom(data); + properties.add(prop); + availLength -= prop.getLength(); + } + } + + public void writeTo(ChannelBuffer data) { + data.writeInt(queueId); + data.writeShort(length); + data.writeShort(0); // pad + + for (OFQueueProp prop : properties) { + prop.writeTo(data); + } + } + + @Override + public int hashCode() { + final int prime = 359; + int result = super.hashCode(); + result = prime * result + queueId; + result = prime * result + length; + result = prime * result + properties.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFPacketQueue)) { + return false; + } + OFPacketQueue other = (OFPacketQueue) obj; + if (queueId != other.queueId) { + return false; + } + if (! properties.equals(other.properties)) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/OFPhysicalPort.java b/src/main/java/org/openflow/protocol/OFPhysicalPort.java new file mode 100644 index 0000000000000000000000000000000000000000..88de8f6570cb3d62370d1d2726fb6350fae36547 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFPhysicalPort.java @@ -0,0 +1,614 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.Arrays; + +import net.floodlightcontroller.core.web.serializers.ByteArrayMACSerializer; +import net.floodlightcontroller.core.web.serializers.UShortSerializer; +import net.floodlightcontroller.util.EnumBitmaps; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.HexString; + +/** + * Represents ofp_phy_port + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 25, 2010 + */ +public class OFPhysicalPort { + public final static int MINIMUM_LENGTH = 48; + public final static int OFP_ETH_ALEN = 6; + + public enum OFPortConfig implements EnumBitmaps.BitmapableEnum { + OFPPC_PORT_DOWN (1 << 0) { + @Override + public String toString() { + return "port-down (0x1)"; + } + }, + OFPPC_NO_STP (1 << 1) { + @Override + public String toString() { + return "no-stp (0x2)"; + } + }, + OFPPC_NO_RECV (1 << 2) { + @Override + public String toString() { + return "no-recv (0x4)"; + } + }, + OFPPC_NO_RECV_STP (1 << 3) { + @Override + public String toString() { + return "no-recv-stp (0x8)"; + } + }, + OFPPC_NO_FLOOD (1 << 4) { + @Override + public String toString() { + return "no-flood (0x10)"; + } + }, + OFPPC_NO_FWD (1 << 5) { + @Override + public String toString() { + return "no-fwd (0x20)"; + } + }, + OFPPC_NO_PACKET_IN (1 << 6) { + @Override + public String toString() { + return "no-pkt-in (0x40)"; + } + }, + OFPPC_BSN_MIRROR_DEST (1 << 31) { + @Override + public String toString() { + return "bsn-mirror-dest (0x80000000)"; + } + }; + + + protected int value; + + private OFPortConfig(int value) { + this.value = value; + } + + /** + * @return the value + */ + @Override + public int getValue() { + return value; + } + } + + public enum OFPortState { + OFPPS_LINK_DOWN (1 << 0, false) { + @Override + public String toString() { + return "link-down (0x1)"; + } + }, + OFPPS_STP_LISTEN (0 << 8, true) { + @Override + public String toString() { + return "listen (0x0)"; + } + }, + OFPPS_STP_LEARN (1 << 8, true) { + @Override + public String toString() { + return "learn-no-relay (0x100)"; + } + }, + OFPPS_STP_FORWARD (2 << 8, true) { + @Override + public String toString() { + return "forward (0x200)"; + } + }, + OFPPS_STP_BLOCK (3 << 8, true) { + @Override + public String toString() { + return "block-broadcast (0x300)"; + } + }, + OFPPS_STP_MASK (3 << 8, false) { // used for STP but not an STP state + @Override + public String toString() { + return "block-broadcast (0x300)"; + } + }; + + protected int value; + protected boolean isStpState; + + private OFPortState(int value, boolean isStpState) { + this.value = value; + this.isStpState = isStpState; + } + + /** + * Returns true if this constants represents one of the four STP + * states + * @return + */ + public boolean isStpState() { + return isStpState; + } + + /** + * return the STP state represented by the given integer. + * the returned value will have isStpState() == true + * @param state + * @return + */ + public static OFPortState getStpState(int state) { + // this ain't pretty + state = state & OFPortState.OFPPS_STP_MASK.getValue(); + for (OFPortState s: OFPortState.values()) { + if (!s.isStpState()) + continue; + if (state == s.getValue()) + return s; + } + return null; // will never happen + } + + public static boolean isPortDown(int state) { + if ((state & OFPPS_LINK_DOWN.getValue()) != 0) + return true; + else + return false; + } + + /** + * @return the value + */ + public int getValue() { + return value; + } + } + + /** + * Represents the speed of a port + */ + public enum PortSpeed { + /** no speed set */ + SPEED_NONE(0), + SPEED_10MB(10), + SPEED_100MB(100), + SPEED_1GB(1000), + SPEED_10GB(10000); + + private long speedInBps; + private PortSpeed(int speedInMbps) { + this.speedInBps = speedInMbps * 1000*1000; + } + + public long getSpeedBps() { + return this.speedInBps; + } + + public static PortSpeed max(PortSpeed s1, PortSpeed s2) { + return (s1.getSpeedBps() > s2.getSpeedBps()) ? s1 : s2; + } + + public static PortSpeed min(PortSpeed s1, PortSpeed s2) { + return (s1.getSpeedBps() < s2.getSpeedBps()) ? s1 : s2; + } + } + + public enum OFPortFeatures implements EnumBitmaps.BitmapableEnum { + OFPPF_10MB_HD (1 << 0, PortSpeed.SPEED_10MB) { + @Override + public String toString() { + return "10mb-hd (0x1)"; + } + }, + OFPPF_10MB_FD (1 << 1, PortSpeed.SPEED_10MB) { + @Override + public String toString() { + return "10mb-fd (0x2)"; + } + }, + OFPPF_100MB_HD (1 << 2, PortSpeed.SPEED_100MB) { + @Override + public String toString() { + return "100mb-hd (0x4)"; + } + }, + OFPPF_100MB_FD (1 << 3, PortSpeed.SPEED_100MB) { + @Override + public String toString() { + return "100mb-fd (0x8)"; + } + }, + OFPPF_1GB_HD (1 << 4, PortSpeed.SPEED_1GB) { + @Override + public String toString() { + return "1gb-hd (0x10)"; + } + }, + OFPPF_1GB_FD (1 << 5, PortSpeed.SPEED_1GB) { + @Override + public String toString() { + return "1gb-fd (0x20)"; + } + }, + OFPPF_10GB_FD (1 << 6, PortSpeed.SPEED_10GB) { + @Override + public String toString() { + return "10gb-fd (0x40)"; + } + }, + OFPPF_COPPER (1 << 7, PortSpeed.SPEED_NONE) { + @Override + public String toString() { + return "copper (0x80)"; + } + }, + OFPPF_FIBER (1 << 8, PortSpeed.SPEED_NONE) { + @Override + public String toString() { + return "fiber (0x100)"; + } + }, + OFPPF_AUTONEG (1 << 9, PortSpeed.SPEED_NONE) { + @Override + public String toString() { + return "autoneg (0x200)"; + } + }, + OFPPF_PAUSE (1 << 10, PortSpeed.SPEED_NONE) { + @Override + public String toString() { + return "pause (0x400)"; + } + }, + OFPPF_PAUSE_ASYM (1 << 11, PortSpeed.SPEED_NONE) { + @Override + public String toString() { + return "pause-asym (0x800)"; + } + }; + + protected int value; + protected PortSpeed speed; + + private OFPortFeatures(int value, PortSpeed speed) { + this.value = value; + if (speed == null) + throw new NullPointerException(); + this.speed = speed; + } + + /** + * @return the bitmap value for this constant + */ + @Override + public int getValue() { + return value; + } + + /** + * @return the port speed associated with this constant. If the + * constant doesn't represent a port speed it returns SPEED_NONE + */ + public PortSpeed getSpeed() { + return speed; + } + } + + protected short portNumber; + protected byte[] hardwareAddress; + protected String name; + protected int config; + protected int state; + protected int currentFeatures; + protected int advertisedFeatures; + protected int supportedFeatures; + protected int peerFeatures; + + /** + * @return the portNumber + */ + @JsonSerialize(using=UShortSerializer.class) + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + /** + * @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; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the config + */ + public int getConfig() { + return config; + } + + /** + * @param config the config to set + */ + public void setConfig(int config) { + this.config = config; + } + + /** + * @return the state + */ + public int getState() { + return state; + } + + /** + * @param state the state to set + */ + public void setState(int state) { + this.state = state; + } + + /** + * @return the currentFeatures + */ + public int getCurrentFeatures() { + return currentFeatures; + } + + /** + * @param currentFeatures the currentFeatures to set + */ + public void setCurrentFeatures(int currentFeatures) { + this.currentFeatures = currentFeatures; + } + + /** + * @return the advertisedFeatures + */ + public int getAdvertisedFeatures() { + return advertisedFeatures; + } + + /** + * @param advertisedFeatures the advertisedFeatures to set + */ + public void setAdvertisedFeatures(int advertisedFeatures) { + this.advertisedFeatures = advertisedFeatures; + } + + /** + * @return the supportedFeatures + */ + public int getSupportedFeatures() { + return supportedFeatures; + } + + /** + * @param supportedFeatures the supportedFeatures to set + */ + public void setSupportedFeatures(int supportedFeatures) { + this.supportedFeatures = supportedFeatures; + } + + /** + * @return the peerFeatures + */ + public int getPeerFeatures() { + return peerFeatures; + } + + /** + * @param peerFeatures the peerFeatures to set + */ + public void setPeerFeatures(int peerFeatures) { + this.peerFeatures = peerFeatures; + } + + /** + * Read this message off the wire from the specified ByteBuffer + * @param data + */ + public void readFrom(ChannelBuffer data) { + this.portNumber = data.readShort(); + if (this.hardwareAddress == null) + this.hardwareAddress = new byte[OFP_ETH_ALEN]; + data.readBytes(this.hardwareAddress); + 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")); + this.config = data.readInt(); + this.state = data.readInt(); + this.currentFeatures = data.readInt(); + this.advertisedFeatures = data.readInt(); + this.supportedFeatures = data.readInt(); + this.peerFeatures = data.readInt(); + } + + /** + * Write this message's binary format to the specified ByteBuffer + * @param data + */ + public void writeTo(ChannelBuffer data) { + data.writeShort(this.portNumber); + data.writeBytes(hardwareAddress); + try { + byte[] name = this.name.getBytes("ASCII"); + if (name.length < 16) { + data.writeBytes(name); + for (int i = name.length; i < 16; ++i) { + data.writeByte((byte) 0); + } + } else { + data.writeBytes(name, 0, 15); + data.writeByte((byte) 0); + } + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + data.writeInt(this.config); + data.writeInt(this.state); + data.writeInt(this.currentFeatures); + data.writeInt(this.advertisedFeatures); + data.writeInt(this.supportedFeatures); + data.writeInt(this.peerFeatures); + } + + @Override + public int hashCode() { + final int prime = 307; + int result = 1; + result = prime * result + advertisedFeatures; + result = prime * result + config; + result = prime * result + currentFeatures; + result = prime * result + Arrays.hashCode(hardwareAddress); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + peerFeatures; + result = prime * result + portNumber; + result = prime * result + state; + result = prime * result + supportedFeatures; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFPhysicalPort)) { + return false; + } + OFPhysicalPort other = (OFPhysicalPort) obj; + if (advertisedFeatures != other.advertisedFeatures) { + return false; + } + if (config != other.config) { + return false; + } + if (currentFeatures != other.currentFeatures) { + return false; + } + if (!Arrays.equals(hardwareAddress, other.hardwareAddress)) { + return false; + } + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (peerFeatures != other.peerFeatures) { + return false; + } + if (portNumber != other.portNumber) { + return false; + } + if (state != other.state) { + return false; + } + if (supportedFeatures != other.supportedFeatures) { + return false; + } + return true; + } + + @Override + public String toString() { + String linkState, linkSpeed; + if ((state & OFPortState.OFPPS_LINK_DOWN.getValue()) != 0) { + linkState = "down"; + } else { + linkState = "up"; + } + if ((currentFeatures & OFPortFeatures.OFPPF_10GB_FD.getValue()) != 0) { + linkSpeed = "10G"; + } else if ((currentFeatures & OFPortFeatures.OFPPF_1GB_FD.getValue()) != 0) { + linkSpeed = "1G"; + } else if ((currentFeatures & OFPortFeatures.OFPPF_1GB_HD.getValue()) != 0) { + linkSpeed = "1G(half-duplex)"; + } else if ((currentFeatures & OFPortFeatures.OFPPF_100MB_FD.getValue()) != 0) { + linkSpeed = "100M"; + } else if ((currentFeatures & OFPortFeatures.OFPPF_100MB_HD.getValue()) != 0) { + linkSpeed = "100M(half-duplex)"; + } else if ((currentFeatures & OFPortFeatures.OFPPF_10MB_FD.getValue()) != 0) { + linkSpeed = "10M"; + } else if ((currentFeatures & OFPortFeatures.OFPPF_10MB_HD.getValue()) != 0) { + linkSpeed = "10M(half-duplex)"; + } else { + linkSpeed = "unknown"; + } + return "port " + name + " (" + portNumber + ")" + + ", mac " + HexString.toHexString(hardwareAddress) + + ", state " + linkState + + ", speed " + linkSpeed; + } +} diff --git a/src/main/java/org/openflow/protocol/OFPort.java b/src/main/java/org/openflow/protocol/OFPort.java new file mode 100644 index 0000000000000000000000000000000000000000..93301bcb097486e454ec9c82bfd5768144d2cd77 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFPort.java @@ -0,0 +1,43 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +public enum OFPort { + OFPP_MAX ((short)0xff00), + OFPP_IN_PORT ((short)0xfff8), + OFPP_TABLE ((short)0xfff9), + OFPP_NORMAL ((short)0xfffa), + OFPP_FLOOD ((short)0xfffb), + OFPP_ALL ((short)0xfffc), + OFPP_CONTROLLER ((short)0xfffd), + OFPP_LOCAL ((short)0xfffe), + OFPP_NONE ((short)0xffff); + + protected short value; + + private OFPort(short value) { + this.value = value; + } + + /** + * @return the value + */ + public short getValue() { + return value; + } +} diff --git a/src/main/java/org/openflow/protocol/OFPortMod.java b/src/main/java/org/openflow/protocol/OFPortMod.java new file mode 100644 index 0000000000000000000000000000000000000000..876e856700788c8a8d64b4ab37fe8febf87b5da7 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFPortMod.java @@ -0,0 +1,182 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents an ofp_port_mod message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFPortMod extends OFMessage { + public static int MINIMUM_LENGTH = 32; + + protected short portNumber; + protected byte[] hardwareAddress; + protected int config; + protected int mask; + protected int advertise; + + public OFPortMod() { + super(); + this.type = OFType.PORT_MOD; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + /** + * @return the hardwareAddress + */ + public byte[] getHardwareAddress() { + return hardwareAddress; + } + + /** + * @param hardwareAddress the hardwareAddress to set + */ + public void setHardwareAddress(byte[] hardwareAddress) { + if (hardwareAddress.length != OFPhysicalPort.OFP_ETH_ALEN) + throw new RuntimeException("Hardware address must have length " + + OFPhysicalPort.OFP_ETH_ALEN); + this.hardwareAddress = hardwareAddress; + } + + /** + * @return the config + */ + public int getConfig() { + return config; + } + + /** + * @param config the config to set + */ + public void setConfig(int config) { + this.config = config; + } + + /** + * @return the mask + */ + public int getMask() { + return mask; + } + + /** + * @param mask the mask to set + */ + public void setMask(int mask) { + this.mask = mask; + } + + /** + * @return the advertise + */ + public int getAdvertise() { + return advertise; + } + + /** + * @param advertise the advertise to set + */ + public void setAdvertise(int advertise) { + this.advertise = advertise; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.portNumber = data.readShort(); + if (this.hardwareAddress == null) + this.hardwareAddress = new byte[OFPhysicalPort.OFP_ETH_ALEN]; + data.readBytes(this.hardwareAddress); + this.config = data.readInt(); + this.mask = data.readInt(); + this.advertise = data.readInt(); + data.readInt(); // pad + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.portNumber); + data.writeBytes(this.hardwareAddress); + data.writeInt(this.config); + data.writeInt(this.mask); + data.writeInt(this.advertise); + data.writeInt(0); // pad + } + + @Override + public int hashCode() { + final int prime = 311; + int result = super.hashCode(); + result = prime * result + advertise; + result = prime * result + config; + result = prime * result + Arrays.hashCode(hardwareAddress); + result = prime * result + mask; + result = prime * result + portNumber; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFPortMod)) { + return false; + } + OFPortMod other = (OFPortMod) obj; + if (advertise != other.advertise) { + return false; + } + if (config != other.config) { + return false; + } + if (!Arrays.equals(hardwareAddress, other.hardwareAddress)) { + return false; + } + if (mask != other.mask) { + return false; + } + if (portNumber != other.portNumber) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/OFPortStatus.java b/src/main/java/org/openflow/protocol/OFPortStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..b7a3158ceee2ef7d6cc99086089cbe8e4203531f --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFPortStatus.java @@ -0,0 +1,144 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents an ofp_port_status message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFPortStatus extends OFMessage { + public static int MINIMUM_LENGTH = 64; + + public enum OFPortReason { + OFPPR_ADD((byte)0), + OFPPR_DELETE((byte)1), + OFPPR_MODIFY((byte)2); + + private byte reason; + + private OFPortReason(byte reason) { + this.reason = reason; + } + + public byte getReasonCode() { + return this.reason; + } + + public static OFPortReason fromReasonCode(byte reason) { + for (OFPortReason r: OFPortReason.values()) { + if (r.getReasonCode() == reason) + return r; + } + return null; + } + } + + protected byte reason; + protected OFPhysicalPort desc; + + /** + * @return the reason + */ + public byte getReason() { + return reason; + } + + /** + * @param reason the reason to set + */ + public void setReason(byte reason) { + this.reason = reason; + } + + /** + * @return the desc + */ + public OFPhysicalPort getDesc() { + return desc; + } + + /** + * @param desc the desc to set + */ + public void setDesc(OFPhysicalPort desc) { + this.desc = desc; + } + + public OFPortStatus() { + super(); + this.type = OFType.PORT_STATUS; + this.length = U16.t(MINIMUM_LENGTH); + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.reason = data.readByte(); + data.readerIndex(data.readerIndex() + 7); // skip 7 bytes of padding + if (this.desc == null) + this.desc = new OFPhysicalPort(); + this.desc.readFrom(data); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeByte(this.reason); + for (int i = 0; i < 7; ++i) + data.writeByte((byte) 0); + this.desc.writeTo(data); + } + + @Override + public int hashCode() { + final int prime = 313; + int result = super.hashCode(); + result = prime * result + ((desc == null) ? 0 : desc.hashCode()); + result = prime * result + reason; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFPortStatus)) { + return false; + } + OFPortStatus other = (OFPortStatus) obj; + if (desc == null) { + if (other.desc != null) { + return false; + } + } else if (!desc.equals(other.desc)) { + return false; + } + if (reason != other.reason) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/OFQueueGetConfigReply.java b/src/main/java/org/openflow/protocol/OFQueueGetConfigReply.java new file mode 100644 index 0000000000000000000000000000000000000000..62be90d59f4106e8a6581dcf81bc5153cee83ce1 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFQueueGetConfigReply.java @@ -0,0 +1,125 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* 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.openflow.protocol; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents an ofp_queue_get_config_request message + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFQueueGetConfigReply extends OFMessage { + public static int MINIMUM_LENGTH = 16; + + protected short portNumber; + protected List<OFPacketQueue> queues = new ArrayList<OFPacketQueue>(); + + public OFQueueGetConfigReply() { + super(); + this.type = OFType.QUEUE_GET_CONFIG_REPLY; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + /** + * @return the port's queues + */ + public List<OFPacketQueue> getQueues() { + return queues; + } + + /** + * @param queues the queues to set + */ + public void setQueues(List<OFPacketQueue> queues) { + this.queues.clear(); + this.queues.addAll(queues); + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.portNumber = data.readShort(); + data.readInt(); // pad + data.readShort(); // pad + + int availLength = (this.length - MINIMUM_LENGTH); + this.queues.clear(); + + while (availLength > 0) { + OFPacketQueue queue = new OFPacketQueue(); + queue.readFrom(data); + queues.add(queue); + availLength -= queue.getLength(); + } + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.portNumber); + data.writeInt(0); // pad + data.writeShort(0); // pad + + for (OFPacketQueue queue : queues) { + queue.writeTo(data); + } + } + + @Override + public int hashCode() { + final int prime = 349; + int result = super.hashCode(); + result = prime * result + portNumber; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFQueueGetConfigReply)) { + return false; + } + OFQueueGetConfigReply other = (OFQueueGetConfigReply) obj; + if (portNumber != other.portNumber) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/OFQueueGetConfigRequest.java b/src/main/java/org/openflow/protocol/OFQueueGetConfigRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..cbb4a3734b9bc6510362afa237b6121398d9e4cd --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFQueueGetConfigRequest.java @@ -0,0 +1,95 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* 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.openflow.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * Represents an ofp_queue_get_config_request message + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFQueueGetConfigRequest extends OFMessage { + public static int MINIMUM_LENGTH = 12; + + protected short portNumber; + + public OFQueueGetConfigRequest(short portNumber) { + super(); + this.type = OFType.QUEUE_GET_CONFIG_REQUEST; + this.length = U16.t(MINIMUM_LENGTH); + this.portNumber = portNumber; + } + + public OFQueueGetConfigRequest() { + this((short) 0); + } + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.portNumber = data.readShort(); + data.readShort(); // pad + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.portNumber); + data.writeShort(0); // pad + } + + @Override + public int hashCode() { + final int prime = 347; + int result = super.hashCode(); + result = prime * result + portNumber; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFQueueGetConfigRequest)) { + return false; + } + OFQueueGetConfigRequest other = (OFQueueGetConfigRequest) obj; + if (portNumber != other.portNumber) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/OFQueueProp.java b/src/main/java/org/openflow/protocol/OFQueueProp.java new file mode 100644 index 0000000000000000000000000000000000000000..2e12224bbbf1a7baf86c4a61eac9c3ab32f72580 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFQueueProp.java @@ -0,0 +1,175 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* 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.openflow.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +public class OFQueueProp { + private int NONE_MINIMUM_LENGTH = 8; + private int RATE_MINIMUM_LENGTH = 16; + + public enum OFQueuePropType { + OFPQT_NONE (0), + OFPQT_MIN_RATE (1), + OFPQT_MAX_RATE (2); + + protected int value; + + private OFQueuePropType(int value) { + this.value = value; + } + + /** + * @return the value + */ + public int getValue() { + return value; + } + + public static OFQueuePropType fromShort(short x) { + switch (x) { + case 0: + return OFPQT_NONE; + case 1: + return OFPQT_MIN_RATE; + case 2: + return OFPQT_MAX_RATE; + } + return null; + } + } + + protected OFQueuePropType type; + protected short length; + protected short rate = -1; // not valid if type == OFPQT_NONE + + public OFQueueProp() { + this.type = OFQueuePropType.OFPQT_NONE; + this.length = U16.t(NONE_MINIMUM_LENGTH); + } + + /** + * @return the type + */ + public OFQueuePropType getType() { + return type; + } + + /** + * @param type the type to set + */ + public void setType(OFQueuePropType type) { + this.type = type; + + switch (type) { + case OFPQT_NONE: + this.length = U16.t(NONE_MINIMUM_LENGTH); + break; + case OFPQT_MIN_RATE: + this.length = U16.t(RATE_MINIMUM_LENGTH); + break; + case OFPQT_MAX_RATE: + this.length = U16.t(RATE_MINIMUM_LENGTH); + break; + } + } + + /** + * @return the rate + */ + public short getRate() { + return rate; + } + + /** + * @param rate the rate to set + */ + public void setRate(short rate) { + this.rate = rate; + } + + /** + * @return the length + */ + public short getLength() { + return length; + } + + public void readFrom(ChannelBuffer data) { + this.type = OFQueuePropType.fromShort(data.readShort()); + this.length = data.readShort(); + data.readInt(); // pad + + if (this.type == OFQueuePropType.OFPQT_MIN_RATE || + this.type == OFQueuePropType.OFPQT_MAX_RATE) { + assert(this.length == RATE_MINIMUM_LENGTH); + + this.rate = data.readShort(); + data.readInt(); // pad + data.readShort(); // pad + } else { + assert(this.length == NONE_MINIMUM_LENGTH); + } + } + + public void writeTo(ChannelBuffer data) { + data.writeShort(this.type.getValue()); + data.writeShort(this.length); + data.writeInt(0); // pad + + if (this.type == OFQueuePropType.OFPQT_MIN_RATE || + this.type == OFQueuePropType.OFPQT_MAX_RATE) { + data.writeShort(this.rate); + data.writeInt(0); // pad + data.writeShort(0); // pad + } + } + + @Override + public int hashCode() { + final int prime = 353; + int result = super.hashCode(); + result = prime * result + type.getValue(); + result = prime * result + rate; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFQueueProp)) { + return false; + } + OFQueueProp other = (OFQueueProp) obj; + if (type != other.type) { + return false; + } + if (type == OFQueuePropType.OFPQT_MIN_RATE || + type == OFQueuePropType.OFPQT_MAX_RATE) { + if (rate != other.rate) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/OFSetConfig.java b/src/main/java/org/openflow/protocol/OFSetConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..4b2356476e5b1b7af304e92d239ab14dd2a2e29b --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFSetConfig.java @@ -0,0 +1,29 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +/** + * Represents an OFPT_SET_CONFIG type message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFSetConfig extends OFSwitchConfig { + public OFSetConfig() { + super(); + this.type = OFType.SET_CONFIG; + } +} diff --git a/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java b/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java new file mode 100644 index 0000000000000000000000000000000000000000..e5a9c01e0093e29055bb75943909860490347e43 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java @@ -0,0 +1,180 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.factory.OFStatisticsFactory; +import org.openflow.protocol.factory.OFStatisticsFactoryAware; +import org.openflow.protocol.statistics.OFStatistics; +import org.openflow.protocol.statistics.OFStatisticsType; + + +/** + * Base class for statistics requests/replies + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 27, 2010 + */ +public abstract class OFStatisticsMessageBase extends OFMessage implements + OFStatisticsFactoryAware { + public static int MINIMUM_LENGTH = 12; + + protected OFStatisticsFactory statisticsFactory; + protected OFStatisticsType statisticType; + protected short flags; + + // TODO: this should be List<? extends OFStatistics>, to + // allow for type safe assignments of lists of specific message + protected List<? extends OFStatistics> statistics; + + /** + * @return the statisticType + */ + public OFStatisticsType getStatisticType() { + return statisticType; + } + + /** + * @param statisticType the statisticType to set + */ + public void setStatisticType(OFStatisticsType statisticType) { + this.statisticType = statisticType; + } + + /** + * @return the flags + */ + public short getFlags() { + return flags; + } + + /** + * @param flags the flags to set + */ + public void setFlags(short flags) { + this.flags = flags; + } + + /** + * @return the statistics + */ + public List<? extends OFStatistics> getStatistics() { + return statistics; + } + + /** + * return the first statistics request in the list of statistics, for + * statistics messages that expect exactly one message in their body (e.g., + * flow stats request, port statsrequest) + * + * @return the first and only element in the list of statistics + * @throw IllegalArgumentException if the list does not contain exactly one + * element + */ + public OFStatistics getFirstStatistics() { + if (statistics == null ) { + throw new IllegalArgumentException("Invariant violation: statistics message of type "+statisticType+" is null"); + } + if (statistics.size() != 1) { + throw new IllegalArgumentException("Invariant violation: statistics message of type "+statisticType+" contains "+statistics.size() +" statreq/reply messages in its body (should be 1)"); + } + + return statistics.get(0); + } + + /** + * @param statistics the statistics to set + */ + public void setStatistics(List<? extends OFStatistics> statistics) { + this.statistics = statistics; + } + + @Override + public void setStatisticsFactory(OFStatisticsFactory statisticsFactory) { + this.statisticsFactory = statisticsFactory; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.statisticType = OFStatisticsType.valueOf(data.readShort(), this + .getType()); + this.flags = data.readShort(); + if (this.statisticsFactory == null) + throw new RuntimeException("OFStatisticsFactory not set"); + this.statistics = statisticsFactory.parseStatistics(this.getType(), + this.statisticType, data, super.getLengthU() - MINIMUM_LENGTH); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.statisticType.getTypeValue()); + data.writeShort(this.flags); + if (this.statistics != null) { + for (OFStatistics statistic : this.statistics) { + statistic.writeTo(data); + } + } + } + + @Override + public int hashCode() { + final int prime = 317; + int result = super.hashCode(); + result = prime * result + flags; + result = prime * result + + ((statisticType == null) ? 0 : statisticType.hashCode()); + result = prime * result + + ((statistics == null) ? 0 : statistics.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFStatisticsMessageBase)) { + return false; + } + OFStatisticsMessageBase other = (OFStatisticsMessageBase) obj; + if (flags != other.flags) { + return false; + } + if (statisticType == null) { + if (other.statisticType != null) { + return false; + } + } else if (!statisticType.equals(other.statisticType)) { + return false; + } + if (statistics == null) { + if (other.statistics != null) { + return false; + } + } else if (!statistics.equals(other.statistics)) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/OFStatisticsReply.java b/src/main/java/org/openflow/protocol/OFStatisticsReply.java new file mode 100644 index 0000000000000000000000000000000000000000..ddc7267425ab98431c350d393253623c894bed4f --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFStatisticsReply.java @@ -0,0 +1,46 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import org.openflow.util.U16; + +/** + * Represents an ofp_stats_reply message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFStatisticsReply extends OFStatisticsMessageBase { + public enum OFStatisticsReplyFlags { + REPLY_MORE (1 << 0); + + protected short type; + + OFStatisticsReplyFlags(int type) { + this.type = (short) type; + } + + public short getTypeValue() { + return type; + } + } + + public OFStatisticsReply() { + super(); + this.type = OFType.STATS_REPLY; + this.length = U16.t(OFStatisticsMessageBase.MINIMUM_LENGTH); + } +} diff --git a/src/main/java/org/openflow/protocol/OFStatisticsRequest.java b/src/main/java/org/openflow/protocol/OFStatisticsRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..d1d8010e921a5f7918ac3869af387e2b12185016 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFStatisticsRequest.java @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import org.openflow.util.U16; + +/** + * Represents an ofp_stats_request message + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFStatisticsRequest extends OFStatisticsMessageBase { + public OFStatisticsRequest() { + super(); + this.type = OFType.STATS_REQUEST; + this.length = U16.t(OFStatisticsMessageBase.MINIMUM_LENGTH); + } +} diff --git a/src/main/java/org/openflow/protocol/OFSwitchConfig.java b/src/main/java/org/openflow/protocol/OFSwitchConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..e04e3fa60fe65a1f85bdc767588e63f45990efc2 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFSwitchConfig.java @@ -0,0 +1,118 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Base class representing ofp_switch_config based messages + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public abstract class OFSwitchConfig extends OFMessage { + public static int MINIMUM_LENGTH = 12; + + public enum OFConfigFlags { + OFPC_FRAG_NORMAL, + OFPC_FRAG_DROP, + OFPC_FRAG_REASM, + OFPC_FRAG_MASK + } + + protected short flags; + protected short missSendLength; + + public OFSwitchConfig() { + super(); + super.setLengthU(MINIMUM_LENGTH); + } + + /** + * @return the flags + */ + public short getFlags() { + return flags; + } + + /** + * @param flags the flags to set + */ + public OFSwitchConfig setFlags(short flags) { + this.flags = flags; + return this; + } + + /** + * @return the missSendLength + */ + public short getMissSendLength() { + return missSendLength; + } + + /** + * @param missSendLength the missSendLength to set + */ + public OFSwitchConfig setMissSendLength(short missSendLength) { + this.missSendLength = missSendLength; + return this; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.flags = data.readShort(); + this.missSendLength = data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.flags); + data.writeShort(this.missSendLength); + } + + @Override + public int hashCode() { + final int prime = 331; + int result = super.hashCode(); + result = prime * result + flags; + result = prime * result + missSendLength; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFSwitchConfig)) { + return false; + } + OFSwitchConfig other = (OFSwitchConfig) obj; + if (flags != other.flags) { + return false; + } + if (missSendLength != other.missSendLength) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/OFType.java b/src/main/java/org/openflow/protocol/OFType.java new file mode 100644 index 0000000000000000000000000000000000000000..f1c81e2884b0f850efe420aa54c1fe810869442e --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFType.java @@ -0,0 +1,249 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.lang.reflect.Constructor; + +/** + * List of OpenFlow types and mappings to wire protocol value and derived + * classes + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * @author David Erickson (daviderickson@cs.stanford.edu) + * + */ +public enum OFType { + HELLO (0, OFHello.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFHello(); + }}), + ERROR (1, OFError.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFError(); + }}), + ECHO_REQUEST (2, OFEchoRequest.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFEchoRequest(); + }}), + ECHO_REPLY (3, OFEchoReply.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFEchoReply(); + }}), + VENDOR (4, OFVendor.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFVendor(); + }}), + FEATURES_REQUEST (5, OFFeaturesRequest.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFFeaturesRequest(); + }}), + FEATURES_REPLY (6, OFFeaturesReply.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFFeaturesReply(); + }}), + GET_CONFIG_REQUEST (7, OFGetConfigRequest.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFGetConfigRequest(); + }}), + GET_CONFIG_REPLY (8, OFGetConfigReply.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFGetConfigReply(); + }}), + SET_CONFIG (9, OFSetConfig.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFSetConfig(); + }}), + PACKET_IN (10, OFPacketIn.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFPacketIn(); + }}), + FLOW_REMOVED (11, OFFlowRemoved.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFFlowRemoved(); + }}), + PORT_STATUS (12, OFPortStatus.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFPortStatus(); + }}), + PACKET_OUT (13, OFPacketOut.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFPacketOut(); + }}), + FLOW_MOD (14, OFFlowMod.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFFlowMod(); + }}), + PORT_MOD (15, OFPortMod.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFPortMod(); + }}), + STATS_REQUEST (16, OFStatisticsRequest.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFStatisticsRequest(); + }}), + STATS_REPLY (17, OFStatisticsReply.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFStatisticsReply(); + }}), + BARRIER_REQUEST (18, OFBarrierRequest.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFBarrierRequest(); + }}), + BARRIER_REPLY (19, OFBarrierReply.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFBarrierReply(); + }}), + QUEUE_GET_CONFIG_REQUEST (20, OFQueueGetConfigRequest.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFQueueGetConfigRequest(); + }}), + QUEUE_GET_CONFIG_REPLY (21, OFQueueGetConfigReply.class, new Instantiable<OFMessage>() { + @Override + public OFMessage instantiate() { + return new OFQueueGetConfigReply(); + }}); + + static OFType[] mapping; + + protected Class<? extends OFMessage> clazz; + protected Constructor<? extends OFMessage> constructor; + protected Instantiable<OFMessage> instantiable; + protected byte type; + + /** + * Store some information about the OpenFlow type, including wire protocol + * type number, length, and derived class + * + * @param type Wire protocol number associated with this OFType + * @param clazz The Java class corresponding to this type of OpenFlow + * message + * @param instantiator An Instantiator<OFMessage> implementation that creates an + * instance of the specified OFMessage + */ + OFType(int type, Class<? extends OFMessage> clazz, Instantiable<OFMessage> instantiator) { + this.type = (byte) type; + this.clazz = clazz; + this.instantiable = instantiator; + try { + this.constructor = clazz.getConstructor(new Class[]{}); + } catch (Exception e) { + throw new RuntimeException( + "Failure getting constructor for class: " + clazz, e); + } + OFType.addMapping(this.type, this); + } + + /** + * Adds a mapping from type value to OFType enum + * + * @param i OpenFlow wire protocol type + * @param t type + */ + static public void addMapping(byte i, OFType t) { + if (mapping == null) + mapping = new OFType[32]; + OFType.mapping[i] = t; + } + + /** + * Remove a mapping from type value to OFType enum + * + * @param i OpenFlow wire protocol type + */ + static public void removeMapping(byte i) { + OFType.mapping[i] = null; + } + + /** + * Given a wire protocol OpenFlow type number, return the OFType associated + * with it + * + * @param i wire protocol number + * @return OFType enum type + */ + + static public OFType valueOf(Byte i) { + return OFType.mapping[i]; + } + + /** + * @return Returns the wire protocol value corresponding to this OFType + */ + public byte getTypeValue() { + return this.type; + } + + /** + * @return return the OFMessage subclass corresponding to this OFType + */ + public Class<? extends OFMessage> toClass() { + return clazz; + } + + /** + * Returns the no-argument Constructor of the implementation class for + * this OFType + * @return the constructor + */ + public Constructor<? extends OFMessage> getConstructor() { + return constructor; + } + + /** + * Returns a new instance of the OFMessage represented by this OFType + * @return the new object + */ + public OFMessage newInstance() { + return instantiable.instantiate(); + } + + /** + * @return the instantiable + */ + public Instantiable<OFMessage> getInstantiable() { + return instantiable; + } + + /** + * @param instantiable the instantiable to set + */ + public void setInstantiable(Instantiable<OFMessage> instantiable) { + this.instantiable = instantiable; + } +} diff --git a/src/main/java/org/openflow/protocol/OFVendor.java b/src/main/java/org/openflow/protocol/OFVendor.java new file mode 100644 index 0000000000000000000000000000000000000000..8ecb862becf1a2ba52d4c0ddabeb8474a5c58ef5 --- /dev/null +++ b/src/main/java/org/openflow/protocol/OFVendor.java @@ -0,0 +1,131 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; +import org.openflow.protocol.factory.OFVendorDataFactory; +import org.openflow.protocol.factory.OFVendorDataFactoryAware; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Represents ofp_vendor_header + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFVendor extends OFMessage implements OFVendorDataFactoryAware { + public static int MINIMUM_LENGTH = 12; + + protected int vendor; + protected OFVendorData vendorData; + protected OFVendorDataFactory vendorDataFactory; + + public OFVendor() { + super(); + this.type = OFType.VENDOR; + this.length = U16.t(MINIMUM_LENGTH); + } + + /** + * @return the vendor + */ + public int getVendor() { + return vendor; + } + + /** + * @param vendor the vendor to set + */ + public void setVendor(int vendor) { + this.vendor = vendor; + } + + /** + * @return the data + */ + public OFVendorData getVendorData() { + return vendorData; + } + + /** + * @param data the data to set + */ + public void setVendorData(OFVendorData vendorData) { + this.vendorData = vendorData; + } + + @Override + public void setVendorDataFactory(OFVendorDataFactory vendorDataFactory) { + this.vendorDataFactory = vendorDataFactory; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.vendor = data.readInt(); + if (vendorDataFactory == null) + throw new RuntimeException("OFVendorDataFactory not set"); + + this.vendorData = vendorDataFactory.parseVendorData(vendor, + data, super.getLengthU() - MINIMUM_LENGTH); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(this.vendor); + if (vendorData != null) + vendorData.writeTo(data); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 337; + int result = super.hashCode(); + result = prime * result + vendor; + if (vendorData != null) + result = prime * result + vendorData.hashCode(); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + OFVendor other = (OFVendor) obj; + if (vendor != other.vendor) + return false; + if (vendorData == null) { + if (other.vendorData != null) { + return false; + } + } else if (!vendorData.equals(other.vendorData)) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/Wildcards.java b/src/main/java/org/openflow/protocol/Wildcards.java new file mode 100644 index 0000000000000000000000000000000000000000..4b0ea07b5146e45c8ab69102487437ee7ff24820 --- /dev/null +++ b/src/main/java/org/openflow/protocol/Wildcards.java @@ -0,0 +1,603 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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.openflow.protocol; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; + +import com.google.common.base.Joiner; + +/** + * a more user friendly representation of the wildcards bits in an OpenFlow + * match. The Wildcards object is + * <ul> + * <li>immutable (i.e., threadsafe)</li> + * <li>instance managed (don't instantiate it yourself), instead call "of"</li> + * <ul> + * <p> + * You can construct a Wildcard object from either its integer representation + * </p> + * <code> + * Wildcard.of(0x3820e0); + * </code> + * <p> + * Or start with either an empty or full wildcard, and select/unselect foo. + * </p> + * <code> + * Wildcard w = Wildcards.NONE + * .set(Flag.DL_SRC, Flag. DL_DST, Flag.DL_VLAN_PCP) + * .setNwDstMask(8) + * .setNwSrcMask(8); + * </code> + * <p> + * <b>Remember:</b> Wildcards objects are immutable. set... operations have + * <b>NO EFFECT</b> on the current wildcard object. You HAVE to use the returned + * changed object. + * </p> + * + * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> + */ +public class Wildcards { + + public final static Wildcards FULL = new Wildcards(OFMatch.OFPFW_ALL_SANITIZED); + private static final int FULL_INT = FULL.getInt(); + + public final static Wildcards EXACT = new Wildcards(0); + + // floodlight common case: matches on inport + l2 + public final static int INT_INPORT_L2_MATCH = 0x3820e0; + public final static Wildcards INPORT_L2_MATCH = new Wildcards( + INT_INPORT_L2_MATCH); + + /** + * enum type for the binary flags that can be set in the wildcards field of + * an OFMatch. Replaces the unwieldy c-ish int constants in OFMatch. + */ + public static enum Flag { + IN_PORT(OFMatch.OFPFW_IN_PORT), /* Switch input port. */ + DL_VLAN(OFMatch.OFPFW_DL_VLAN), /* VLAN id. */ + DL_SRC(OFMatch.OFPFW_DL_SRC), /* Ethernet source address. */ + DL_DST(OFMatch.OFPFW_DL_DST), /* Ethernet destination addr */ + DL_TYPE(OFMatch.OFPFW_DL_TYPE), /* Ethernet frame type. */ + NW_PROTO(OFMatch.OFPFW_NW_PROTO), /* IP protocol. */ + TP_SRC(OFMatch.OFPFW_TP_SRC), /* TCP/UDP source port. */ + TP_DST(OFMatch.OFPFW_TP_DST), /* TCP/UDP destination port. */ + DL_VLAN_PCP(OFMatch.OFPFW_DL_VLAN_PCP), /* VLAN priority. */ + NW_SRC(-1) { /* + * virtual NW_SRC flag => translates to the strange 6 bits + * in the header + */ + @Override + boolean isBolean() { + return false; + } + + @Override + int getInt(int flags) { + return ((flags & OFMatch.OFPFW_NW_SRC_MASK) >> OFMatch.OFPFW_NW_SRC_SHIFT); + } + + @Override + int setInt(int flags, int srcMask) { + return (flags & ~OFMatch.OFPFW_NW_SRC_MASK) | (srcMask << OFMatch.OFPFW_NW_SRC_SHIFT); + } + + @Override + int wildcard(int flags) { + return flags & ~OFMatch.OFPFW_NW_SRC_MASK; + } + + @Override + int matchOn(int flags) { + return flags | OFMatch.OFPFW_NW_SRC_ALL; + } + + @Override + boolean isPartiallyOn(int flags) { + int intValue = getInt(flags); + return intValue > 0 && intValue < 32; + } + + @Override + boolean isFullyOn(int flags) { + return getInt(flags) >= 32; + } + + }, + NW_DST(-1) { /* + * virtual NW_SRC flag => translates to the strange 6 bits + * in the header + */ + @Override + boolean isBolean() { + return false; + } + + @Override + int getInt(int flags) { + return ((flags & OFMatch.OFPFW_NW_DST_MASK) >> OFMatch.OFPFW_NW_DST_SHIFT); + } + + @Override + int setInt(int flags, int srcMask) { + return (flags & ~OFMatch.OFPFW_NW_DST_MASK) | (srcMask << OFMatch.OFPFW_NW_DST_SHIFT); + } + + @Override + int wildcard(int flags) { + return flags & ~OFMatch.OFPFW_NW_DST_MASK; + } + + @Override + int matchOn(int flags) { + return flags | OFMatch.OFPFW_NW_DST_ALL; + } + + @Override + boolean isFullyOn(int flags) { + return getInt(flags) >= 32; + } + }, + NW_TOS(OFMatch.OFPFW_NW_TOS); /* IP ToS (DSCP field, 6 bits). */ + + final int bitPosition; + + Flag(int bitPosition) { + this.bitPosition = bitPosition; + } + + /** + * @return a modified OF-1.0 flags field with this flag cleared (match + * on this field) + */ + int matchOn(int flags) { + return flags & ~this.bitPosition; + } + + /** + * @return a modified OF-1.0 flags field with this flag set (wildcard + * this field) + */ + int wildcard(int flags) { + return flags | this.bitPosition; + } + + /** + * @return true iff this is a true boolean flag that can either be off + * or on.True in OF-1.0 for all fields except NW_SRC and NW_DST + */ + boolean isBolean() { + return false; + } + + /** + * @return true iff this wildcard field is currently 'partially on'. + * Always false for true Boolean Flags. Can be true in OF-1.0 + * for NW_SRC, NW_DST. + */ + boolean isPartiallyOn(int flags) { + return false; + } + + /** + * @return true iff this wildcard field currently fully on (fully + * wildcarded). Equivalent to the boolean flag being set in the + * bitmask for bit flags, and to the wildcarded bit length set + * to >=32 for NW_SRC and NW_DST + * @param flags + * @return + */ + boolean isFullyOn(int flags) { + return (flags & this.bitPosition) != 0; + } + + /** + * set the integer representation of this flag. only for NW_SRC and + * NW_DST + */ + int setInt(int flags, int srcMask) { + throw new UnsupportedOperationException(); + } + + /** + * set the integer representation of this flag. only for NW_SRC and + * NW_DST + */ + int getInt(int flags) { + throw new UnsupportedOperationException(); + } + + + } + + private final int flags; + + /** private constructor. use Wildcard.of() instead */ + private Wildcards(int flags) { + this.flags = flags; + } + + /** + * return a wildcard object matching the given int flags. May reuse / cache + * frequently used wildcard instances. Don't rely on it though (use equals + * not ==). + * + * @param flags + * @return + */ + public static Wildcards of(int paramFlags) { + int flags = sanitizeInt(paramFlags); + switch(flags) { + case 0x0000: + return EXACT; + case OFMatch.OFPFW_ALL_SANITIZED: + return FULL; + case INT_INPORT_L2_MATCH: + return INPORT_L2_MATCH; + default: + return new Wildcards(flags); + } + } + + /** convience method return a wildcard for exactly one set flag */ + public static Wildcards of(Wildcards.Flag setFlag) { + return Wildcards.of(setFlag.wildcard(0)); + } + + /** convience method return a wildcard for exactly two set flags */ + public static Wildcards of(Wildcards.Flag setFlag, Wildcards.Flag setFlag2) { + return Wildcards.of(setFlag.wildcard(setFlag2.wildcard(0))); + } + + /** convience method return a wildcard for an arbitrary number of set flags */ + public static Wildcards of(Wildcards.Flag... setFlags) { + int flags = 0; + for (Wildcards.Flag flag : setFlags) + flags = flag.wildcard(0); + return Wildcards.of(flags); + } + + /** convience method return a wildcards for ofmatches that match on one flag */ + public static Wildcards ofMatches(Wildcards.Flag setFlag) { + return Wildcards.of(setFlag.matchOn(FULL_INT)); + } + + /** + * convience method return a wildcard for for an ofmatch that match on two + * flags + */ + public static Wildcards ofMatches(Wildcards.Flag setFlag, Wildcards.Flag setFlag2) { + return Wildcards.of(setFlag.matchOn(setFlag2.matchOn(FULL_INT))); + } + + /** + * convience method return a wildcard for an ofmatch that amtch on an + * arbitrary number of set flags + */ + public static Wildcards ofMatches(Wildcards.Flag... setFlags) { + int flags = FULL_INT; + for (Wildcards.Flag flag : setFlags) + flags = flag.matchOn(flags); + return Wildcards.of(flags); + } + + /** + * return a Wildcards object that has the given flags set + * <p> + * <b>NOTE:</b> NOT a mutator function. 'this' wildcard object stays + * unmodified. </b> + */ + public Wildcards wildcard(Wildcards.Flag flag) { + int flags = flag.wildcard(this.flags); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcards object that has the given flags set + * <p> + * <b>NOTE:</b> NOT a mutator function. 'this' wildcard object stays + * unmodified. </b> + */ + public Wildcards wildcard(Wildcards.Flag flag, Wildcards.Flag flag2) { + int flags = flag.wildcard(flag2.wildcard(this.flags)); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcards object that has the given flags wildcarded + * <p> + * <b>NOTE:</b> NOT a mutator function. 'this' wildcard object stays + * unmodified. </b> + */ + public Wildcards wildcard(Wildcards.Flag... setFlags) { + int flags = this.flags; + for (Wildcards.Flag flag : setFlags) + flags = flag.wildcard(flags); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcards object that matches on exactly the given flag + * <p> + * <b>NOTE:</b> NOT a mutator function. 'this' wildcard object stays + * unmodified. </b> + */ + public Wildcards matchOn(Wildcards.Flag flag) { + int flags = flag.matchOn(this.flags); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcards object that matches on exactly the given flags + * <p> + * <b>NOTE:</b> NOT a mutator function. 'this' wildcard object stays + * unmodified. </b> + */ + public Wildcards matchOn(Wildcards.Flag flag, Wildcards.Flag flag2) { + int flags = flag.matchOn(flag2.matchOn(this.flags)); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcards object that matches on exactly the given flags + * <p> + * <b>NOTE:</b> NOT a mutator function. 'this' wildcard object stays + * unmodified. </b> + */ + public Wildcards matchOn(Wildcards.Flag... setFlags) { + int flags = this.flags; + for (Wildcards.Flag flag : setFlags) + flags = flag.matchOn(flags); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return the nw src mask in normal CIDR style, e.g., 8 means x.x.x.x/8 + * means 8 bits wildcarded + */ + public int getNwSrcMask() { + return Math.max(0, 32 - Flag.NW_SRC.getInt(flags)); + } + + /** + * return the nw dst mask in normal CIDR style, e.g., 8 means x.x.x.x/8 + * means 8 bits wildcarded + */ + public int getNwDstMask() { + return Math.max(0, 32 - Flag.NW_DST.getInt(flags)); + } + + /** + * return a Wildcard object that has the given nwSrcCidrMask set. + * <b>NOTE:</b> NOT a mutator function. 'this' wildcard object stays + * unmodified. </b> + * + * @param srcCidrMask + * source mask to set in <b>normal CIDR notation</b>, i.e., 8 + * means x.x.x.x/8 + * @return a modified object + */ + public Wildcards withNwSrcMask(int srcCidrMask) { + int flags = Flag.NW_SRC.setInt(this.flags, Math.max(0, 32 - srcCidrMask)); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcard object that has the given nwDstCidrMask set. + * <b>NOTE:</b> NOT a mutator function. 'this' wildcard object stays + * unmodified. </b> + * + * @param dstCidrMask + * dest mask to set in <b>normal CIDR notation</b>, i.e., 8 means + * x.x.x.x/8 + * @return a modified object + */ + public Wildcards withNwDstMask(int dstCidrMask) { + int flags = Flag.NW_DST.setInt(this.flags, Math.max(0, 32 - dstCidrMask)); + if (flags == this.flags) + return this; + else + return new Wildcards(flags); + } + + /** + * return a Wildcard object that is inverted to this wildcard object. + * <b>NOTE:</b> NOT a mutator function. 'this' wildcard object stays + * unmodified. </b> + * @return a modified object + */ + public Wildcards inverted() { + return Wildcards.of(flags ^ OFMatch.OFPFW_ALL_SANITIZED); + } + + public boolean isWildcarded(Flag flag) { + return flag.isFullyOn(flags); + } + + /** + * return all wildcard flags that are fully wildcarded as an EnumSet. Do not + * modify. Note: some flags (like NW_SRC and NW_DST) that are partially + * wildcarded are not returned in this set. + * + * @return the EnumSet of wildcards + */ + public EnumSet<Wildcards.Flag> getWildcardedFlags() { + EnumSet<Wildcards.Flag> res = EnumSet.noneOf(Wildcards.Flag.class); + for (Wildcards.Flag flag : Flag.values()) { + if (flag.isFullyOn(flags)) { + res.add(flag); + } + } + return res; + } + + /** return the OpenFlow 'wire' integer representation of these wildcards */ + public int getInt() { + return flags; + } + + /** + * return the OpenFlow 'wire' integer representation of these wildcards. + * Sanitize nw_src and nw_dst to be max. 32 (values > 32 are technically + * possible, but don't make semantic sense) + */ + public static int sanitizeInt(int flags) { + if (((flags & OFMatch.OFPFW_NW_SRC_MASK) >> OFMatch.OFPFW_NW_SRC_SHIFT) > 32) { + flags = (flags & ~OFMatch.OFPFW_NW_SRC_MASK) | OFMatch.OFPFW_NW_SRC_ALL; + } + if (((flags & OFMatch.OFPFW_NW_DST_MASK) >> OFMatch.OFPFW_NW_DST_SHIFT) > 32) { + flags = (flags & ~OFMatch.OFPFW_NW_DST_MASK) | OFMatch.OFPFW_NW_DST_ALL; + } + return flags; + } + + /** + * is this a wildcard set that has all flags set + and full (/0) nw_src and + * nw_dst wildcarding ? + */ + public boolean isFull() { + return flags == OFMatch.OFPFW_ALL || flags == OFMatch.OFPFW_ALL_SANITIZED; + } + + /** is this a wildcard of an exact match */ + public boolean isExact() { + return flags == 0; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + flags; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Wildcards other = (Wildcards) obj; + if (flags != other.flags) + return false; + return true; + } + + private final static Joiner pipeJoiner = Joiner.on("|"); + + @Override + public String toString() { + List<String> res = new ArrayList<String>(); + for (Wildcards.Flag flag : Flag.values()) { + if (flag.isFullyOn(flags)) { + res.add(flag.name().toLowerCase()); + } + } + + if (Flag.NW_SRC.isPartiallyOn(flags)) { + res.add("nw_src(/" + getNwSrcMask() + ")"); + } + + if (Flag.NW_DST.isPartiallyOn(flags)) { + res.add("nw_dst(/" + getNwDstMask() + ")"); + } + + return pipeJoiner.join(res); + } + + private final static Joiner commaJoiner = Joiner.on(", "); + + /** a Java expression that constructs 'this' wildcards set */ + public String toJava() { + if(isFull()) { + return "Wildcards.FULL"; + } else if (isExact()){ + return "Wildcards.EXACT"; + } + + StringBuilder b = new StringBuilder(); + + EnumSet<Flag> myFlags = getWildcardedFlags(); + if (myFlags.size() < 3) { + // default to start with empty + b.append("Wildcards.of(" + + commaJoiner.join(prefix("Flag.", myFlags.iterator())) + ")"); + } else { + // too many - start with full + + EnumSet<Flag> invFlags = inverted().getWildcardedFlags(); + b.append("Wildcards.ofMatches(" + + commaJoiner.join(prefix("Flag.", invFlags.iterator())) + ")"); + } + if (Flag.NW_SRC.isPartiallyOn(flags)) { + b.append(".setNwSrcMask(" + getNwSrcMask() + ")"); + } + if (Flag.NW_DST.isPartiallyOn(flags)) { + b.append(".setNwDstMask(" + getNwDstMask() + ")"); + } + return b.toString(); + } + + private Iterator<String> prefix(final String prefix, final Iterator<?> i) { + return new Iterator<String>() { + + @Override + public boolean hasNext() { + return i.hasNext(); + } + + @Override + public String next() { + Object next = i.next(); + return next == null ? null : prefix + next.toString(); + } + + @Override + public void remove() { + i.remove(); + } + }; + } + + +} \ No newline at end of file diff --git a/src/main/java/org/openflow/protocol/action/OFAction.java b/src/main/java/org/openflow/protocol/action/OFAction.java new file mode 100644 index 0000000000000000000000000000000000000000..57b5dc1efe690532e373d7663cfd6bcb4b5dcff9 --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFAction.java @@ -0,0 +1,173 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.U16; + +/** + * The base class for all OpenFlow Actions. + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public class OFAction implements Cloneable { + /** + * Note the true minimum length for this header is 8 including a pad to 64 + * bit alignment, however as this base class is used for demuxing an + * incoming Action, it is only necessary to read the first 4 bytes. All + * Actions extending this class are responsible for reading/writing the + * first 8 bytes, including the pad if necessary. + */ + public static int MINIMUM_LENGTH = 4; + public static int OFFSET_LENGTH = 2; + public static int OFFSET_TYPE = 0; + + protected OFActionType type; + protected short length; + + /** + * Get the length of this message + * + * @return + */ + public short getLength() { + return length; + } + + /** + * Get the length of this message, unsigned + * + * @return + */ + public int getLengthU() { + return U16.f(length); + } + + /** + * Set the length of this message + * + * @param length + */ + public OFAction setLength(short length) { + this.length = length; + return this; + } + + /** + * Get the type of this message + * + * @return OFActionType enum + */ + public OFActionType getType() { + return this.type; + } + + /** + * Set the type of this message + * + * @param type + */ + public void setType(OFActionType type) { + this.type = type; + } + + /** + * Returns a summary of the message + * @return "ofmsg=v=$version;t=$type:l=$len:xid=$xid" + */ + public String toString() { + return "ofaction" + + ";t=" + this.getType() + + ";l=" + this.getLength(); + } + + /** + * Given the output from toString(), + * create a new OFAction + * @param val + * @return + */ + public static OFAction fromString(String val) { + String tokens[] = val.split(";"); + if (!tokens[0].equals("ofaction")) + throw new IllegalArgumentException("expected 'ofaction' but got '" + + tokens[0] + "'"); + String type_tokens[] = tokens[1].split("="); + String len_tokens[] = tokens[2].split("="); + OFAction action = new OFAction(); + action.setLength(Short.valueOf(len_tokens[1])); + action.setType(OFActionType.valueOf(type_tokens[1])); + return action; + } + + public void readFrom(ChannelBuffer data) { + this.type = OFActionType.valueOf(data.readShort()); + this.length = data.readShort(); + // Note missing PAD, see MINIMUM_LENGTH comment for details + } + + public void writeTo(ChannelBuffer data) { + data.writeShort(type.getTypeValue()); + data.writeShort(length); + // Note missing PAD, see MINIMUM_LENGTH comment for details + } + + @Override + public int hashCode() { + final int prime = 347; + int result = 1; + result = prime * result + length; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFAction)) { + return false; + } + OFAction other = (OFAction) obj; + if (length != other.length) { + return false; + } + if (type == null) { + if (other.type != null) { + return false; + } + } else if (!type.equals(other.type)) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#clone() + */ + @Override + public OFAction clone() throws CloneNotSupportedException { + return (OFAction) super.clone(); + } + +} diff --git a/src/main/java/org/openflow/protocol/action/OFActionDataLayer.java b/src/main/java/org/openflow/protocol/action/OFActionDataLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..644dc5f2fb53d020f8d8c002d83e0df6e017b8bd --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionDataLayer.java @@ -0,0 +1,109 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + +import java.util.Arrays; + +import net.floodlightcontroller.core.web.serializers.ByteArrayMACSerializer; +import net.floodlightcontroller.util.MACAddress; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFPhysicalPort; + +/** + * Represents an ofp_action_dl_addr + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public abstract class OFActionDataLayer extends OFAction { + public static int MINIMUM_LENGTH = 16; + + protected byte[] dataLayerAddress; + + /** + * @return the dataLayerAddress + */ + @JsonSerialize(using=ByteArrayMACSerializer.class) + public byte[] getDataLayerAddress() { + return dataLayerAddress; + } + + /** + * @param dataLayerAddress the dataLayerAddress to set + */ + public void setDataLayerAddress(byte[] dataLayerAddress) { + this.dataLayerAddress = dataLayerAddress; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + if (this.dataLayerAddress == null) + this.dataLayerAddress = new byte[OFPhysicalPort.OFP_ETH_ALEN]; + data.readBytes(this.dataLayerAddress); + data.readInt(); + data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeBytes(this.dataLayerAddress); + data.writeInt(0); + data.writeShort((short) 0); + } + + @Override + public int hashCode() { + final int prime = 347; + int result = super.hashCode(); + result = prime * result + Arrays.hashCode(dataLayerAddress); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionDataLayer)) { + return false; + } + OFActionDataLayer other = (OFActionDataLayer) obj; + if (!Arrays.equals(dataLayerAddress, other.dataLayerAddress)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(type); + builder.append("["); + builder.append(MACAddress.valueOf(dataLayerAddress).toString()); + builder.append("]"); + return builder.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/openflow/protocol/action/OFActionDataLayerDestination.java b/src/main/java/org/openflow/protocol/action/OFActionDataLayerDestination.java new file mode 100644 index 0000000000000000000000000000000000000000..48b8d0f84c4f0a7247d6949d8623cca45181692b --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionDataLayerDestination.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.action; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFActionDataLayerDestination extends OFActionDataLayer { + public OFActionDataLayerDestination() { + super(); + super.setType(OFActionType.SET_DL_DST); + super.setLength((short) OFActionDataLayer.MINIMUM_LENGTH); + } + + public OFActionDataLayerDestination(byte[] address) { + this(); + this.dataLayerAddress = address; + } +} diff --git a/src/main/java/org/openflow/protocol/action/OFActionDataLayerSource.java b/src/main/java/org/openflow/protocol/action/OFActionDataLayerSource.java new file mode 100644 index 0000000000000000000000000000000000000000..e04561ceefdfb052386b8cf0040b90f84ec2a2ef --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionDataLayerSource.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.action; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFActionDataLayerSource extends OFActionDataLayer { + public OFActionDataLayerSource() { + super(); + super.setType(OFActionType.SET_DL_SRC); + super.setLength((short) OFActionDataLayer.MINIMUM_LENGTH); + } + + public OFActionDataLayerSource(byte[] address) { + this(); + this.dataLayerAddress = address; + } +} diff --git a/src/main/java/org/openflow/protocol/action/OFActionEnqueue.java b/src/main/java/org/openflow/protocol/action/OFActionEnqueue.java new file mode 100644 index 0000000000000000000000000000000000000000..537716612ab86bd06e513dfd5f7217fd802ad60e --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionEnqueue.java @@ -0,0 +1,136 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_action_enqueue + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public class OFActionEnqueue extends OFAction { + public static int MINIMUM_LENGTH = 16; + + protected short port; + protected int queueId; + + public OFActionEnqueue() { + super.setType(OFActionType.OPAQUE_ENQUEUE); + super.setLength((short) MINIMUM_LENGTH); + } + + public OFActionEnqueue(short port, int queueId) { + this(); + this.port = port; + this.queueId = queueId; + } + + /** + * Get the output port + * @return + */ + public short getPort() { + return this.port; + } + + /** + * Set the output port + * @param port + */ + public void setPort(short port) { + this.port = port; + } + + /** + * @return the queueId + */ + public int getQueueId() { + return queueId; + } + + /** + * @param queueId the queueId to set + */ + public void setQueueId(int queueId) { + this.queueId = queueId; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.port = data.readShort(); + data.readShort(); + data.readInt(); + this.queueId = data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.port); + data.writeShort((short) 0); + data.writeInt(0); + data.writeInt(this.queueId); + } + + @Override + public int hashCode() { + final int prime = 349; + int result = super.hashCode(); + result = prime * result + port; + result = prime * result + queueId; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionEnqueue)) { + return false; + } + OFActionEnqueue other = (OFActionEnqueue) obj; + if (port != other.port) { + return false; + } + if (queueId != other.queueId) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(type); + builder.append("["); + builder.append("Port: "); + builder.append(port); + builder.append(", Queue Id: "); + builder.append(queueId); + builder.append("]"); + return builder.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerAddress.java b/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerAddress.java new file mode 100644 index 0000000000000000000000000000000000000000..67bc5a82203fe5d821aabb19a2d352b6df0eabc6 --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerAddress.java @@ -0,0 +1,98 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import net.floodlightcontroller.packet.IPv4; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_action_nw_addr + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public abstract class OFActionNetworkLayerAddress extends OFAction { + public static int MINIMUM_LENGTH = 8; + + protected int networkAddress; + + /** + * @return the networkAddress + */ + public int getNetworkAddress() { + return networkAddress; + } + + /** + * @param networkAddress the networkAddress to set + */ + public void setNetworkAddress(int networkAddress) { + this.networkAddress = networkAddress; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.networkAddress = data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(this.networkAddress); + } + + @Override + public int hashCode() { + final int prime = 353; + int result = super.hashCode(); + result = prime * result + networkAddress; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionNetworkLayerAddress)) { + return false; + } + OFActionNetworkLayerAddress other = (OFActionNetworkLayerAddress) obj; + if (networkAddress != other.networkAddress) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(type); + builder.append("["); + builder.append(IPv4.fromIPv4Address(networkAddress)); + builder.append("]"); + return builder.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerDestination.java b/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerDestination.java new file mode 100644 index 0000000000000000000000000000000000000000..13c14ff0bfb9435c5c8e9156344583e575c04624 --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerDestination.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.action; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFActionNetworkLayerDestination extends OFActionNetworkLayerAddress { + public OFActionNetworkLayerDestination() { + super(); + super.setType(OFActionType.SET_NW_DST); + super.setLength((short) OFActionNetworkLayerAddress.MINIMUM_LENGTH); + } + + public OFActionNetworkLayerDestination(int ip) { + this(); + this.networkAddress = ip; + } +} diff --git a/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerSource.java b/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerSource.java new file mode 100644 index 0000000000000000000000000000000000000000..ef1d005e32f1f5b6aa92945a52f50df236413f74 --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionNetworkLayerSource.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.action; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFActionNetworkLayerSource extends OFActionNetworkLayerAddress { + public OFActionNetworkLayerSource() { + super(); + super.setType(OFActionType.SET_NW_SRC); + super.setLength((short) OFActionNetworkLayerAddress.MINIMUM_LENGTH); + } + + public OFActionNetworkLayerSource(int ip) { + this(); + this.networkAddress = ip; + } +} diff --git a/src/main/java/org/openflow/protocol/action/OFActionNetworkTypeOfService.java b/src/main/java/org/openflow/protocol/action/OFActionNetworkTypeOfService.java new file mode 100644 index 0000000000000000000000000000000000000000..ec91c764916beff70ee7bec9e8ba4e7a27f08645 --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionNetworkTypeOfService.java @@ -0,0 +1,110 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_action_enqueue + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public class OFActionNetworkTypeOfService extends OFAction { + public static int MINIMUM_LENGTH = 8; + + protected byte networkTypeOfService; + + public OFActionNetworkTypeOfService() { + super.setType(OFActionType.SET_NW_TOS); + super.setLength((short) MINIMUM_LENGTH); + } + + public OFActionNetworkTypeOfService(byte tos) { + this(); + this.networkTypeOfService = tos; + } + + + /** + * @return the networkTypeOfService + */ + public byte getNetworkTypeOfService() { + return networkTypeOfService; + } + + /** + * @param networkTypeOfService the networkTypeOfService to set + */ + public void setNetworkTypeOfService(byte networkTypeOfService) { + this.networkTypeOfService = networkTypeOfService; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.networkTypeOfService = data.readByte(); + data.readShort(); + data.readByte(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeByte(this.networkTypeOfService); + data.writeShort((short) 0); + data.writeByte((byte) 0); + } + + @Override + public int hashCode() { + final int prime = 359; + int result = super.hashCode(); + result = prime * result + networkTypeOfService; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionNetworkTypeOfService)) { + return false; + } + OFActionNetworkTypeOfService other = (OFActionNetworkTypeOfService) obj; + if (networkTypeOfService != other.networkTypeOfService) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(type); + builder.append("["); + builder.append(networkTypeOfService); + builder.append("]"); + return builder.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/openflow/protocol/action/OFActionOutput.java b/src/main/java/org/openflow/protocol/action/OFActionOutput.java new file mode 100644 index 0000000000000000000000000000000000000000..beca7e4f21f84a146f2b01a0a5585527c8fc67ef --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionOutput.java @@ -0,0 +1,161 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + * @author Rob Sherwood (rob.sherwood@stanford.edu) + */ +public class OFActionOutput extends OFAction implements Cloneable { + public static int MINIMUM_LENGTH = 8; + + protected short port; + protected short maxLength; + + public OFActionOutput() { + super.setType(OFActionType.OUTPUT); + super.setLength((short) MINIMUM_LENGTH); + } + + /** + * Create an Output Action sending packets out the specified + * OpenFlow port. + * + * This is the most common creation pattern for OFActions. + * + * @param port + */ + + public OFActionOutput(short port) { + this(port, (short) 65535); + } + + /** + * Create an Output Action specifying both the port AND + * the snaplen of the packet to send out that port. + * The length field is only meaningful when port == OFPort.OFPP_CONTROLLER + * @param port + * @param maxLength The maximum number of bytes of the packet to send. + * Most hardware only supports this value for OFPP_CONTROLLER + */ + + public OFActionOutput(short port, short maxLength) { + super(); + super.setType(OFActionType.OUTPUT); + super.setLength((short) MINIMUM_LENGTH); + this.port = port; + this.maxLength = maxLength; + } + + /** + * Get the output port + * @return + */ + public short getPort() { + return this.port; + } + + /** + * Set the output port + * @param port + */ + public OFActionOutput setPort(short port) { + this.port = port; + return this; + } + + /** + * Get the max length to send to the controller + * @return + */ + public short getMaxLength() { + return this.maxLength; + } + + /** + * Set the max length to send to the controller + * @param maxLength + */ + public OFActionOutput setMaxLength(short maxLength) { + this.maxLength = maxLength; + return this; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.port = data.readShort(); + this.maxLength = data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(port); + data.writeShort(maxLength); + } + + @Override + public int hashCode() { + final int prime = 367; + int result = super.hashCode(); + result = prime * result + maxLength; + result = prime * result + port; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionOutput)) { + return false; + } + OFActionOutput other = (OFActionOutput) obj; + if (maxLength != other.maxLength) { + return false; + } + if (port != other.port) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(type); + builder.append("["); + builder.append(port); + builder.append("]"); + return builder.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/openflow/protocol/action/OFActionStripVirtualLan.java b/src/main/java/org/openflow/protocol/action/OFActionStripVirtualLan.java new file mode 100644 index 0000000000000000000000000000000000000000..f79c84d391751abeb8e49606382a9e1325264f39 --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionStripVirtualLan.java @@ -0,0 +1,60 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + + +/** + * Represents an ofp_action_strip_vlan + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public class OFActionStripVirtualLan extends OFAction { + public static int MINIMUM_LENGTH = 8; + + public OFActionStripVirtualLan() { + super(); + super.setType(OFActionType.STRIP_VLAN); + super.setLength((short) MINIMUM_LENGTH); + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + // PAD + data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + // PAD + data.writeInt(0); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(type); + return builder.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/openflow/protocol/action/OFActionTransportLayer.java b/src/main/java/org/openflow/protocol/action/OFActionTransportLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..696c1c7a29d01132c2667e0cda9639b3dcc4eec9 --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionTransportLayer.java @@ -0,0 +1,98 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_action_tp_port + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public abstract class OFActionTransportLayer extends OFAction { + public static int MINIMUM_LENGTH = 8; + + protected short transportPort; + + /** + * @return the transportPort + */ + public short getTransportPort() { + return transportPort; + } + + /** + * @param transportPort the transportPort to set + */ + public void setTransportPort(short transportPort) { + this.transportPort = transportPort; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.transportPort = data.readShort(); + data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.transportPort); + data.writeShort((short) 0); + } + + @Override + public int hashCode() { + final int prime = 373; + int result = super.hashCode(); + result = prime * result + transportPort; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionTransportLayer)) { + return false; + } + OFActionTransportLayer other = (OFActionTransportLayer) obj; + if (transportPort != other.transportPort) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(type); + builder.append("["); + builder.append(transportPort); + builder.append("]"); + return builder.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/openflow/protocol/action/OFActionTransportLayerDestination.java b/src/main/java/org/openflow/protocol/action/OFActionTransportLayerDestination.java new file mode 100644 index 0000000000000000000000000000000000000000..7e7b0f1fc6539d306ed69548e1b24c33a0aa2d65 --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionTransportLayerDestination.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.action; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFActionTransportLayerDestination extends OFActionTransportLayer { + public OFActionTransportLayerDestination() { + super(); + super.setType(OFActionType.SET_TP_DST); + super.setLength((short) OFActionTransportLayer.MINIMUM_LENGTH); + } + + public OFActionTransportLayerDestination(short port) { + this(); + this.transportPort = port; + } +} diff --git a/src/main/java/org/openflow/protocol/action/OFActionTransportLayerSource.java b/src/main/java/org/openflow/protocol/action/OFActionTransportLayerSource.java new file mode 100644 index 0000000000000000000000000000000000000000..385aa53c88478f04f92644271342ee02c6e93d25 --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionTransportLayerSource.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.action; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFActionTransportLayerSource extends OFActionTransportLayer { + public OFActionTransportLayerSource() { + super(); + super.setType(OFActionType.SET_TP_SRC); + super.setLength((short) OFActionTransportLayer.MINIMUM_LENGTH); + } + + public OFActionTransportLayerSource(short port) { + this(); + this.transportPort = port; + } +} diff --git a/src/main/java/org/openflow/protocol/action/OFActionType.java b/src/main/java/org/openflow/protocol/action/OFActionType.java new file mode 100644 index 0000000000000000000000000000000000000000..18229170b77dc1a01984cfc9f156272f5ad858c5 --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionType.java @@ -0,0 +1,203 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.action; + +import java.lang.reflect.Constructor; + +import org.openflow.protocol.Instantiable; + +/** + * List of OpenFlow Action types and mappings to wire protocol value and + * derived classes + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public enum OFActionType { + OUTPUT (0, OFActionOutput.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionOutput(); + }}), + SET_VLAN_ID (1, OFActionVirtualLanIdentifier.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionVirtualLanIdentifier(); + }}), + SET_VLAN_PCP (2, OFActionVirtualLanPriorityCodePoint.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionVirtualLanPriorityCodePoint(); + }}), + STRIP_VLAN (3, OFActionStripVirtualLan.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionStripVirtualLan(); + }}), + SET_DL_SRC (4, OFActionDataLayerSource.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionDataLayerSource(); + }}), + SET_DL_DST (5, OFActionDataLayerDestination.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionDataLayerDestination(); + }}), + SET_NW_SRC (6, OFActionNetworkLayerSource.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionNetworkLayerSource(); + }}), + SET_NW_DST (7, OFActionNetworkLayerDestination.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionNetworkLayerDestination(); + }}), + SET_NW_TOS (8, OFActionNetworkTypeOfService.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionNetworkTypeOfService(); + }}), + SET_TP_SRC (9, OFActionTransportLayerSource.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionTransportLayerSource(); + }}), + SET_TP_DST (10, OFActionTransportLayerDestination.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionTransportLayerDestination(); + }}), + OPAQUE_ENQUEUE (11, OFActionEnqueue.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionEnqueue(); + }}), + VENDOR (0xffff, OFActionVendor.class, new Instantiable<OFAction>() { + @Override + public OFAction instantiate() { + return new OFActionVendorGeneric(); + }}); + + protected static OFActionType[] mapping; + + protected Class<? extends OFAction> clazz; + protected Constructor<? extends OFAction> constructor; + protected Instantiable<OFAction> instantiable; + protected int minLen; + protected short type; + + /** + * Store some information about the OpenFlow Action type, including wire + * protocol type number, length, and derrived class + * + * @param type Wire protocol number associated with this OFType + * @param clazz The Java class corresponding to this type of OpenFlow Action + * @param instantiable the instantiable for the OFAction this type represents + */ + OFActionType(int type, Class<? extends OFAction> clazz, Instantiable<OFAction> instantiable) { + this.type = (short) type; + this.clazz = clazz; + this.instantiable = instantiable; + try { + this.constructor = clazz.getConstructor(new Class[]{}); + } catch (Exception e) { + throw new RuntimeException( + "Failure getting constructor for class: " + clazz, e); + } + OFActionType.addMapping(this.type, this); + } + + /** + * Adds a mapping from type value to OFActionType enum + * + * @param i OpenFlow wire protocol Action type value + * @param t type + */ + static public void addMapping(short i, OFActionType t) { + if (mapping == null) + mapping = new OFActionType[16]; + // bring higher mappings down to the edge of our array + if (i < 0) + i = (short) (16 + i); + OFActionType.mapping[i] = t; + } + + /** + * Given a wire protocol OpenFlow type number, return the OFType associated + * with it + * + * @param i wire protocol number + * @return OFType enum type + */ + + static public OFActionType valueOf(short i) { + if (i < 0) + i = (short) (16+i); + return OFActionType.mapping[i]; + } + + /** + * @return Returns the wire protocol value corresponding to this + * OFActionType + */ + public short getTypeValue() { + return this.type; + } + + /** + * @return return the OFAction subclass corresponding to this OFActionType + */ + public Class<? extends OFAction> toClass() { + return clazz; + } + + /** + * Returns the no-argument Constructor of the implementation class for + * this OFActionType + * @return the constructor + */ + public Constructor<? extends OFAction> getConstructor() { + return constructor; + } + + /** + * Returns a new instance of the OFAction represented by this OFActionType + * @return the new object + */ + public OFAction newInstance() { + return instantiable.instantiate(); + } + + /** + * @return the instantiable + */ + public Instantiable<OFAction> getInstantiable() { + return instantiable; + } + + /** + * @param instantiable the instantiable to set + */ + public void setInstantiable(Instantiable<OFAction> instantiable) { + this.instantiable = instantiable; + } +} diff --git a/src/main/java/org/openflow/protocol/action/OFActionVendor.java b/src/main/java/org/openflow/protocol/action/OFActionVendor.java new file mode 100644 index 0000000000000000000000000000000000000000..5860ef1160ae43fe3606a582c8e7d7ae842820eb --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionVendor.java @@ -0,0 +1,94 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public abstract class OFActionVendor extends OFAction { + public static int MINIMUM_LENGTH = 8; + + protected int vendor; + + public OFActionVendor() { + super(); + super.setType(OFActionType.VENDOR); + super.setLength((short) MINIMUM_LENGTH); + } + + /** + * @return the vendor + */ + public int getVendor() { + return vendor; + } + + /** + * @param vendor the vendor to set + */ + public void setVendor(int vendor) { + this.vendor = vendor; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.vendor = data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(this.vendor); + } + + @Override + public int hashCode() { + final int prime = 379; + int result = super.hashCode(); + result = prime * result + vendor; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionVendor)) { + return false; + } + OFActionVendor other = (OFActionVendor) obj; + if (vendor != other.vendor) { + return false; + } + return true; + } + + @Override + public String toString() { + return super.toString() + "; vendor=" + vendor; + } +} diff --git a/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java b/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java new file mode 100644 index 0000000000000000000000000000000000000000..4f7859f5ec43e8e13ff8340c50f5cda6f2d2311c --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java @@ -0,0 +1,96 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.action; + + +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** A generic / unparsed vendor action. This action is returned by + * BasicFactory.readFromWire if no more specific OFVendorActionFactory + * is registered. + * + * @author David Erickson (daviderickson@cs.stanford.edu) + * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> + */ +public class OFActionVendorGeneric extends OFActionVendor { + public static int MINIMUM_LENGTH = 8; + + private final static byte[] EMPTY_ARRAY = new byte[0]; + + protected byte[] vendorData; + + public OFActionVendorGeneric() { + super(); + } + + public byte[] getVendorData() { + return vendorData; + } + + public void setVendorData(byte[] vendorData) { + this.vendorData = vendorData; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + + int vendorDataLength = this.getLength() - MINIMUM_LENGTH; + if (vendorDataLength > 0) { + vendorData = new byte[vendorDataLength]; + data.readBytes(vendorData); + } else { + vendorData = EMPTY_ARRAY; + } + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(this.vendor); + data.writeBytes(vendorData); + } + + @Override + public int hashCode() { + final int prime = 379; + int result = super.hashCode(); + result = prime * result + Arrays.hashCode(vendorData); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionVendorGeneric)) { + return false; + } + OFActionVendorGeneric other = (OFActionVendorGeneric) obj; + if (!Arrays.equals(vendorData, other.vendorData)) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/action/OFActionVirtualLanIdentifier.java b/src/main/java/org/openflow/protocol/action/OFActionVirtualLanIdentifier.java new file mode 100644 index 0000000000000000000000000000000000000000..5bd0e0bda156cc34ac915ee29900e8a83b5cfbd0 --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionVirtualLanIdentifier.java @@ -0,0 +1,98 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_action_vlan_vid + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public class OFActionVirtualLanIdentifier extends OFAction { + public static int MINIMUM_LENGTH = 8; + + protected short virtualLanIdentifier; + + public OFActionVirtualLanIdentifier() { + super.setType(OFActionType.SET_VLAN_ID); + super.setLength((short) MINIMUM_LENGTH); + } + + public OFActionVirtualLanIdentifier(short vlanId) { + this(); + this.virtualLanIdentifier = vlanId; + } + + /** + * @return the virtualLanIdentifier + */ + public short getVirtualLanIdentifier() { + return virtualLanIdentifier; + } + + /** + * @param virtualLanIdentifier the virtualLanIdentifier to set + */ + public void setVirtualLanIdentifier(short virtualLanIdentifier) { + this.virtualLanIdentifier = virtualLanIdentifier; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.virtualLanIdentifier = data.readShort(); + data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.virtualLanIdentifier); + data.writeShort((short) 0); + } + + @Override + public int hashCode() { + final int prime = 383; + int result = super.hashCode(); + result = prime * result + virtualLanIdentifier; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionVirtualLanIdentifier)) { + return false; + } + OFActionVirtualLanIdentifier other = (OFActionVirtualLanIdentifier) obj; + if (virtualLanIdentifier != other.virtualLanIdentifier) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/org/openflow/protocol/action/OFActionVirtualLanPriorityCodePoint.java b/src/main/java/org/openflow/protocol/action/OFActionVirtualLanPriorityCodePoint.java new file mode 100644 index 0000000000000000000000000000000000000000..9202df33f82548c74919d7b34be5714aa9036256 --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionVirtualLanPriorityCodePoint.java @@ -0,0 +1,100 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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. +**/ + +/** + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +package org.openflow.protocol.action; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_action_vlan_pcp + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public class OFActionVirtualLanPriorityCodePoint extends OFAction { + public static int MINIMUM_LENGTH = 8; + + protected byte virtualLanPriorityCodePoint; + + public OFActionVirtualLanPriorityCodePoint() { + super.setType(OFActionType.SET_VLAN_PCP); + super.setLength((short) MINIMUM_LENGTH); + } + + public OFActionVirtualLanPriorityCodePoint(byte priority) { + this(); + this.virtualLanPriorityCodePoint = priority; + } + + /** + * @return the virtualLanPriorityCodePoint + */ + public byte getVirtualLanPriorityCodePoint() { + return virtualLanPriorityCodePoint; + } + + /** + * @param virtualLanPriorityCodePoint the virtualLanPriorityCodePoint to set + */ + public void setVirtualLanPriorityCodePoint(byte virtualLanPriorityCodePoint) { + this.virtualLanPriorityCodePoint = virtualLanPriorityCodePoint; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.virtualLanPriorityCodePoint = data.readByte(); + data.readShort(); // pad + data.readByte(); // pad + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeByte(this.virtualLanPriorityCodePoint); + data.writeShort((short) 0); + data.writeByte((byte) 0); + } + + @Override + public int hashCode() { + final int prime = 389; + int result = super.hashCode(); + result = prime * result + virtualLanPriorityCodePoint; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionVirtualLanPriorityCodePoint)) { + return false; + } + OFActionVirtualLanPriorityCodePoint other = (OFActionVirtualLanPriorityCodePoint) obj; + if (virtualLanPriorityCodePoint != other.virtualLanPriorityCodePoint) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/org/openflow/protocol/factory/BasicFactory.java b/src/main/java/org/openflow/protocol/factory/BasicFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..f484a167f5cd64f1eaec92dd366a4f0ff02a8ece --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/BasicFactory.java @@ -0,0 +1,346 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.factory; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFType; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionType; +import org.openflow.protocol.action.OFActionVendor; +import org.openflow.protocol.statistics.OFStatistics; +import org.openflow.protocol.statistics.OFStatisticsType; +import org.openflow.protocol.statistics.OFVendorStatistics; +import org.openflow.protocol.vendor.OFByteArrayVendorData; +import org.openflow.protocol.vendor.OFVendorData; +import org.openflow.protocol.vendor.OFVendorDataType; +import org.openflow.protocol.vendor.OFVendorId; + + +/** + * A basic OpenFlow factory that supports naive creation of both Messages and + * Actions. + * + * @author David Erickson (daviderickson@cs.stanford.edu) + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * + */ +public enum BasicFactory implements OFMessageFactory, OFActionFactory, + OFStatisticsFactory, OFVendorDataFactory { + SINGLETON_INSTANCE; + + + private final OFVendorActionRegistry vendorActionRegistry; + + private BasicFactory() { + vendorActionRegistry = OFVendorActionRegistry.getInstance(); + } + + public static BasicFactory getInstance() { + return SINGLETON_INSTANCE; + } + + /** + * create and return a new instance of a message for OFType t. Also injects + * factories for those message types that implement the *FactoryAware + * interfaces. + * + * @return a newly created instance that may be modified / used freely by + * the caller + */ + @Override + public OFMessage getMessage(OFType t) { + OFMessage message = t.newInstance(); + injectFactories(message); + return message; + } + + @Override + public List<OFMessage> parseMessage(ChannelBuffer data) throws MessageParseException { + List<OFMessage> msglist = new ArrayList<OFMessage>(); + OFMessage msg = null; + + while (data.readableBytes() >= OFMessage.MINIMUM_LENGTH) { + data.markReaderIndex(); + msg = this.parseMessageOne(data); + if (msg == null) { + data.resetReaderIndex(); + break; + } + else { + msglist.add(msg); + } + } + + if (msglist.size() == 0) { + return null; + } + return msglist; + + } + + public OFMessage parseMessageOne(ChannelBuffer data) throws MessageParseException { + try { + OFMessage demux = new OFMessage(); + OFMessage ofm = null; + + if (data.readableBytes() < OFMessage.MINIMUM_LENGTH) + return ofm; + + data.markReaderIndex(); + demux.readFrom(data); + data.resetReaderIndex(); + + if (demux.getLengthU() > data.readableBytes()) + return ofm; + + ofm = getMessage(demux.getType()); + if (ofm == null) + return null; + + injectFactories(ofm); + ofm.readFrom(data); + if (OFMessage.class.equals(ofm.getClass())) { + // advance the position for un-implemented messages + data.readerIndex(data.readerIndex()+(ofm.getLengthU() - + OFMessage.MINIMUM_LENGTH)); + } + + return ofm; + } catch (Exception e) { + /* Write the offending data along with the error message */ + data.resetReaderIndex(); + String msg = + "Message Parse Error for packet:" + dumpBuffer(data) + + "\nException: " + e.toString(); + data.resetReaderIndex(); + + throw new MessageParseException(msg, e); + } + } + + private void injectFactories(OFMessage ofm) { + if (ofm instanceof OFActionFactoryAware) { + ((OFActionFactoryAware)ofm).setActionFactory(this); + } + if (ofm instanceof OFMessageFactoryAware) { + ((OFMessageFactoryAware)ofm).setMessageFactory(this); + } + if (ofm instanceof OFStatisticsFactoryAware) { + ((OFStatisticsFactoryAware)ofm).setStatisticsFactory(this); + } + if (ofm instanceof OFVendorDataFactoryAware) { + ((OFVendorDataFactoryAware)ofm).setVendorDataFactory(this); + } + } + + @Override + public OFAction getAction(OFActionType t) { + return t.newInstance(); + } + + @Override + public List<OFAction> parseActions(ChannelBuffer data, int length) { + return parseActions(data, length, 0); + } + + @Override + public List<OFAction> parseActions(ChannelBuffer data, int length, int limit) { + List<OFAction> results = new ArrayList<OFAction>(); + OFAction demux = new OFAction(); + OFAction ofa; + int end = data.readerIndex() + length; + + while (limit == 0 || results.size() <= limit) { + if ((data.readableBytes() < OFAction.MINIMUM_LENGTH || + (data.readerIndex() + OFAction.MINIMUM_LENGTH) > end)) + return results; + + data.markReaderIndex(); + demux.readFrom(data); + data.resetReaderIndex(); + + if ((demux.getLengthU() > data.readableBytes() || + (data.readerIndex() + demux.getLengthU()) > end)) + return results; + + ofa = parseActionOne(demux.getType(), data); + results.add(ofa); + } + + return results; + } + + private OFAction parseActionOne(OFActionType type, ChannelBuffer data) { + OFAction ofa; + data.markReaderIndex(); + ofa = getAction(type); + ofa.readFrom(data); + + if(type == OFActionType.VENDOR) { + OFActionVendor vendorAction = (OFActionVendor) ofa; + + OFVendorActionFactory vendorActionFactory = vendorActionRegistry.get(vendorAction.getVendor()); + + if(vendorActionFactory != null) { + // if we have a specific vendorActionFactory for this vendor id, + // delegate to it for vendor-specific reparsing of the message + data.resetReaderIndex(); + OFActionVendor newAction = vendorActionFactory.readFrom(data); + if(newAction != null) + ofa = newAction; + } + } + + if (OFAction.class.equals(ofa.getClass())) { + // advance the position for un-implemented messages + data.readerIndex(data.readerIndex()+(ofa.getLengthU() - + OFAction.MINIMUM_LENGTH)); + } + return ofa; + } + + @Override + public OFActionFactory getActionFactory() { + return this; + } + + @Override + public OFStatistics getStatistics(OFType t, OFStatisticsType st) { + return st.newInstance(t); + } + + @Override + public List<OFStatistics> parseStatistics(OFType t, OFStatisticsType st, + ChannelBuffer data, int length) { + return parseStatistics(t, st, data, length, 0); + } + + /** + * @param t + * OFMessage type: should be one of stats_request or stats_reply + * @param st + * statistics type of this message, e.g., DESC, TABLE + * @param data + * buffer to read from + * @param length + * length of statistics + * @param limit + * number of statistics to grab; 0 == all + * + * @return list of statistics + */ + + @Override + public List<OFStatistics> parseStatistics(OFType t, OFStatisticsType st, + ChannelBuffer data, int length, int limit) { + List<OFStatistics> results = new ArrayList<OFStatistics>(); + OFStatistics statistics = getStatistics(t, st); + + int start = data.readerIndex(); + int count = 0; + + while (limit == 0 || results.size() <= limit) { + // TODO Create a separate MUX/DEMUX path for vendor stats + if (statistics instanceof OFVendorStatistics) + ((OFVendorStatistics)statistics).setLength(length); + + /** + * can't use data.remaining() here, b/c there could be other data + * buffered past this message + */ + if ((length - count) >= statistics.getLength()) { + if (statistics instanceof OFActionFactoryAware) + ((OFActionFactoryAware)statistics).setActionFactory(this); + statistics.readFrom(data); + results.add(statistics); + count += statistics.getLength(); + statistics = getStatistics(t, st); + } else { + if (count < length) { + /** + * Nasty case: partial/incomplete statistic found even + * though we have a full message. Found when NOX sent + * agg_stats request with wrong agg statistics length (52 + * instead of 56) + * + * just throw the rest away, or we will break framing + */ + data.readerIndex(start + length); + } + return results; + } + } + return results; // empty; no statistics at all + } + + + @Override + public OFVendorData getVendorData(OFVendorId vendorId, + OFVendorDataType vendorDataType) { + if (vendorDataType == null) + return null; + + return vendorDataType.newInstance(); + } + + /** + * Attempts to parse and return the OFVendorData contained in the given + * ChannelBuffer, beginning right after the vendor id. + * @param vendor the vendor id that was parsed from the OFVendor message. + * @param data the ChannelBuffer from which to parse the vendor data + * @param length the length to the end of the enclosing message. + * @return an OFVendorData instance + */ + @Override + public OFVendorData parseVendorData(int vendor, ChannelBuffer data, + int length) { + OFVendorDataType vendorDataType = null; + OFVendorId vendorId = OFVendorId.lookupVendorId(vendor); + if (vendorId != null) { + data.markReaderIndex(); + vendorDataType = vendorId.parseVendorDataType(data, length); + data.resetReaderIndex(); + } + + OFVendorData vendorData = getVendorData(vendorId, vendorDataType); + if (vendorData == null) + vendorData = new OFByteArrayVendorData(); + + vendorData.readFrom(data, length); + + return vendorData; + } + + public static String dumpBuffer(ChannelBuffer data) { + // NOTE: Reads all the bytes in buffer from current read offset. + // Set/Reset ReaderIndex if you want to read from a different location + int len = data.readableBytes(); + StringBuffer sb = new StringBuffer(); + for (int i=0 ; i<len; i++) { + if (i%32 == 0) sb.append("\n"); + if (i%4 == 0) sb.append(" "); + sb.append(String.format("%02x", data.getUnsignedByte(i))); + } + return sb.toString(); + } + +} diff --git a/src/main/java/org/openflow/protocol/factory/MessageParseException.java b/src/main/java/org/openflow/protocol/factory/MessageParseException.java new file mode 100644 index 0000000000000000000000000000000000000000..20f381ef8d095a22f98936640435b94a61f5c237 --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/MessageParseException.java @@ -0,0 +1,45 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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.openflow.protocol.factory; + +/** + * Exception thrown when an openflow message fails to parse properly + */ +public class MessageParseException extends Exception { + /** + * + */ + private static final long serialVersionUID = -75893812926304726L; + + public MessageParseException() { + super(); + } + + public MessageParseException(String message, Throwable cause) { + super(message, cause); + this.setStackTrace(cause.getStackTrace()); + } + + public MessageParseException(String message) { + super(message); + } + + public MessageParseException(Throwable cause) { + super(cause); + this.setStackTrace(cause.getStackTrace()); + } +} diff --git a/src/main/java/org/openflow/protocol/factory/OFActionFactory.java b/src/main/java/org/openflow/protocol/factory/OFActionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..c3cd06265bf9a7d0b832afabc5ff18ce0412eacb --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/OFActionFactory.java @@ -0,0 +1,61 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.factory; + +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionType; + + +/** + * The interface to factories used for retrieving OFAction instances. All + * methods are expected to be thread-safe. + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public interface OFActionFactory { + /** + * Retrieves an OFAction instance corresponding to the specified + * OFActionType + * @param t the type of the OFAction to be retrieved + * @return an OFAction instance + */ + public OFAction getAction(OFActionType t); + + /** + * Attempts to parse and return all OFActions contained in the given + * ByteBuffer, beginning at the ByteBuffer's position, and ending at + * position+length. + * @param data the ChannelBuffer to parse for OpenFlow actions + * @param length the number of Bytes to examine for OpenFlow actions + * @return a list of OFAction instances + */ + public List<OFAction> parseActions(ChannelBuffer data, int length); + + /** + * Attempts to parse and return all OFActions contained in the given + * ByteBuffer, beginning at the ByteBuffer's position, and ending at + * position+length. + * @param data the ChannelBuffer to parse for OpenFlow actions + * @param length the number of Bytes to examine for OpenFlow actions + * @param limit the maximum number of messages to return, 0 means no limit + * @return a list of OFAction instances + */ + public List<OFAction> parseActions(ChannelBuffer data, int length, int limit); +} diff --git a/src/main/java/org/openflow/protocol/factory/OFActionFactoryAware.java b/src/main/java/org/openflow/protocol/factory/OFActionFactoryAware.java new file mode 100644 index 0000000000000000000000000000000000000000..a97a95c0a6c666fc01112cb629ad750c0f4d8012 --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/OFActionFactoryAware.java @@ -0,0 +1,31 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.factory; + +/** + * Objects implementing this interface are expected to be instantiated with an + * instance of an OFActionFactory + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public interface OFActionFactoryAware { + /** + * Sets the OFActionFactory + * @param actionFactory + */ + public void setActionFactory(OFActionFactory actionFactory); +} diff --git a/src/main/java/org/openflow/protocol/factory/OFMessageFactory.java b/src/main/java/org/openflow/protocol/factory/OFMessageFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..8bb7045463aee7cfc6d66a207f9737b8a7eabdf6 --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/OFMessageFactory.java @@ -0,0 +1,55 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.factory; + +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFType; + + +/** + * The interface to factories used for retrieving OFMessage instances. All + * methods are expected to be thread-safe. + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public interface OFMessageFactory { + /** + * Retrieves an OFMessage instance corresponding to the specified OFType + * @param t the type of the OFMessage to be retrieved + * @return an OFMessage instance + */ + public OFMessage getMessage(OFType t); + + /** + * Attempts to parse and return a OFMessages contained in the given + * ChannelBuffer, beginning at the ChannelBuffer's position, and ending at the + * after the first parsed message + * @param data the ChannelBuffer to parse for an OpenFlow message + * @return a list of OFMessage instances + * @throws MessageParseException + */ + public List<OFMessage> parseMessage(ChannelBuffer data) throws MessageParseException; + + /** + * Retrieves an OFActionFactory + * @return an OFActionFactory + */ + public OFActionFactory getActionFactory(); +} diff --git a/src/main/java/org/openflow/protocol/factory/OFMessageFactoryAware.java b/src/main/java/org/openflow/protocol/factory/OFMessageFactoryAware.java new file mode 100644 index 0000000000000000000000000000000000000000..adb1421e13a7638b84f6aca7c9e320a4dc944e69 --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/OFMessageFactoryAware.java @@ -0,0 +1,35 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.factory; + +/** + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * + */ +public interface OFMessageFactoryAware { + + /** + * Sets the message factory for this object + * + * @param factory + */ + void setMessageFactory(OFMessageFactory factory); +} diff --git a/src/main/java/org/openflow/protocol/factory/OFStatisticsFactory.java b/src/main/java/org/openflow/protocol/factory/OFStatisticsFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..32eb3cbffa5fb1765439108c4691279981743240 --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/OFStatisticsFactory.java @@ -0,0 +1,72 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.factory; + +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFType; +import org.openflow.protocol.statistics.OFStatistics; +import org.openflow.protocol.statistics.OFStatisticsType; + + +/** + * The interface to factories used for retrieving OFStatistics instances. All + * methods are expected to be thread-safe. + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public interface OFStatisticsFactory { + /** + * Retrieves an OFStatistics instance corresponding to the specified + * OFStatisticsType + * @param t the type of the containing OFMessage, only accepts statistics + * request or reply + * @param st the type of the OFStatistics to be retrieved + * @return an OFStatistics instance + */ + public OFStatistics getStatistics(OFType t, OFStatisticsType st); + + /** + * Attempts to parse and return all OFStatistics contained in the given + * ByteBuffer, beginning at the ByteBuffer's position, and ending at + * position+length. + * @param t the type of the containing OFMessage, only accepts statistics + * request or reply + * @param st the type of the OFStatistics to be retrieved + * @param data the ChannelBuffer to parse for OpenFlow Statistics + * @param length the number of Bytes to examine for OpenFlow Statistics + * @return a list of OFStatistics instances + */ + public List<OFStatistics> parseStatistics(OFType t, + OFStatisticsType st, ChannelBuffer data, int length); + + /** + * Attempts to parse and return all OFStatistics contained in the given + * ByteBuffer, beginning at the ByteBuffer's position, and ending at + * position+length. + * @param t the type of the containing OFMessage, only accepts statistics + * request or reply + * @param st the type of the OFStatistics to be retrieved + * @param data the ChannelBuffer to parse for OpenFlow Statistics + * @param length the number of Bytes to examine for OpenFlow Statistics + * @param limit the maximum number of messages to return, 0 means no limit + * @return a list of OFStatistics instances + */ + public List<OFStatistics> parseStatistics(OFType t, + OFStatisticsType st, ChannelBuffer data, int length, int limit); +} diff --git a/src/main/java/org/openflow/protocol/factory/OFStatisticsFactoryAware.java b/src/main/java/org/openflow/protocol/factory/OFStatisticsFactoryAware.java new file mode 100644 index 0000000000000000000000000000000000000000..52ab09a9501ce05e358766d6d08056dd33997b07 --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/OFStatisticsFactoryAware.java @@ -0,0 +1,31 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.factory; + +/** + * Objects implementing this interface are expected to be instantiated with an + * instance of an OFStatisticsFactory + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public interface OFStatisticsFactoryAware { + /** + * Sets the OFStatisticsFactory + * @param statisticsFactory + */ + public void setStatisticsFactory(OFStatisticsFactory statisticsFactory); +} diff --git a/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java b/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..eb89810b78b9709b89b651731cd20d5615f6b80e --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java @@ -0,0 +1,43 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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.openflow.protocol.factory; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.action.OFActionVendor; + +/** Interface contract for an actionfactory that creates vendor-specific actions. + * VendorActionFactories are registered with the BasicFactory for a specific + * vendor id. + * <p> + * <b>Note:</b> Implementations are expected to be thread-safe. + * + * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> + */ +public interface OFVendorActionFactory { + + /** parse the data from the wire, create and return a vendor-specific action. + * + * @param data contains a serialized vendor action at the current readerPosition. + * The full message is guaranteed to be available in the buffer. + * + * @return upon success returns a newly allocated vendor-specific + * action instance, and advances the readerPosition in data for the + * entire length. Upon failure, returns null and leaves the readerPosition + * in data unmodified. + */ + OFActionVendor readFrom(ChannelBuffer data); +} diff --git a/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java b/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java new file mode 100644 index 0000000000000000000000000000000000000000..1f556812c65bde8dce9e7c17de2ac050a8c24160 --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java @@ -0,0 +1,50 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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.openflow.protocol.factory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** Singleton registry object that holds a mapping from vendor ids to vendor-specific + * mapping factories. Threadsafe. + * + * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> + */ +public class OFVendorActionRegistry { + private static class InstanceHolder { + private final static OFVendorActionRegistry instance = new OFVendorActionRegistry(); + } + + public static OFVendorActionRegistry getInstance() { + return InstanceHolder.instance; + } + private final Map <Integer, OFVendorActionFactory> vendorActionFactories; + + public OFVendorActionRegistry() { + vendorActionFactories = new ConcurrentHashMap<Integer, OFVendorActionFactory>(); + } + + public OFVendorActionFactory register(int vendorId, OFVendorActionFactory factory) { + return vendorActionFactories.put(vendorId, factory); + } + + public OFVendorActionFactory get(int vendorId) { + return vendorActionFactories.get(vendorId); + } + + +} diff --git a/src/main/java/org/openflow/protocol/factory/OFVendorDataFactory.java b/src/main/java/org/openflow/protocol/factory/OFVendorDataFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..d754a4a31087cc309cc6ac8885d05c492c0da23a --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/OFVendorDataFactory.java @@ -0,0 +1,69 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* 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.openflow.protocol.factory; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.vendor.OFVendorData; +import org.openflow.protocol.vendor.OFVendorDataType; +import org.openflow.protocol.vendor.OFVendorId; + +/** + * The interface to factories used for parsing/creating OFVendorData instances. + * All methods are expected to be thread-safe. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public interface OFVendorDataFactory { + /** + * Retrieves an OFVendorData instance corresponding to the specified + * OFVendorId and OFVendorDataType. There are 3 possible cases for + * how this will be called: + * + * 1) If the vendor id in the OFVendor message is an unknown value, + * then this method is called with both vendorId and vendorDataType + * set to null. In this case typically the factory method should + * return an instance of OFGenericVendorData that just contains + * the raw byte array of the vendor data. + * + * 2) If the vendor id is known but no vendor data type has been + * registered for the data in the message, then vendorId is set to + * the appropriate OFVendorId instance and OFVendorDataType is set + * to null. This would typically be handled the same way as #1 + * + * 3) If both the vendor id and and vendor data type are known, then + * typically you'd just call the method in OFVendorDataType to + * instantiate the appropriate subclass of OFVendorData. + * + * @param vendorId the vendorId of the containing OFVendor message + * @param vendorDataType the type of the OFVendorData to be retrieved + * @return an OFVendorData instance + */ + public OFVendorData getVendorData(OFVendorId vendorId, + OFVendorDataType vendorDataType); + + /** + * Attempts to parse and return the OFVendorData contained in the given + * ChannelBuffer, beginning right after the vendor id. + * @param vendorId the vendor id that was parsed from the OFVendor message. + * @param data the ChannelBuffer from which to parse the vendor data + * @param length the length to the end of the enclosing message. + * @return an OFVendorData instance + */ + public OFVendorData parseVendorData(int vendorId, ChannelBuffer data, + int length); +} diff --git a/src/main/java/org/openflow/protocol/factory/OFVendorDataFactoryAware.java b/src/main/java/org/openflow/protocol/factory/OFVendorDataFactoryAware.java new file mode 100644 index 0000000000000000000000000000000000000000..23614b0d0eccdfe23184973ddb57f790b7ff48c3 --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/OFVendorDataFactoryAware.java @@ -0,0 +1,28 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* 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.openflow.protocol.factory; + +/** + * Classes implementing this interface are expected to be instantiated with an + * instance of an OFVendorDataFactory + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public interface OFVendorDataFactoryAware { + public void setVendorDataFactory(OFVendorDataFactory vendorDataFactory); +} diff --git a/src/main/java/org/openflow/protocol/serializers/OFFeaturesReplyJSONSerializer.java b/src/main/java/org/openflow/protocol/serializers/OFFeaturesReplyJSONSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..1102dc78653263a2e33a4967b8d4a3935f44df67 --- /dev/null +++ b/src/main/java/org/openflow/protocol/serializers/OFFeaturesReplyJSONSerializer.java @@ -0,0 +1,57 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* 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.openflow.protocol.serializers; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import org.openflow.protocol.OFFeaturesReply; +import org.openflow.util.HexString; + +public class OFFeaturesReplyJSONSerializer extends JsonSerializer<OFFeaturesReply> { + + /** + * Performs the serialization of a OFFeaturesReply object + */ + @Override + public void serialize(OFFeaturesReply reply, JsonGenerator jGen, SerializerProvider serializer) throws IOException, JsonProcessingException { + jGen.writeStartObject(); + jGen.writeNumberField("actions", reply.getActions()); + jGen.writeNumberField("buffers", reply.getBuffers()); + jGen.writeNumberField("capabilities", reply.getCapabilities()); + jGen.writeStringField("datapathId", HexString.toHexString(reply.getDatapathId())); + jGen.writeNumberField("length", reply.getLength()); + serializer.defaultSerializeField("ports", reply.getPorts(), jGen); + jGen.writeNumberField("tables", reply.getTables()); + jGen.writeStringField("type", reply.getType().toString()); + jGen.writeNumberField("version", reply.getVersion()); + jGen.writeNumberField("xid", reply.getXid()); + jGen.writeEndObject(); + } + + /** + * Tells SimpleModule that we are the serializer for OFFeaturesReply + */ + @Override + public Class<OFFeaturesReply> handledType() { + return OFFeaturesReply.class; + } +} diff --git a/src/main/java/org/openflow/protocol/serializers/OFMatchJSONSerializer.java b/src/main/java/org/openflow/protocol/serializers/OFMatchJSONSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..2ae2d1c467d095491b88d8c78a15479ed04bdbae --- /dev/null +++ b/src/main/java/org/openflow/protocol/serializers/OFMatchJSONSerializer.java @@ -0,0 +1,91 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* 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.openflow.protocol.serializers; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import org.openflow.protocol.OFMatch; +import org.openflow.util.HexString; + +public class OFMatchJSONSerializer extends JsonSerializer<OFMatch> { + + /** + * Converts an IP in a 32 bit integer to a dotted-decimal string + * @param i The IP address in a 32 bit integer + * @return An IP address string in dotted-decimal + */ + private String intToIp(int i) { + return ((i >> 24 ) & 0xFF) + "." + + ((i >> 16 ) & 0xFF) + "." + + ((i >> 8 ) & 0xFF) + "." + + ( i & 0xFF); + } + + /** + * Performs the serialization of a OFMatch object + */ + @Override + public void serialize(OFMatch match, JsonGenerator jGen, + SerializerProvider serializer) + throws IOException, JsonProcessingException { + jGen.writeStartObject(); + jGen.writeStringField("dataLayerDestination", + HexString.toHexString(match.getDataLayerDestination())); + jGen.writeStringField("dataLayerSource", + HexString.toHexString(match.getDataLayerSource())); + String dataType = Integer.toHexString(match.getDataLayerType()); + while (dataType.length() < 4) { + dataType = "0".concat(dataType); + } + jGen.writeStringField("dataLayerType", "0x" + dataType); + jGen.writeNumberField("dataLayerVirtualLan", + match.getDataLayerVirtualLan()); + jGen.writeNumberField("dataLayerVirtualLanPriorityCodePoint", + match.getDataLayerVirtualLanPriorityCodePoint()); + jGen.writeNumberField("inputPort", match.getInputPort()); + jGen.writeStringField("networkDestination", + intToIp(match.getNetworkDestination())); + jGen.writeNumberField("networkDestinationMaskLen", + match.getNetworkDestinationMaskLen()); + jGen.writeNumberField("networkProtocol", match.getNetworkProtocol()); + jGen.writeStringField("networkSource", + intToIp(match.getNetworkSource())); + jGen.writeNumberField("networkSourceMaskLen", + match.getNetworkSourceMaskLen()); + jGen.writeNumberField("networkTypeOfService", + match.getNetworkTypeOfService()); + jGen.writeNumberField("transportDestination", + match.getTransportDestination()); + jGen.writeNumberField("transportSource", + match.getTransportSource()); + jGen.writeNumberField("wildcards", match.getWildcards()); + jGen.writeEndObject(); + } + + /** + * Tells SimpleModule that we are the serializer for OFMatch + */ + @Override + public Class<OFMatch> handledType() { + return OFMatch.class; + } +} diff --git a/src/main/java/org/openflow/protocol/serializers/StringDpidToLongJSONDeserializer.java b/src/main/java/org/openflow/protocol/serializers/StringDpidToLongJSONDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..60c5a664e828508323c68657ec0663b6ab658152 --- /dev/null +++ b/src/main/java/org/openflow/protocol/serializers/StringDpidToLongJSONDeserializer.java @@ -0,0 +1,23 @@ +package org.openflow.protocol.serializers; + +import java.io.IOException; + +import org.openflow.util.HexString; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +public class StringDpidToLongJSONDeserializer extends + JsonDeserializer<Long> { + + @Override + public Long deserialize(JsonParser jsonParser, + DeserializationContext cntx) + throws IOException, + JsonProcessingException { + return Long.valueOf(HexString.toLong(jsonParser.getText())); + } + +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsReply.java b/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsReply.java new file mode 100644 index 0000000000000000000000000000000000000000..0c86006b0919e1a87120ab96dec2caed96cd48da --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsReply.java @@ -0,0 +1,130 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_aggregate_stats_reply structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFAggregateStatisticsReply implements OFStatistics { + protected long packetCount; + protected long byteCount; + protected int flowCount; + + /** + * @return the packetCount + */ + public long getPacketCount() { + return packetCount; + } + + /** + * @param packetCount the packetCount to set + */ + public void setPacketCount(long packetCount) { + this.packetCount = packetCount; + } + + /** + * @return the byteCount + */ + public long getByteCount() { + return byteCount; + } + + /** + * @param byteCount the byteCount to set + */ + public void setByteCount(long byteCount) { + this.byteCount = byteCount; + } + + /** + * @return the flowCount + */ + public int getFlowCount() { + return flowCount; + } + + /** + * @param flowCount the flowCount to set + */ + public void setFlowCount(int flowCount) { + this.flowCount = flowCount; + } + + @Override + @JsonIgnore + public int getLength() { + return 24; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.packetCount = data.readLong(); + this.byteCount = data.readLong(); + this.flowCount = data.readInt(); + data.readInt(); // pad + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeLong(this.packetCount); + data.writeLong(this.byteCount); + data.writeInt(this.flowCount); + data.writeInt(0); // pad + } + + @Override + public int hashCode() { + final int prime = 397; + int result = 1; + result = prime * result + (int) (byteCount ^ (byteCount >>> 32)); + result = prime * result + flowCount; + result = prime * result + (int) (packetCount ^ (packetCount >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFAggregateStatisticsReply)) { + return false; + } + OFAggregateStatisticsReply other = (OFAggregateStatisticsReply) obj; + if (byteCount != other.byteCount) { + return false; + } + if (flowCount != other.flowCount) { + return false; + } + if (packetCount != other.packetCount) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsRequest.java b/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..f41a4f1d57c15a341aeb66f54cc0a87cf011ec61 --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFAggregateStatisticsRequest.java @@ -0,0 +1,135 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFMatch; + +/** + * Represents an ofp_aggregate_stats_request structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFAggregateStatisticsRequest implements OFStatistics { + protected OFMatch match; + protected byte tableId; + protected short outPort; + + /** + * @return the match + */ + public OFMatch getMatch() { + return match; + } + + /** + * @param match the match to set + */ + public void setMatch(OFMatch match) { + this.match = match; + } + + /** + * @return the tableId + */ + public byte getTableId() { + return tableId; + } + + /** + * @param tableId the tableId to set + */ + public void setTableId(byte tableId) { + this.tableId = tableId; + } + + /** + * @return the outPort + */ + public short getOutPort() { + return outPort; + } + + /** + * @param outPort the outPort to set + */ + public void setOutPort(short outPort) { + this.outPort = outPort; + } + + @Override + public int getLength() { + return 44; + } + + @Override + public void readFrom(ChannelBuffer data) { + if (this.match == null) + this.match = new OFMatch(); + this.match.readFrom(data); + this.tableId = data.readByte(); + data.readByte(); // pad + this.outPort = data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + this.match.writeTo(data); + data.writeByte(this.tableId); + data.writeByte((byte) 0); + data.writeShort(this.outPort); + } + + @Override + public int hashCode() { + final int prime = 401; + int result = 1; + result = prime * result + ((match == null) ? 0 : match.hashCode()); + result = prime * result + outPort; + result = prime * result + tableId; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFAggregateStatisticsRequest)) { + return false; + } + OFAggregateStatisticsRequest other = (OFAggregateStatisticsRequest) obj; + if (match == null) { + if (other.match != null) { + return false; + } + } else if (!match.equals(other.match)) { + return false; + } + if (outPort != other.outPort) { + return false; + } + if (tableId != other.tableId) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFDescriptionStatistics.java b/src/main/java/org/openflow/protocol/statistics/OFDescriptionStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..5e6586f0dfd00d71ac89aee183e9cc8e0d299ba8 --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFDescriptionStatistics.java @@ -0,0 +1,246 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.StringByteSerializer; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * Represents an ofp_desc_stats structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFDescriptionStatistics implements OFStatistics { + public static int DESCRIPTION_STRING_LENGTH = 256; + public static int SERIAL_NUMBER_LENGTH = 32; + + protected String manufacturerDescription; + protected String hardwareDescription; + protected String softwareDescription; + protected String serialNumber; + protected String datapathDescription; + + + /** + * + */ + public OFDescriptionStatistics() { + } + + /** + * Copy constructor + */ + public OFDescriptionStatistics(OFDescriptionStatistics other) { + manufacturerDescription = other.manufacturerDescription; + hardwareDescription = other.hardwareDescription; + softwareDescription = other.softwareDescription; + serialNumber = other.serialNumber; + datapathDescription = other.datapathDescription; + } + + /** + * @return the manufacturerDescription + */ + public String getManufacturerDescription() { + return manufacturerDescription; + } + + /** + * @param manufacturerDescription the manufacturerDescription to set + */ + public void setManufacturerDescription(String manufacturerDescription) { + this.manufacturerDescription = manufacturerDescription; + } + + /** + * @return the hardwareDescription + */ + public String getHardwareDescription() { + return hardwareDescription; + } + + /** + * @param hardwareDescription the hardwareDescription to set + */ + public void setHardwareDescription(String hardwareDescription) { + this.hardwareDescription = hardwareDescription; + } + + /** + * @return the softwareDescription + */ + public String getSoftwareDescription() { + return softwareDescription; + } + + /** + * @param softwareDescription the softwareDescription to set + */ + public void setSoftwareDescription(String softwareDescription) { + this.softwareDescription = softwareDescription; + } + + /** + * @return the serialNumber + */ + public String getSerialNumber() { + return serialNumber; + } + + /** + * @param serialNumber the serialNumber to set + */ + public void setSerialNumber(String serialNumber) { + this.serialNumber = serialNumber; + } + + /** + * @return the datapathDescription + */ + public String getDatapathDescription() { + return datapathDescription; + } + + /** + * @param datapathDescription the datapathDescription to set + */ + public void setDatapathDescription(String datapathDescription) { + this.datapathDescription = datapathDescription; + } + + @Override + @JsonIgnore + public int getLength() { + return 1056; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.manufacturerDescription = StringByteSerializer.readFrom(data, + DESCRIPTION_STRING_LENGTH); + this.hardwareDescription = StringByteSerializer.readFrom(data, + DESCRIPTION_STRING_LENGTH); + this.softwareDescription = StringByteSerializer.readFrom(data, + DESCRIPTION_STRING_LENGTH); + this.serialNumber = StringByteSerializer.readFrom(data, + SERIAL_NUMBER_LENGTH); + this.datapathDescription = StringByteSerializer.readFrom(data, + DESCRIPTION_STRING_LENGTH); + } + + @Override + public void writeTo(ChannelBuffer data) { + StringByteSerializer.writeTo(data, DESCRIPTION_STRING_LENGTH, + this.manufacturerDescription); + StringByteSerializer.writeTo(data, DESCRIPTION_STRING_LENGTH, + this.hardwareDescription); + StringByteSerializer.writeTo(data, DESCRIPTION_STRING_LENGTH, + this.softwareDescription); + StringByteSerializer.writeTo(data, SERIAL_NUMBER_LENGTH, + this.serialNumber); + StringByteSerializer.writeTo(data, DESCRIPTION_STRING_LENGTH, + this.datapathDescription); + } + + @Override + public int hashCode() { + final int prime = 409; + int result = 1; + result = prime + * result + + ((datapathDescription == null) ? 0 : datapathDescription + .hashCode()); + result = prime + * result + + ((hardwareDescription == null) ? 0 : hardwareDescription + .hashCode()); + result = prime + * result + + ((manufacturerDescription == null) ? 0 + : manufacturerDescription.hashCode()); + result = prime * result + + ((serialNumber == null) ? 0 : serialNumber.hashCode()); + result = prime + * result + + ((softwareDescription == null) ? 0 : softwareDescription + .hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFDescriptionStatistics)) { + return false; + } + OFDescriptionStatistics other = (OFDescriptionStatistics) obj; + if (datapathDescription == null) { + if (other.datapathDescription != null) { + return false; + } + } else if (!datapathDescription.equals(other.datapathDescription)) { + return false; + } + if (hardwareDescription == null) { + if (other.hardwareDescription != null) { + return false; + } + } else if (!hardwareDescription.equals(other.hardwareDescription)) { + return false; + } + if (manufacturerDescription == null) { + if (other.manufacturerDescription != null) { + return false; + } + } else if (!manufacturerDescription + .equals(other.manufacturerDescription)) { + return false; + } + if (serialNumber == null) { + if (other.serialNumber != null) { + return false; + } + } else if (!serialNumber.equals(other.serialNumber)) { + return false; + } + if (softwareDescription == null) { + if (other.softwareDescription != null) { + return false; + } + } else if (!softwareDescription.equals(other.softwareDescription)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Switch Desc - Vendor: " + manufacturerDescription + + " Model: " + hardwareDescription + + " Make: " + datapathDescription + + " Version: " + softwareDescription + + " S/N: " + serialNumber; + } +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java b/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java new file mode 100644 index 0000000000000000000000000000000000000000..0aad88df7af5355aabe44df5278cba8fe42f019a --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsReply.java @@ -0,0 +1,359 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFMatch; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.factory.OFActionFactory; +import org.openflow.protocol.factory.OFActionFactoryAware; +import org.openflow.util.U16; + +/** + * Represents an ofp_flow_stats structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFFlowStatisticsReply implements OFStatistics, OFActionFactoryAware { + public static int MINIMUM_LENGTH = 88; + + protected OFActionFactory actionFactory; + protected short length = (short) MINIMUM_LENGTH; + protected byte tableId; + protected OFMatch match; + protected int durationSeconds; + protected int durationNanoseconds; + protected short priority; + protected short idleTimeout; + protected short hardTimeout; + protected long cookie; + protected long packetCount; + protected long byteCount; + protected List<OFAction> actions; + + /** + * @return the tableId + */ + public byte getTableId() { + return tableId; + } + + /** + * @param tableId the tableId to set + */ + public void setTableId(byte tableId) { + this.tableId = tableId; + } + + /** + * @return the match + */ + public OFMatch getMatch() { + return match; + } + + /** + * @param match the match to set + */ + public void setMatch(OFMatch match) { + this.match = match; + } + + /** + * @return the durationSeconds + */ + public int getDurationSeconds() { + return durationSeconds; + } + + /** + * @param durationSeconds the durationSeconds to set + */ + public void setDurationSeconds(int durationSeconds) { + this.durationSeconds = durationSeconds; + } + + /** + * @return the durationNanoseconds + */ + public int getDurationNanoseconds() { + return durationNanoseconds; + } + + /** + * @param durationNanoseconds the durationNanoseconds to set + */ + public void setDurationNanoseconds(int durationNanoseconds) { + this.durationNanoseconds = durationNanoseconds; + } + + /** + * @return the priority + */ + public short getPriority() { + return priority; + } + + /** + * @param priority the priority to set + */ + public void setPriority(short priority) { + this.priority = priority; + } + + /** + * @return the idleTimeout + */ + public short getIdleTimeout() { + return idleTimeout; + } + + /** + * @param idleTimeout the idleTimeout to set + */ + public void setIdleTimeout(short idleTimeout) { + this.idleTimeout = idleTimeout; + } + + /** + * @return the hardTimeout + */ + public short getHardTimeout() { + return hardTimeout; + } + + /** + * @param hardTimeout the hardTimeout to set + */ + public void setHardTimeout(short hardTimeout) { + this.hardTimeout = hardTimeout; + } + + /** + * @return the cookie + */ + public long getCookie() { + return cookie; + } + + /** + * @param cookie the cookie to set + */ + public void setCookie(long cookie) { + this.cookie = cookie; + } + + /** + * @return the packetCount + */ + public long getPacketCount() { + return packetCount; + } + + /** + * @param packetCount the packetCount to set + */ + public void setPacketCount(long packetCount) { + this.packetCount = packetCount; + } + + /** + * @return the byteCount + */ + public long getByteCount() { + return byteCount; + } + + /** + * @param byteCount the byteCount to set + */ + public void setByteCount(long byteCount) { + this.byteCount = byteCount; + } + + /** + * @param length the length to set + */ + public void setLength(short length) { + this.length = length; + } + + @Override + @JsonIgnore + public int getLength() { + return U16.f(length); + } + + /** + * @param actionFactory the actionFactory to set + */ + @Override + public void setActionFactory(OFActionFactory actionFactory) { + this.actionFactory = actionFactory; + } + + /** + * @return the actions + */ + public List<OFAction> getActions() { + return actions; + } + + /** + * @param actions the actions to set + */ + public void setActions(List<OFAction> actions) { + this.actions = actions; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.length = data.readShort(); + this.tableId = data.readByte(); + data.readByte(); // pad + if (this.match == null) + this.match = new OFMatch(); + this.match.readFrom(data); + this.durationSeconds = data.readInt(); + this.durationNanoseconds = data.readInt(); + this.priority = data.readShort(); + this.idleTimeout = data.readShort(); + this.hardTimeout = data.readShort(); + data.readInt(); // pad + data.readShort(); // pad + this.cookie = data.readLong(); + this.packetCount = data.readLong(); + this.byteCount = data.readLong(); + if (this.actionFactory == null) + throw new RuntimeException("OFActionFactory not set"); + this.actions = this.actionFactory.parseActions(data, getLength() - + MINIMUM_LENGTH); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeShort(this.length); + data.writeByte(this.tableId); + data.writeByte((byte) 0); + this.match.writeTo(data); + data.writeInt(this.durationSeconds); + data.writeInt(this.durationNanoseconds); + data.writeShort(this.priority); + data.writeShort(this.idleTimeout); + data.writeShort(this.hardTimeout); + data.writeInt(0); // pad + data.writeShort((short)0); // pad + data.writeLong(this.cookie); + data.writeLong(this.packetCount); + data.writeLong(this.byteCount); + if (actions != null) { + for (OFAction action : actions) { + action.writeTo(data); + } + } + } + + @Override + public String toString() { + String str = "match=" + this.match; + str += " tableId=" + this.tableId; + str += " durationSeconds=" + this.durationSeconds; + str += " durationNanoseconds=" + this.durationNanoseconds; + str += " priority=" + this.priority; + str += " idleTimeout=" + this.idleTimeout; + str += " hardTimeout=" + this.hardTimeout; + str += " cookie=" + Long.toHexString(this.cookie); + str += " packetCount=" + this.packetCount; + str += " byteCount=" + this.byteCount; + str += " action=" + this.actions; + + return str; + } + + @Override + public int hashCode() { + final int prime = 419; + int result = 1; + result = prime * result + (int) (byteCount ^ (byteCount >>> 32)); + result = prime * result + (int) (cookie ^ (cookie >>> 32)); + result = prime * result + durationNanoseconds; + result = prime * result + durationSeconds; + result = prime * result + hardTimeout; + result = prime * result + idleTimeout; + result = prime * result + length; + result = prime * result + ((match == null) ? 0 : match.hashCode()); + result = prime * result + (int) (packetCount ^ (packetCount >>> 32)); + result = prime * result + priority; + result = prime * result + tableId; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFFlowStatisticsReply)) { + return false; + } + OFFlowStatisticsReply other = (OFFlowStatisticsReply) obj; + if (byteCount != other.byteCount) { + return false; + } + if (cookie != other.cookie) { + return false; + } + if (durationNanoseconds != other.durationNanoseconds) { + return false; + } + if (durationSeconds != other.durationSeconds) { + return false; + } + if (hardTimeout != other.hardTimeout) { + return false; + } + if (idleTimeout != other.idleTimeout) { + return false; + } + if (length != other.length) { + return false; + } + if (match == null) { + if (other.match != null) { + return false; + } + } else if (!match.equals(other.match)) { + return false; + } + if (packetCount != other.packetCount) { + return false; + } + if (priority != other.priority) { + return false; + } + if (tableId != other.tableId) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsRequest.java b/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..b21de0c7f158298533b3bd8605954ae408282184 --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFFlowStatisticsRequest.java @@ -0,0 +1,135 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFMatch; + +/** + * Represents an ofp_flow_stats_request structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFFlowStatisticsRequest implements OFStatistics { + protected OFMatch match; + protected byte tableId; + protected short outPort; + + /** + * @return the match + */ + public OFMatch getMatch() { + return match; + } + + /** + * @param match the match to set + */ + public void setMatch(OFMatch match) { + this.match = match; + } + + /** + * @return the tableId + */ + public byte getTableId() { + return tableId; + } + + /** + * @param tableId the tableId to set + */ + public void setTableId(byte tableId) { + this.tableId = tableId; + } + + /** + * @return the outPort + */ + public short getOutPort() { + return outPort; + } + + /** + * @param outPort the outPort to set + */ + public void setOutPort(short outPort) { + this.outPort = outPort; + } + + @Override + public int getLength() { + return 44; + } + + @Override + public void readFrom(ChannelBuffer data) { + if (this.match == null) + this.match = new OFMatch(); + this.match.readFrom(data); + this.tableId = data.readByte(); + data.readByte(); // pad + this.outPort = data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + this.match.writeTo(data); + data.writeByte(this.tableId); + data.writeByte((byte) 0); + data.writeShort(this.outPort); + } + + @Override + public int hashCode() { + final int prime = 421; + int result = 1; + result = prime * result + ((match == null) ? 0 : match.hashCode()); + result = prime * result + outPort; + result = prime * result + tableId; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFFlowStatisticsRequest)) { + return false; + } + OFFlowStatisticsRequest other = (OFFlowStatisticsRequest) obj; + if (match == null) { + if (other.match != null) { + return false; + } + } else if (!match.equals(other.match)) { + return false; + } + if (outPort != other.outPort) { + return false; + } + if (tableId != other.tableId) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsReply.java b/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsReply.java new file mode 100644 index 0000000000000000000000000000000000000000..15192bbc4d495fa4fd1f8f064adf0058f348d581 --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsReply.java @@ -0,0 +1,356 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + + +import net.floodlightcontroller.core.web.serializers.UShortSerializer; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_port_stats structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFPortStatisticsReply implements OFStatistics { + protected short portNumber; + protected long receivePackets; + protected long transmitPackets; + protected long receiveBytes; + protected long transmitBytes; + protected long receiveDropped; + protected long transmitDropped; + protected long receiveErrors; + protected long transmitErrors; + protected long receiveFrameErrors; + protected long receiveOverrunErrors; + protected long receiveCRCErrors; + protected long collisions; + + /** + * @return the portNumber + */ + @JsonSerialize(using=UShortSerializer.class) + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + /** + * @return the receivePackets + */ + public long getreceivePackets() { + return receivePackets; + } + + /** + * @param receivePackets the receivePackets to set + */ + public void setreceivePackets(long receivePackets) { + this.receivePackets = receivePackets; + } + + /** + * @return the transmitPackets + */ + public long getTransmitPackets() { + return transmitPackets; + } + + /** + * @param transmitPackets the transmitPackets to set + */ + public void setTransmitPackets(long transmitPackets) { + this.transmitPackets = transmitPackets; + } + + /** + * @return the receiveBytes + */ + public long getReceiveBytes() { + return receiveBytes; + } + + /** + * @param receiveBytes the receiveBytes to set + */ + public void setReceiveBytes(long receiveBytes) { + this.receiveBytes = receiveBytes; + } + + /** + * @return the transmitBytes + */ + public long getTransmitBytes() { + return transmitBytes; + } + + /** + * @param transmitBytes the transmitBytes to set + */ + public void setTransmitBytes(long transmitBytes) { + this.transmitBytes = transmitBytes; + } + + /** + * @return the receiveDropped + */ + public long getReceiveDropped() { + return receiveDropped; + } + + /** + * @param receiveDropped the receiveDropped to set + */ + public void setReceiveDropped(long receiveDropped) { + this.receiveDropped = receiveDropped; + } + + /** + * @return the transmitDropped + */ + public long getTransmitDropped() { + return transmitDropped; + } + + /** + * @param transmitDropped the transmitDropped to set + */ + public void setTransmitDropped(long transmitDropped) { + this.transmitDropped = transmitDropped; + } + + /** + * @return the receiveErrors + */ + public long getreceiveErrors() { + return receiveErrors; + } + + /** + * @param receiveErrors the receiveErrors to set + */ + public void setreceiveErrors(long receiveErrors) { + this.receiveErrors = receiveErrors; + } + + /** + * @return the transmitErrors + */ + public long getTransmitErrors() { + return transmitErrors; + } + + /** + * @param transmitErrors the transmitErrors to set + */ + public void setTransmitErrors(long transmitErrors) { + this.transmitErrors = transmitErrors; + } + + /** + * @return the receiveFrameErrors + */ + public long getReceiveFrameErrors() { + return receiveFrameErrors; + } + + /** + * @param receiveFrameErrors the receiveFrameErrors to set + */ + public void setReceiveFrameErrors(long receiveFrameErrors) { + this.receiveFrameErrors = receiveFrameErrors; + } + + /** + * @return the receiveOverrunErrors + */ + public long getReceiveOverrunErrors() { + return receiveOverrunErrors; + } + + /** + * @param receiveOverrunErrors the receiveOverrunErrors to set + */ + public void setReceiveOverrunErrors(long receiveOverrunErrors) { + this.receiveOverrunErrors = receiveOverrunErrors; + } + + /** + * @return the receiveCRCErrors + */ + public long getReceiveCRCErrors() { + return receiveCRCErrors; + } + + /** + * @param receiveCRCErrors the receiveCRCErrors to set + */ + public void setReceiveCRCErrors(long receiveCRCErrors) { + this.receiveCRCErrors = receiveCRCErrors; + } + + /** + * @return the collisions + */ + public long getCollisions() { + return collisions; + } + + /** + * @param collisions the collisions to set + */ + public void setCollisions(long collisions) { + this.collisions = collisions; + } + + @Override + @JsonIgnore + public int getLength() { + return 104; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.portNumber = data.readShort(); + data.readShort(); // pad + data.readInt(); // pad + this.receivePackets = data.readLong(); + this.transmitPackets = data.readLong(); + this.receiveBytes = data.readLong(); + this.transmitBytes = data.readLong(); + this.receiveDropped = data.readLong(); + this.transmitDropped = data.readLong(); + this.receiveErrors = data.readLong(); + this.transmitErrors = data.readLong(); + this.receiveFrameErrors = data.readLong(); + this.receiveOverrunErrors = data.readLong(); + this.receiveCRCErrors = data.readLong(); + this.collisions = data.readLong(); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeShort(this.portNumber); + data.writeShort((short) 0); // pad + data.writeInt(0); // pad + data.writeLong(this.receivePackets); + data.writeLong(this.transmitPackets); + data.writeLong(this.receiveBytes); + data.writeLong(this.transmitBytes); + data.writeLong(this.receiveDropped); + data.writeLong(this.transmitDropped); + data.writeLong(this.receiveErrors); + data.writeLong(this.transmitErrors); + data.writeLong(this.receiveFrameErrors); + data.writeLong(this.receiveOverrunErrors); + data.writeLong(this.receiveCRCErrors); + data.writeLong(this.collisions); + } + + @Override + public int hashCode() { + final int prime = 431; + int result = 1; + result = prime * result + (int) (collisions ^ (collisions >>> 32)); + result = prime * result + portNumber; + result = prime * result + + (int) (receivePackets ^ (receivePackets >>> 32)); + result = prime * result + (int) (receiveBytes ^ (receiveBytes >>> 32)); + result = prime * result + + (int) (receiveCRCErrors ^ (receiveCRCErrors >>> 32)); + result = prime * result + + (int) (receiveDropped ^ (receiveDropped >>> 32)); + result = prime * result + + (int) (receiveFrameErrors ^ (receiveFrameErrors >>> 32)); + result = prime * result + + (int) (receiveOverrunErrors ^ (receiveOverrunErrors >>> 32)); + result = prime * result + + (int) (receiveErrors ^ (receiveErrors >>> 32)); + result = prime * result + + (int) (transmitBytes ^ (transmitBytes >>> 32)); + result = prime * result + + (int) (transmitDropped ^ (transmitDropped >>> 32)); + result = prime * result + + (int) (transmitErrors ^ (transmitErrors >>> 32)); + result = prime * result + + (int) (transmitPackets ^ (transmitPackets >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFPortStatisticsReply)) { + return false; + } + OFPortStatisticsReply other = (OFPortStatisticsReply) obj; + if (collisions != other.collisions) { + return false; + } + if (portNumber != other.portNumber) { + return false; + } + if (receivePackets != other.receivePackets) { + return false; + } + if (receiveBytes != other.receiveBytes) { + return false; + } + if (receiveCRCErrors != other.receiveCRCErrors) { + return false; + } + if (receiveDropped != other.receiveDropped) { + return false; + } + if (receiveFrameErrors != other.receiveFrameErrors) { + return false; + } + if (receiveOverrunErrors != other.receiveOverrunErrors) { + return false; + } + if (receiveErrors != other.receiveErrors) { + return false; + } + if (transmitBytes != other.transmitBytes) { + return false; + } + if (transmitDropped != other.transmitDropped) { + return false; + } + if (transmitErrors != other.transmitErrors) { + return false; + } + if (transmitPackets != other.transmitPackets) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsRequest.java b/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..c07a895afeb355678db7bf7251e12054369c1c92 --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFPortStatisticsRequest.java @@ -0,0 +1,88 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_port_stats_request structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFPortStatisticsRequest implements OFStatistics { + protected short portNumber; + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + @Override + public int getLength() { + return 8; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.portNumber = data.readShort(); + data.readShort(); // pad + data.readInt(); // pad + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeShort(this.portNumber); + data.writeShort((short) 0); // pad + data.writeInt(0); // pad + } + + @Override + public int hashCode() { + final int prime = 433; + int result = 1; + result = prime * result + portNumber; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFPortStatisticsRequest)) { + return false; + } + OFPortStatisticsRequest other = (OFPortStatisticsRequest) obj; + if (portNumber != other.portNumber) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsReply.java b/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsReply.java new file mode 100644 index 0000000000000000000000000000000000000000..34a9e18200bc73ff5a7d628c671bb641cd243068 --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsReply.java @@ -0,0 +1,175 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_queue_stats structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFQueueStatisticsReply implements OFStatistics { + protected short portNumber; + protected int queueId; + protected long transmitBytes; + protected long transmitPackets; + protected long transmitErrors; + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + /** + * @return the queueId + */ + public int getQueueId() { + return queueId; + } + + /** + * @param queueId the queueId to set + */ + public void setQueueId(int queueId) { + this.queueId = queueId; + } + + /** + * @return the transmitBytes + */ + public long getTransmitBytes() { + return transmitBytes; + } + + /** + * @param transmitBytes the transmitBytes to set + */ + public void setTransmitBytes(long transmitBytes) { + this.transmitBytes = transmitBytes; + } + + /** + * @return the transmitPackets + */ + public long getTransmitPackets() { + return transmitPackets; + } + + /** + * @param transmitPackets the transmitPackets to set + */ + public void setTransmitPackets(long transmitPackets) { + this.transmitPackets = transmitPackets; + } + + /** + * @return the transmitErrors + */ + public long getTransmitErrors() { + return transmitErrors; + } + + /** + * @param transmitErrors the transmitErrors to set + */ + public void setTransmitErrors(long transmitErrors) { + this.transmitErrors = transmitErrors; + } + + @Override + @JsonIgnore + public int getLength() { + return 32; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.portNumber = data.readShort(); + data.readShort(); // pad + this.queueId = data.readInt(); + this.transmitBytes = data.readLong(); + this.transmitPackets = data.readLong(); + this.transmitErrors = data.readLong(); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeShort(this.portNumber); + data.writeShort((short) 0); // pad + data.writeInt(this.queueId); + data.writeLong(this.transmitBytes); + data.writeLong(this.transmitPackets); + data.writeLong(this.transmitErrors); + } + + @Override + public int hashCode() { + final int prime = 439; + int result = 1; + result = prime * result + portNumber; + result = prime * result + queueId; + result = prime * result + + (int) (transmitBytes ^ (transmitBytes >>> 32)); + result = prime * result + + (int) (transmitErrors ^ (transmitErrors >>> 32)); + result = prime * result + + (int) (transmitPackets ^ (transmitPackets >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFQueueStatisticsReply)) { + return false; + } + OFQueueStatisticsReply other = (OFQueueStatisticsReply) obj; + if (portNumber != other.portNumber) { + return false; + } + if (queueId != other.queueId) { + return false; + } + if (transmitBytes != other.transmitBytes) { + return false; + } + if (transmitErrors != other.transmitErrors) { + return false; + } + if (transmitPackets != other.transmitPackets) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsRequest.java b/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..33314539f62b1bbb21e59a92c07712e653b2584e --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFQueueStatisticsRequest.java @@ -0,0 +1,107 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Represents an ofp_queue_stats_request structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFQueueStatisticsRequest implements OFStatistics { + protected short portNumber; + protected int queueId; + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param portNumber the portNumber to set + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + /** + * @return the queueId + */ + public int getQueueId() { + return queueId; + } + + /** + * @param queueId the queueId to set + */ + public void setQueueId(int queueId) { + this.queueId = queueId; + } + + @Override + public int getLength() { + return 8; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.portNumber = data.readShort(); + data.readShort(); // pad + this.queueId = data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeShort(this.portNumber); + data.writeShort((short) 0); // pad + data.writeInt(this.queueId); + } + + @Override + public int hashCode() { + final int prime = 443; + int result = 1; + result = prime * result + portNumber; + result = prime * result + queueId; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFQueueStatisticsRequest)) { + return false; + } + OFQueueStatisticsRequest other = (OFQueueStatisticsRequest) obj; + if (portNumber != other.portNumber) { + return false; + } + if (queueId != other.queueId) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFStatistics.java b/src/main/java/org/openflow/protocol/statistics/OFStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..5e8f4dd3f84949b602f977d3c983a81af0e04383 --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFStatistics.java @@ -0,0 +1,45 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * The base class for all OpenFlow statistics. + * + * @author David Erickson (daviderickson@cs.stanford.edu) - Mar 11, 2010 + */ +public interface OFStatistics { + /** + * Returns the wire length of this message in bytes + * @return the length + */ + public int getLength(); + + /** + * Read this message off the wire from the specified ByteBuffer + * @param data + */ + public void readFrom(ChannelBuffer data); + + /** + * Write this message's binary format to the specified ByteBuffer + * @param data + */ + public void writeTo(ChannelBuffer data); +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFStatisticsType.java b/src/main/java/org/openflow/protocol/statistics/OFStatisticsType.java new file mode 100644 index 0000000000000000000000000000000000000000..f2b9e3015263345a44bac8c9ffbc88c1a6d4fe5b --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFStatisticsType.java @@ -0,0 +1,317 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + +import java.lang.reflect.Constructor; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.OFType; + +public enum OFStatisticsType { + DESC (0, OFDescriptionStatistics.class, OFDescriptionStatistics.class, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFDescriptionStatistics(); + } + }, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFDescriptionStatistics(); + } + }), + FLOW (1, OFFlowStatisticsRequest.class, OFFlowStatisticsReply.class, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFFlowStatisticsRequest(); + } + }, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFFlowStatisticsReply(); + } + }), + AGGREGATE (2, OFAggregateStatisticsRequest.class, OFAggregateStatisticsReply.class, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFAggregateStatisticsRequest(); + } + }, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFAggregateStatisticsReply(); + } + }), + TABLE (3, OFTableStatistics.class, OFTableStatistics.class, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFTableStatistics(); + } + }, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFTableStatistics(); + } + }), + PORT (4, OFPortStatisticsRequest.class, OFPortStatisticsReply.class, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFPortStatisticsRequest(); + } + }, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFPortStatisticsReply(); + } + }), + QUEUE (5, OFQueueStatisticsRequest.class, OFQueueStatisticsReply.class, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFQueueStatisticsRequest(); + } + }, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFQueueStatisticsReply(); + } + }), + VENDOR (0xffff, OFVendorStatistics.class, OFVendorStatistics.class, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFVendorStatistics(); + } + }, + new Instantiable<OFStatistics>() { + @Override + public OFStatistics instantiate() { + return new OFVendorStatistics(); + } + }); + + static OFStatisticsType[] requestMapping; + static OFStatisticsType[] replyMapping; + + protected Class<? extends OFStatistics> requestClass; + protected Constructor<? extends OFStatistics> requestConstructor; + protected Instantiable<OFStatistics> requestInstantiable; + protected Class<? extends OFStatistics> replyClass; + protected Constructor<? extends OFStatistics> replyConstructor; + protected Instantiable<OFStatistics> replyInstantiable; + protected short type; + + /** + * Store some information about the OpenFlow Statistic type, including wire + * protocol type number, and derived class + * + * @param type Wire protocol number associated with this OFStatisticsType + * @param requestClass The Statistics Java class to return when the + * containing OFType is STATS_REQUEST + * @param replyClass The Statistics Java class to return when the + * containing OFType is STATS_REPLY + */ + OFStatisticsType(int type, Class<? extends OFStatistics> requestClass, + Class<? extends OFStatistics> replyClass, + Instantiable<OFStatistics> requestInstantiable, + Instantiable<OFStatistics> replyInstantiable) { + this.type = (short) type; + this.requestClass = requestClass; + try { + this.requestConstructor = requestClass.getConstructor(new Class[]{}); + } catch (Exception e) { + throw new RuntimeException( + "Failure getting constructor for class: " + requestClass, e); + } + + this.replyClass = replyClass; + try { + this.replyConstructor = replyClass.getConstructor(new Class[]{}); + } catch (Exception e) { + throw new RuntimeException( + "Failure getting constructor for class: " + replyClass, e); + } + this.requestInstantiable = requestInstantiable; + this.replyInstantiable = replyInstantiable; + OFStatisticsType.addMapping(this.type, OFType.STATS_REQUEST, this); + OFStatisticsType.addMapping(this.type, OFType.STATS_REPLY, this); + } + + /** + * Adds a mapping from type value to OFStatisticsType enum + * + * @param i OpenFlow wire protocol type + * @param t type of containing OFMessage, only accepts STATS_REQUEST or + * STATS_REPLY + * @param st type + */ + static public void addMapping(short i, OFType t, OFStatisticsType st) { + if (i < 0) + i = (short) (16+i); + if (t == OFType.STATS_REQUEST) { + if (requestMapping == null) + requestMapping = new OFStatisticsType[16]; + OFStatisticsType.requestMapping[i] = st; + } else if (t == OFType.STATS_REPLY){ + if (replyMapping == null) + replyMapping = new OFStatisticsType[16]; + OFStatisticsType.replyMapping[i] = st; + } else { + throw new RuntimeException(t.toString() + " is an invalid OFType"); + } + } + + /** + * Remove a mapping from type value to OFStatisticsType enum + * + * @param i OpenFlow wire protocol type + * @param t type of containing OFMessage, only accepts STATS_REQUEST or + * STATS_REPLY + */ + static public void removeMapping(short i, OFType t) { + if (i < 0) + i = (short) (16+i); + if (t == OFType.STATS_REQUEST) { + requestMapping[i] = null; + } else if (t == OFType.STATS_REPLY){ + replyMapping[i] = null; + } else { + throw new RuntimeException(t.toString() + " is an invalid OFType"); + } + } + + /** + * Given a wire protocol OpenFlow type number, return the OFStatisticsType + * associated with it + * + * @param i wire protocol number + * @param t type of containing OFMessage, only accepts STATS_REQUEST or + * STATS_REPLY + * @return OFStatisticsType enum type + */ + static public OFStatisticsType valueOf(short i, OFType t) { + if (i < 0) + i = (short) (16+i); + if (t == OFType.STATS_REQUEST) { + return requestMapping[i]; + } else if (t == OFType.STATS_REPLY){ + return replyMapping[i]; + } else { + throw new RuntimeException(t.toString() + " is an invalid OFType"); + } + } + + /** + * @return Returns the wire protocol value corresponding to this + * OFStatisticsType + */ + public short getTypeValue() { + return this.type; + } + + /** + * @param t type of containing OFMessage, only accepts STATS_REQUEST or + * STATS_REPLY + * @return return the OFMessage subclass corresponding to this + * OFStatisticsType + */ + public Class<? extends OFStatistics> toClass(OFType t) { + if (t == OFType.STATS_REQUEST) { + return requestClass; + } else if (t == OFType.STATS_REPLY){ + return replyClass; + } else { + throw new RuntimeException(t.toString() + " is an invalid OFType"); + } + } + + /** + * Returns the no-argument Constructor of the implementation class for + * this OFStatisticsType, either request or reply based on the supplied + * OFType + * + * @param t + * @return + */ + public Constructor<? extends OFStatistics> getConstructor(OFType t) { + if (t == OFType.STATS_REQUEST) { + return requestConstructor; + } else if (t == OFType.STATS_REPLY) { + return replyConstructor; + } else { + throw new RuntimeException(t.toString() + " is an invalid OFType"); + } + } + + /** + * @return the requestInstantiable + */ + public Instantiable<OFStatistics> getRequestInstantiable() { + return requestInstantiable; + } + + /** + * @param requestInstantiable the requestInstantiable to set + */ + public void setRequestInstantiable( + Instantiable<OFStatistics> requestInstantiable) { + this.requestInstantiable = requestInstantiable; + } + + /** + * @return the replyInstantiable + */ + public Instantiable<OFStatistics> getReplyInstantiable() { + return replyInstantiable; + } + + /** + * @param replyInstantiable the replyInstantiable to set + */ + public void setReplyInstantiable(Instantiable<OFStatistics> replyInstantiable) { + this.replyInstantiable = replyInstantiable; + } + + /** + * Returns a new instance of the implementation class for + * this OFStatisticsType, either request or reply based on the supplied + * OFType + * + * @param t + * @return + */ + public OFStatistics newInstance(OFType t) { + if (t == OFType.STATS_REQUEST) { + return requestInstantiable.instantiate(); + } else if (t == OFType.STATS_REPLY) { + return replyInstantiable.instantiate(); + } else { + throw new RuntimeException(t.toString() + " is an invalid OFType"); + } + } +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFTableStatistics.java b/src/main/java/org/openflow/protocol/statistics/OFTableStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..9e6d34e10bdfa16cf7f75be64d03eab22f86b26e --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFTableStatistics.java @@ -0,0 +1,223 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.util.StringByteSerializer; + +/** + * Represents an ofp_table_stats structure + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFTableStatistics implements OFStatistics { + public static int MAX_TABLE_NAME_LEN = 32; + + protected byte tableId; + protected String name; + protected int wildcards; + protected int maximumEntries; + protected int activeCount; + protected long lookupCount; + protected long matchedCount; + + /** + * @return the tableId + */ + public byte getTableId() { + return tableId; + } + + /** + * @param tableId the tableId to set + */ + public void setTableId(byte tableId) { + this.tableId = tableId; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the wildcards + */ + public int getWildcards() { + return wildcards; + } + + /** + * @param wildcards the wildcards to set + */ + public void setWildcards(int wildcards) { + this.wildcards = wildcards; + } + + /** + * @return the maximumEntries + */ + public int getMaximumEntries() { + return maximumEntries; + } + + /** + * @param maximumEntries the maximumEntries to set + */ + public void setMaximumEntries(int maximumEntries) { + this.maximumEntries = maximumEntries; + } + + /** + * @return the activeCount + */ + public int getActiveCount() { + return activeCount; + } + + /** + * @param activeCount the activeCount to set + */ + public void setActiveCount(int activeCount) { + this.activeCount = activeCount; + } + + /** + * @return the lookupCount + */ + public long getLookupCount() { + return lookupCount; + } + + /** + * @param lookupCount the lookupCount to set + */ + public void setLookupCount(long lookupCount) { + this.lookupCount = lookupCount; + } + + /** + * @return the matchedCount + */ + public long getMatchedCount() { + return matchedCount; + } + + /** + * @param matchedCount the matchedCount to set + */ + public void setMatchedCount(long matchedCount) { + this.matchedCount = matchedCount; + } + + @Override + public int getLength() { + return 64; + } + + @Override + public void readFrom(ChannelBuffer data) { + this.tableId = data.readByte(); + data.readByte(); // pad + data.readByte(); // pad + data.readByte(); // pad + this.name = StringByteSerializer.readFrom(data, MAX_TABLE_NAME_LEN); + this.wildcards = data.readInt(); + this.maximumEntries = data.readInt(); + this.activeCount = data.readInt(); + this.lookupCount = data.readLong(); + this.matchedCount = data.readLong(); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeByte(this.tableId); + data.writeByte((byte) 0); // pad + data.writeByte((byte) 0); // pad + data.writeByte((byte) 0); // pad + StringByteSerializer.writeTo(data, MAX_TABLE_NAME_LEN, this.name); + data.writeInt(this.wildcards); + data.writeInt(this.maximumEntries); + data.writeInt(this.activeCount); + data.writeLong(this.lookupCount); + data.writeLong(this.matchedCount); + } + + @Override + public int hashCode() { + final int prime = 449; + int result = 1; + result = prime * result + activeCount; + result = prime * result + (int) (lookupCount ^ (lookupCount >>> 32)); + result = prime * result + (int) (matchedCount ^ (matchedCount >>> 32)); + result = prime * result + maximumEntries; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + tableId; + result = prime * result + wildcards; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFTableStatistics)) { + return false; + } + OFTableStatistics other = (OFTableStatistics) obj; + if (activeCount != other.activeCount) { + return false; + } + if (lookupCount != other.lookupCount) { + return false; + } + if (matchedCount != other.matchedCount) { + return false; + } + if (maximumEntries != other.maximumEntries) { + return false; + } + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (tableId != other.tableId) { + return false; + } + if (wildcards != other.wildcards) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/statistics/OFVendorStatistics.java b/src/main/java/org/openflow/protocol/statistics/OFVendorStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..0257a6aab024ef55ab6b8b7d0f80a473b806ebfc --- /dev/null +++ b/src/main/java/org/openflow/protocol/statistics/OFVendorStatistics.java @@ -0,0 +1,83 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol.statistics; + + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * The base class for vendor implemented statistics + * @author David Erickson (daviderickson@cs.stanford.edu) + */ +public class OFVendorStatistics implements OFStatistics { + protected int vendor; + protected byte[] body; + + // non-message fields + protected int length = 0; + + @Override + public void readFrom(ChannelBuffer data) { + this.vendor = data.readInt(); + if (body == null) + body = new byte[length - 4]; + data.readBytes(body); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeInt(this.vendor); + if (body != null) + data.writeBytes(body); + } + + @Override + public int hashCode() { + final int prime = 457; + int result = 1; + result = prime * result + vendor; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OFVendorStatistics)) { + return false; + } + OFVendorStatistics other = (OFVendorStatistics) obj; + if (vendor != other.vendor) { + return false; + } + return true; + } + + @Override + public int getLength() { + return length; + } + + public void setLength(int length) { + this.length = length; + } +} diff --git a/src/main/java/org/openflow/protocol/vendor/OFBasicVendorDataType.java b/src/main/java/org/openflow/protocol/vendor/OFBasicVendorDataType.java new file mode 100644 index 0000000000000000000000000000000000000000..1f0e14b2dd4b6dac9f5dadc5d5c9e6a0b4a1a4a5 --- /dev/null +++ b/src/main/java/org/openflow/protocol/vendor/OFBasicVendorDataType.java @@ -0,0 +1,71 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* 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.openflow.protocol.vendor; + +import org.openflow.protocol.Instantiable; + +/** + * Subclass of OFVendorDataType that works with any vendor data format that + * begins with a integral value to indicate the format of the remaining data. + * It maps from the per-vendor-id integral data type code to the object + * used to instantiate the class associated with that vendor data type. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFBasicVendorDataType extends OFVendorDataType { + + /** + * The data type value at the beginning of the vendor data. + */ + protected long type; + + /** + * Construct an empty (i.e. no specified data type value) vendor data type. + */ + public OFBasicVendorDataType() { + super(); + this.type = 0; + } + + /** + * Store some information about the vendor data type, including wire protocol + * type number, derived class and instantiator. + * + * @param type Wire protocol number associated with this vendor data type + * @param instantiator An Instantiator<OFVendorData> implementation that + * creates an instance of an appropriate subclass of OFVendorData. + */ + public OFBasicVendorDataType(long type, Instantiable<OFVendorData> instantiator) { + super(instantiator); + this.type = type; + } + + /** + * @return Returns the wire protocol value corresponding to this OFVendorDataType + */ + public long getTypeValue() { + return this.type; + } + + /** + * @param type the wire protocol value for this data type + */ + public void setTypeValue(long type) { + this.type = type; + } +} diff --git a/src/main/java/org/openflow/protocol/vendor/OFBasicVendorId.java b/src/main/java/org/openflow/protocol/vendor/OFBasicVendorId.java new file mode 100644 index 0000000000000000000000000000000000000000..5f789dc3f9a661c8dc84d74904b209e004bef215 --- /dev/null +++ b/src/main/java/org/openflow/protocol/vendor/OFBasicVendorId.java @@ -0,0 +1,162 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* 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.openflow.protocol.vendor; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.Instantiable; + +/** + * Basic subclass of OFVendorId that works with any vendor data format where + * the data begins with an integral data type value. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFBasicVendorId extends OFVendorId { + + /** + * The size of the data type value at the beginning of all vendor + * data associated with this vendor id. The data type size must be + * either 1, 2, 4 or 8. + */ + protected int dataTypeSize; + + /** + * Map of the vendor data types that have been registered for this + * vendor id. + */ + protected Map<Long, OFBasicVendorDataType> dataTypeMap = + new HashMap<Long, OFBasicVendorDataType>(); + + /** + * Construct an OFVendorId that where the vendor data begins + * with a data type value whose size is dataTypeSize. + * @param id the id of the vendor, typically the OUI of a vendor + * prefixed with 0. + * @param dataTypeSize the size of the integral data type value + * at the beginning of the vendor data. The value must be the + * size of an integeral data type (i.e. either 1,2,4 or 8). + */ + public OFBasicVendorId(int id, int dataTypeSize) { + super(id); + assert (dataTypeSize == 1) || (dataTypeSize == 2) || + (dataTypeSize == 4) || (dataTypeSize == 8); + this.dataTypeSize = dataTypeSize; + } + + /** + * Get the size of the data type value at the beginning of the vendor + * data. OFBasicVendorId assumes that this value is common across all of + * the vendor data formats associated with a given vendor id. + * @return + */ + public int getDataTypeSize() { + return dataTypeSize; + } + + /** + * Register a vendor data type with this vendor id. + * @param vendorDataType + */ + public void registerVendorDataType(OFBasicVendorDataType vendorDataType) { + dataTypeMap.put(vendorDataType.getTypeValue(), vendorDataType); + } + + /** + * Lookup the OFVendorDataType instance that has been registered with + * this vendor id. + * + * @param vendorDataType the integer code that was parsed from the + * @return + */ + public OFVendorDataType lookupVendorDataType(int vendorDataType) { + return dataTypeMap.get(Long.valueOf(vendorDataType)); + } + + /** + * This function parses enough of the data from the buffer to be able + * to determine the appropriate OFVendorDataType for the data. It is meant + * to be a reasonably generic implementation that will work for most + * formats of vendor extensions. If the vendor data doesn't fit the + * assumptions listed below, then this method will need to be overridden + * to implement custom parsing. + * + * This implementation assumes that the vendor data begins with a data + * type code that is used to distinguish different formats of vendor + * data associated with a particular vendor ID. + * The exact format of the data is vendor-defined, so we don't know how + * how big the code is (or really even if there is a code). This code + * assumes that the common case will be that the data does include + * an initial type code (i.e. so that the vendor can have multiple + * message/data types) and that the size is either 1, 2 or 4 bytes. + * The size of the initial type code is configured by the subclass of + * OFVendorId. + * + * @param data the channel buffer containing the vendor data. + * @param length the length to the end of the enclosing message + * @return the OFVendorDataType that can be used to instantiate the + * appropriate subclass of OFVendorData. + */ + public OFVendorDataType parseVendorDataType(ChannelBuffer data, int length) { + OFVendorDataType vendorDataType = null; + + // Parse out the type code from the vendor data. + long dataTypeValue = 0; + if ((length == 0) || (length >= dataTypeSize)) { + switch (dataTypeSize) { + case 1: + dataTypeValue = data.readByte(); + break; + case 2: + dataTypeValue = data.readShort(); + break; + case 4: + dataTypeValue = data.readInt(); + break; + case 8: + dataTypeValue = data.readLong(); + break; + default: + // This would be indicative of a coding error where the + // dataTypeSize was specified incorrectly. This should have been + // caught in the constructor for OFVendorId. + assert false; + } + + vendorDataType = dataTypeMap.get(dataTypeValue); + } + + // If we weren't able to parse/map the data to a known OFVendorDataType, + // then map it to a generic vendor data type. + if (vendorDataType == null) { + vendorDataType = new OFBasicVendorDataType(dataTypeValue, + new Instantiable<OFVendorData>() { + @Override + public OFVendorData instantiate() { + return new OFByteArrayVendorData(); + } + } + ); + } + + return vendorDataType; + } + +} diff --git a/src/main/java/org/openflow/protocol/vendor/OFByteArrayVendorData.java b/src/main/java/org/openflow/protocol/vendor/OFByteArrayVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..08fa003172312af1569daf4eb83700fca177df2e --- /dev/null +++ b/src/main/java/org/openflow/protocol/vendor/OFByteArrayVendorData.java @@ -0,0 +1,94 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* 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.openflow.protocol.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Basic implementation of OFVendorData that just treats the data as a + * byte array. This is used if there's an OFVendor message where there's + * no registered OFVendorId or no specific OFVendorDataType that can be + * determined from the data. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFByteArrayVendorData implements OFVendorData { + + protected byte[] bytes; + + /** + * Construct vendor data with an empty byte array. + */ + public OFByteArrayVendorData() { + } + + /** + * Construct vendor data with the specified byte array. + * @param bytes + */ + public OFByteArrayVendorData(byte[] bytes) { + this.bytes = bytes; + } + + /** + * Get the associated byte array for this vendor data. + * @return the byte array containing the raw vendor data. + */ + public byte[] getBytes() { + return bytes; + } + + /** + * Set the byte array for the vendor data. + * @param bytes the raw byte array containing the vendor data. + */ + public void setBytes(byte[] bytes) { + this.bytes = bytes; + } + + /** + * Get the length of the vendor data. In this case it's just then length + * of the underlying byte array. + * @return the length of the vendor data + */ + @Override + public int getLength() { + return (bytes != null) ? bytes.length : 0; + } + + /** + * Read the vendor data from the ChannelBuffer into the byte array. + * @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) { + bytes = new byte[length]; + data.readBytes(bytes); + } + + /** + * Write the vendor data bytes to the ChannelBuffer + * @param data the channel buffer to which we're serializing + */ + @Override + public void writeTo(ChannelBuffer data) { + if (bytes != null) + data.writeBytes(bytes); + } +} diff --git a/src/main/java/org/openflow/protocol/vendor/OFVendorData.java b/src/main/java/org/openflow/protocol/vendor/OFVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..6dfb4e6cb62b170e83b28678ac5c0669f3d6610a --- /dev/null +++ b/src/main/java/org/openflow/protocol/vendor/OFVendorData.java @@ -0,0 +1,44 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* 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.openflow.protocol.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * The base class for all vendor data. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public interface OFVendorData { + /** + * @return length of the data + */ + public int getLength(); + + /** + * Read the vendor data from the specified ChannelBuffer + * @param data + */ + public void readFrom(ChannelBuffer data, int length); + + /** + * Write the vendor data to the specified ChannelBuffer + * @param data + */ + public void writeTo(ChannelBuffer data); +} diff --git a/src/main/java/org/openflow/protocol/vendor/OFVendorDataType.java b/src/main/java/org/openflow/protocol/vendor/OFVendorDataType.java new file mode 100644 index 0000000000000000000000000000000000000000..ecae4823925750fc8e4f6becb9d4249b1c567f85 --- /dev/null +++ b/src/main/java/org/openflow/protocol/vendor/OFVendorDataType.java @@ -0,0 +1,79 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* 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.openflow.protocol.vendor; + +import org.openflow.protocol.Instantiable; + +/** + * Class that represents a specific vendor data type format in an + * OFVendor message. Typically the vendor data will begin with an integer + * code that determines the format of the rest of the data, but this + * class does not assume that. It's basically just a holder for an + * instantiator of the appropriate subclass of OFVendorData. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFVendorDataType { + + /** + * Object that instantiates the subclass of OFVendorData + * associated with this data type. + */ + protected Instantiable<OFVendorData> instantiable; + + /** + * Construct an empty vendor data type. + */ + public OFVendorDataType() { + super(); + } + + /** + * Construct a vendor data type with the specified instantiable. + * @param instantiable object that creates the subclass of OFVendorData + * associated with this data type. + */ + public OFVendorDataType(Instantiable<OFVendorData> instantiable) { + this.instantiable = instantiable; + } + + /** + * Returns a new instance of a subclass of OFVendorData associated with + * this OFVendorDataType. + * + * @return the new object + */ + public OFVendorData newInstance() { + return instantiable.instantiate(); + } + + /** + * @return the instantiable + */ + public Instantiable<OFVendorData> getInstantiable() { + return instantiable; + } + + /** + * @param instantiable the instantiable to set + */ + public void setInstantiable(Instantiable<OFVendorData> instantiable) { + this.instantiable = instantiable; + } + +} diff --git a/src/main/java/org/openflow/protocol/vendor/OFVendorId.java b/src/main/java/org/openflow/protocol/vendor/OFVendorId.java new file mode 100644 index 0000000000000000000000000000000000000000..f0af8a7635088c21bd889dc0632c481fb61350ab --- /dev/null +++ b/src/main/java/org/openflow/protocol/vendor/OFVendorId.java @@ -0,0 +1,85 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* 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.openflow.protocol.vendor; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Base class for the vendor ID corresponding to vendor extensions from a + * given vendor. It is responsible for knowing how to parse out some sort of + * data type value from the vendor data in an OFVendor message so that we can + * dispatch to the different subclasses of OFVendorData corresponding to the + * different formats of data for the vendor extensions. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public abstract class OFVendorId { + static Map<Integer, OFVendorId> mapping = new HashMap<Integer, OFVendorId>(); + + /** + * The vendor id value, typically the OUI of the vendor prefixed with 0. + */ + protected int id; + + /** + * Register a new vendor id. + * @param vendorId the vendor id to register + */ + public static void registerVendorId(OFVendorId vendorId) { + mapping.put(vendorId.getId(), vendorId); + } + + /** + * Lookup the OFVendorId instance corresponding to the given id value. + * @param id the integer vendor id value + * @return the corresponding OFVendorId that's been registered for the + * given value, or null if there id has not been registered. + */ + public static OFVendorId lookupVendorId(int id) { + return mapping.get(id); + } + + /** + * Create an OFVendorId with the give vendor id value + * @param id + */ + public OFVendorId(int id) { + this.id = id; + } + + /** + * @return the vendor id value + */ + public int getId() { + return id; + } + + /** + * This function parses enough of the data from the channel buffer to be + * able to determine the appropriate OFVendorDataType for the data. + * + * @param data the channel buffer containing the vendor data. + * @param length the length to the end of the enclosing message + * @return the OFVendorDataType that can be used to instantiate the + * appropriate subclass of OFVendorData. + */ + public abstract OFVendorDataType parseVendorDataType(ChannelBuffer data, int length); +} diff --git a/src/main/java/org/openflow/util/HexString.java b/src/main/java/org/openflow/util/HexString.java new file mode 100644 index 0000000000000000000000000000000000000000..b5628242dadcbc7a12ccd8568f9c00b319439783 --- /dev/null +++ b/src/main/java/org/openflow/util/HexString.java @@ -0,0 +1,93 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +import java.math.BigInteger; + +public class HexString { + /** + * Convert a string of bytes to a ':' separated hex string + * @param bytes + * @return "0f:ca:fe:de:ad:be:ef" + */ + public static String toHexString(byte[] bytes) { + if (bytes == null) return ""; + int i; + String ret = ""; + String tmp; + for(i=0; i< bytes.length; i++) { + if(i> 0) + ret += ":"; + tmp = Integer.toHexString(U8.f(bytes[i])); + if (tmp.length() == 1) + ret += "0"; + ret += tmp; + } + return ret; + } + + public static String toHexString(long val, int padTo) { + char arr[] = Long.toHexString(val).toCharArray(); + String ret = ""; + // prepend the right number of leading zeros + int i = 0; + for (; i < (padTo * 2 - arr.length); i++) { + ret += "0"; + if ((i % 2) != 0) + ret += ":"; + } + for (int j = 0; j < arr.length; j++) { + ret += arr[j]; + if ((((i + j) % 2) != 0) && (j < (arr.length - 1))) + ret += ":"; + } + return ret; + } + + public static String toHexString(long val) { + return toHexString(val, 8); + } + + + /** + * Convert a string of hex values into a string of bytes + * @param values "0f:ca:fe:de:ad:be:ef" + * @return [15, 5 ,2, 5, 17] + * @throws NumberFormatException If the string can not be parsed + */ + public static byte[] fromHexString(String values) throws NumberFormatException { + String[] octets = values.split(":"); + byte[] ret = new byte[octets.length]; + + for(int i = 0; i < octets.length; i++) { + if (octets[i].length() > 2) + throw new NumberFormatException("Invalid octet length"); + ret[i] = Integer.valueOf(octets[i], 16).byteValue(); + } + return ret; + } + + public static long toLong(String values) throws NumberFormatException { + // Long.parseLong() can't handle HexStrings with MSB set. Sigh. + BigInteger bi = new BigInteger(values.replaceAll(":", ""),16); + if (bi.bitLength() > 64) + throw new NumberFormatException("Input string too big to fit in long: " + values); + return bi.longValue(); + } + +} diff --git a/src/main/java/org/openflow/util/IProducer.java b/src/main/java/org/openflow/util/IProducer.java new file mode 100644 index 0000000000000000000000000000000000000000..52ae79a52b5dc47af63f71d0aceeb3ee26105727 --- /dev/null +++ b/src/main/java/org/openflow/util/IProducer.java @@ -0,0 +1,9 @@ +package org.openflow.util; + +public interface IProducer { + + public void registerConsumer(Class<?> iface, Object anObj); + + public void deregisterConsumer(Class<?> iface, Object anObj); + +} diff --git a/src/main/java/org/openflow/util/LRULinkedHashMap.java b/src/main/java/org/openflow/util/LRULinkedHashMap.java new file mode 100644 index 0000000000000000000000000000000000000000..7f05381c20bbe04760714cd8a45f3d50907c1558 --- /dev/null +++ b/src/main/java/org/openflow/util/LRULinkedHashMap.java @@ -0,0 +1,42 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +import java.util.LinkedHashMap; + +public class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> { + private static final long serialVersionUID = -2964986094089626647L; + protected int maximumCapacity; + + public LRULinkedHashMap(int initialCapacity, int maximumCapacity) { + super(initialCapacity, 0.75f, true); + this.maximumCapacity = maximumCapacity; + } + + public LRULinkedHashMap(int maximumCapacity) { + super(16, 0.75f, true); + this.maximumCapacity = maximumCapacity; + } + + @Override + protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) { + if (this.size() > maximumCapacity) + return true; + return false; + } +} diff --git a/src/main/java/org/openflow/util/ProducerConsumer.java b/src/main/java/org/openflow/util/ProducerConsumer.java new file mode 100644 index 0000000000000000000000000000000000000000..f2244ef3524483b0a2fc70e8f2ba433efd363c01 --- /dev/null +++ b/src/main/java/org/openflow/util/ProducerConsumer.java @@ -0,0 +1,223 @@ +package org.openflow.util; + +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; + +/** + * The following implement a producer/consumer design pattern in which both + * producers and consumers explicitly employ a centralized registration + * mechanism, and java Interfaces are used as contracts.<br> + */ +public class ProducerConsumer { + + /* + * Class variables + */ + protected static ProducerConsumer singleton; + + /* + * Default constructor + */ + protected ProducerConsumer() { + producerMap = new Hashtable<Class<?>, Set<IProducer>>(); + } + + /* + * Instance variables + */ + + // Interface/IProducer map + protected Map<Class<?>, Set<IProducer>> producerMap; + + /* + * Protected methods + */ + + protected void _registerConsumer(Object consumer, Class<?>[] interfaces, + Set<Class<?>> iSet, + Set<Class<?>> iUniqueSet) { + // *...Process all interfaces...*/ + for (Class<?> iface : interfaces) { + + // *...Protect against repeated interfaces...*/ + if (!iUniqueSet.contains(iface)) { + iUniqueSet.add(iface); + + Set<IProducer> producers = producerMap.get(iface); + + if (producers != null) { + for (IProducer producer : producers) + producer.registerConsumer(iface, consumer); + iSet.add(iface); + } + + // *...Recurse...*/ + _registerConsumer(consumer, iface.getInterfaces(), iSet, + iUniqueSet); + } + } + } + + protected void _registerConsumer(Object consumer, Class<?> clazz, + Set<Class<?>> iSet, + Set<Class<?>> iUniqueSet) { + if (clazz != null) { + // *...Process all interfaces...*/ + _registerConsumer(consumer, clazz.getInterfaces(), iSet, + iUniqueSet); + + // *...Recurse the class hierarchy...*/ + _registerConsumer(consumer, clazz.getSuperclass(), iSet, + iUniqueSet); + } + } + + protected int _deregisterConsumer(Object consumer, + Class<?>[] interfaces, + Set<Class<?>> iUniqueSet) { + int count = 0; + + // *...Process all interfaces...*/ + for (Class<?> iface : interfaces) { + + // *...Protect against repeated interfaces...*/ + if (!iUniqueSet.contains(iface)) { + iUniqueSet.add(iface); + + Set<IProducer> producers = producerMap.get(iface); + + if (producers != null) { + for (IProducer producer : producers) + producer.deregisterConsumer(iface, consumer); + + count++; + } + + // *...Recurse...*/ + count += _deregisterConsumer(consumer, + iface.getInterfaces(), + iUniqueSet); + } + } + + return count; + } + + protected int _deregisterConsumer(Object consumer, Class<?> clazz, + Set<Class<?>> iUniqueSet) { + int count = 0; + + if (clazz != null) { + // *...Process all interfaces...*/ + count += _deregisterConsumer(consumer, clazz.getInterfaces(), + iUniqueSet); + + // *...Recurse the class hierarchy...*/ + count += _deregisterConsumer(consumer, clazz.getSuperclass(), + iUniqueSet); + } + + return count; + } + + /* + * Singleton API + */ + + /** + * @return singleton ProducerConsumer + */ + public static synchronized ProducerConsumer getSingleton() { + if (singleton == null) singleton = new ProducerConsumer(); + + return singleton; + } + + /* + * Producer APIs + */ + + /** + * Producer registration + * + * @param producer + * object that implements IProducer + * @param iface + * interface supported by the producer + * @return whether there was a previously registered producer, or true if + * one or more the arguments were invalid + */ + public boolean registerProducer(IProducer producer, Class<?> iface) { + if (producer != null && iface != null && iface.isInterface()) { + Set<IProducer> producers = producerMap.get(iface); + + if (producers == null) { + producers = new HashSet<IProducer>(); + producerMap.put(iface, producers); + } + + return producers.add(producer); + } else + return true; + } + + /** + * Producer deregistration + * + * @param producer + * object that implements IProducer + * @param iface + * interface supported by the producer + * @return whether the interface/producer pair was removed, or false if one + * or more the arguments were invalid + */ + public boolean deregisterProducer(IProducer producer, Class<?> iface) { + if (producer != null && iface != null && iface.isInterface()) { + Set<IProducer> producers = producerMap.get(iface); + + if (producers != null) return producers.remove(producer); + } + + return false; + } + + /* + * Consumer APIs + */ + + /** + * Consumer registration + * + * @param consumer + * object that implements producer-specific interfaces + * @return set of supported interfaces + */ + public Set<Class<?>> registerConsumer(Object consumer) { + Set<Class<?>> iSet = new HashSet<Class<?>>(); + + if (consumer != null) + _registerConsumer(consumer, + consumer.getClass(), iSet, + new HashSet<Class<?>>()); + + return iSet; + } + + /** + * Consumer deregistration + * + * @param consumer + * object to deregister + * @return number of unregistered interfaces + */ + public int deregisterConsumer(Object consumer) { + if (consumer != null) + return _deregisterConsumer(consumer, consumer.getClass(), + new HashSet<Class<?>>()); + else + return 0; + } + +} diff --git a/src/main/java/org/openflow/util/StringByteSerializer.java b/src/main/java/org/openflow/util/StringByteSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..9287fd20a73464d2483e42a6a4cc60781c45f5bb --- /dev/null +++ b/src/main/java/org/openflow/util/StringByteSerializer.java @@ -0,0 +1,58 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; + +public class StringByteSerializer { + public static String readFrom(ChannelBuffer data, int length) { + byte[] stringBytes = new byte[length]; + data.readBytes(stringBytes); + // find the first index of 0 + int index = 0; + for (byte b : stringBytes) { + if (0 == b) + break; + ++index; + } + return new String(Arrays.copyOf(stringBytes, index), + Charset.forName("ascii")); + } + + public static void writeTo(ChannelBuffer data, int length, String value) { + try { + byte[] name = value.getBytes("ASCII"); + if (name.length < length) { + data.writeBytes(name); + for (int i = name.length; i < length; ++i) { + data.writeByte((byte) 0); + } + } else { + data.writeBytes(name, 0, length-1); + data.writeByte((byte) 0); + } + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + + } +} diff --git a/src/main/java/org/openflow/util/U16.java b/src/main/java/org/openflow/util/U16.java new file mode 100644 index 0000000000000000000000000000000000000000..0d8917da5fd1c0f18e287d5506904102be196161 --- /dev/null +++ b/src/main/java/org/openflow/util/U16.java @@ -0,0 +1,28 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +public class U16 { + public static int f(short i) { + return (int)i & 0xffff; + } + + public static short t(int l) { + return (short) l; + } +} diff --git a/src/main/java/org/openflow/util/U32.java b/src/main/java/org/openflow/util/U32.java new file mode 100644 index 0000000000000000000000000000000000000000..3aab400ebf128079673ad1e98f7b1a1b484bd4aa --- /dev/null +++ b/src/main/java/org/openflow/util/U32.java @@ -0,0 +1,28 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +public class U32 { + public static long f(int i) { + return (long)i & 0xffffffffL; + } + + public static int t(long l) { + return (int) l; + } +} diff --git a/src/main/java/org/openflow/util/U64.java b/src/main/java/org/openflow/util/U64.java new file mode 100644 index 0000000000000000000000000000000000000000..c6ae0f7b5c4e7f31521b03c84254502c43542e5d --- /dev/null +++ b/src/main/java/org/openflow/util/U64.java @@ -0,0 +1,30 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +import java.math.BigInteger; + +public class U64 { + public static BigInteger f(long i) { + return new BigInteger(Long.toBinaryString(i), 2); + } + + public static long t(BigInteger l) { + return l.longValue(); + } +} diff --git a/src/main/java/org/openflow/util/U8.java b/src/main/java/org/openflow/util/U8.java new file mode 100644 index 0000000000000000000000000000000000000000..0b575ad24e4d0d0abe433ae49f8725bd96c5fa39 --- /dev/null +++ b/src/main/java/org/openflow/util/U8.java @@ -0,0 +1,28 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +public class U8 { + public static short f(byte i) { + return (short) ((short)i & 0xff); + } + + public static byte t(short l) { + return (byte) l; + } +} diff --git a/src/main/java/org/openflow/util/Unsigned.java b/src/main/java/org/openflow/util/Unsigned.java new file mode 100644 index 0000000000000000000000000000000000000000..0754a3f73d751e0296ecd95d6671ec2db7c78dff --- /dev/null +++ b/src/main/java/org/openflow/util/Unsigned.java @@ -0,0 +1,212 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +import java.math.BigInteger; +import java.nio.ByteBuffer; + +/***** + * A util library class for dealing with the lack of unsigned datatypes in Java + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * @author David Erickson (daviderickson@cs.stanford.edu) + */ + +public class Unsigned { + /** + * Get an unsigned byte from the current position of the ByteBuffer + * + * @param bb ByteBuffer to get the byte from + * @return an unsigned byte contained in a short + */ + public static short getUnsignedByte(ByteBuffer bb) { + return ((short) (bb.get() & (short) 0xff)); + } + + /** + * Get an unsigned byte from the specified offset in the ByteBuffer + * + * @param bb ByteBuffer to get the byte from + * @param offset the offset to get the byte from + * @return an unsigned byte contained in a short + */ + public static short getUnsignedByte(ByteBuffer bb, int offset) { + return ((short) (bb.get(offset) & (short) 0xff)); + } + + /** + * Put an unsigned byte into the specified ByteBuffer at the current + * position + * + * @param bb ByteBuffer to put the byte into + * @param v the short containing the unsigned byte + */ + public static void putUnsignedByte(ByteBuffer bb, short v) { + bb.put((byte) (v & 0xff)); + } + + /** + * Put an unsigned byte into the specified ByteBuffer at the specified + * offset + * + * @param bb ByteBuffer to put the byte into + * @param v the short containing the unsigned byte + * @param offset the offset to insert the unsigned byte at + */ + public static void putUnsignedByte(ByteBuffer bb, short v, int offset) { + bb.put(offset, (byte) (v & 0xff)); + } + + /** + * Get an unsigned short from the current position of the ByteBuffer + * + * @param bb ByteBuffer to get the byte from + * @return an unsigned short contained in a int + */ + public static int getUnsignedShort(ByteBuffer bb) { + return (bb.getShort() & 0xffff); + } + + /** + * Get an unsigned short from the specified offset in the ByteBuffer + * + * @param bb ByteBuffer to get the short from + * @param offset the offset to get the short from + * @return an unsigned short contained in a int + */ + public static int getUnsignedShort(ByteBuffer bb, int offset) { + return (bb.getShort(offset) & 0xffff); + } + + /** + * Put an unsigned short into the specified ByteBuffer at the current + * position + * + * @param bb ByteBuffer to put the short into + * @param v the int containing the unsigned short + */ + public static void putUnsignedShort(ByteBuffer bb, int v) { + bb.putShort((short) (v & 0xffff)); + } + + /** + * Put an unsigned short into the specified ByteBuffer at the specified + * offset + * + * @param bb ByteBuffer to put the short into + * @param v the int containing the unsigned short + * @param offset the offset to insert the unsigned short at + */ + public static void putUnsignedShort(ByteBuffer bb, int v, int offset) { + bb.putShort(offset, (short) (v & 0xffff)); + } + + /** + * Get an unsigned int from the current position of the ByteBuffer + * + * @param bb ByteBuffer to get the int from + * @return an unsigned int contained in a long + */ + public static long getUnsignedInt(ByteBuffer bb) { + return ((long) bb.getInt() & 0xffffffffL); + } + + /** + * Get an unsigned int from the specified offset in the ByteBuffer + * + * @param bb ByteBuffer to get the int from + * @param offset the offset to get the int from + * @return an unsigned int contained in a long + */ + public static long getUnsignedInt(ByteBuffer bb, int offset) { + return ((long) bb.getInt(offset) & 0xffffffffL); + } + + /** + * Put an unsigned int into the specified ByteBuffer at the current position + * + * @param bb ByteBuffer to put the int into + * @param v the long containing the unsigned int + */ + public static void putUnsignedInt(ByteBuffer bb, long v) { + bb.putInt((int) (v & 0xffffffffL)); + } + + /** + * Put an unsigned int into the specified ByteBuffer at the specified offset + * + * @param bb ByteBuffer to put the int into + * @param v the long containing the unsigned int + * @param offset the offset to insert the unsigned int at + */ + public static void putUnsignedInt(ByteBuffer bb, long v, int offset) { + bb.putInt(offset, (int) (v & 0xffffffffL)); + } + + /** + * Get an unsigned long from the current position of the ByteBuffer + * + * @param bb ByteBuffer to get the long from + * @return an unsigned long contained in a BigInteger + */ + public static BigInteger getUnsignedLong(ByteBuffer bb) { + byte[] v = new byte[8]; + for (int i = 0; i < 8; ++i) { + v[i] = bb.get(i); + } + return new BigInteger(1, v); + } + + /** + * Get an unsigned long from the specified offset in the ByteBuffer + * + * @param bb ByteBuffer to get the long from + * @param offset the offset to get the long from + * @return an unsigned long contained in a BigInteger + */ + public static BigInteger getUnsignedLong(ByteBuffer bb, int offset) { + byte[] v = new byte[8]; + for (int i = 0; i < 8; ++i) { + v[i] = bb.get(offset+i); + } + return new BigInteger(1, v); + } + + /** + * Put an unsigned long into the specified ByteBuffer at the current + * position + * + * @param bb ByteBuffer to put the long into + * @param v the BigInteger containing the unsigned long + */ + public static void putUnsignedLong(ByteBuffer bb, BigInteger v) { + bb.putLong(v.longValue()); + } + + /** + * Put an unsigned long into the specified ByteBuffer at the specified + * offset + * + * @param bb ByteBuffer to put the long into + * @param v the BigInteger containing the unsigned long + * @param offset the offset to insert the unsigned long at + */ + public static void putUnsignedLong(ByteBuffer bb, BigInteger v, int offset) { + bb.putLong(offset, v.longValue()); + } +} diff --git a/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorData.java b/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..687d5449f2dfdce8a4200f4c70c0f28ac72b67fe --- /dev/null +++ b/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorData.java @@ -0,0 +1,98 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* 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.openflow.vendor.nicira; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Base class for vendor data corresponding to a Nicira vendor extension. + * Nicira vendor data always starts with a 4-byte integer data type value. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFNiciraVendorData implements OFVendorData { + + public static final int NX_VENDOR_ID = 0x00002320; + /** + * The value of the integer data type at the beginning of the vendor data + */ + protected int dataType; + + /** + * Construct empty (i.e. unspecified data type) Nicira vendor data. + */ + public OFNiciraVendorData() { + } + + /** + * Contruct Nicira vendor data with the specified data type + * @param dataType the data type value at the beginning of the vendor data. + */ + public OFNiciraVendorData(int dataType) { + this.dataType = dataType; + } + + /** + * Get the data type value at the beginning of the vendor data + * @return the integer data type value + */ + public int getDataType() { + return dataType; + } + + /** + * Set the data type value + * @param dataType the integer data type value at the beginning of the + * vendor data. + */ + 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/org/openflow/vendor/nicira/OFNiciraVendorExtensions.java b/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorExtensions.java new file mode 100644 index 0000000000000000000000000000000000000000..98f88b242f62a34a3ff4e45b9197f8da58ecc9ec --- /dev/null +++ b/src/main/java/org/openflow/vendor/nicira/OFNiciraVendorExtensions.java @@ -0,0 +1,30 @@ +package org.openflow.vendor.nicira; + +import org.openflow.protocol.vendor.OFBasicVendorDataType; +import org.openflow.protocol.vendor.OFBasicVendorId; +import org.openflow.protocol.vendor.OFVendorId; + +public class OFNiciraVendorExtensions { + private static boolean initialized = false; + + public static synchronized void initialize() { + if (initialized) + return; + + // Configure openflowj to be able to parse the role request/reply + // vendor messages. + OFBasicVendorId niciraVendorId = + new OFBasicVendorId(OFNiciraVendorData.NX_VENDOR_ID, 4); + OFVendorId.registerVendorId(niciraVendorId); + OFBasicVendorDataType roleRequestVendorData = + new OFBasicVendorDataType(OFRoleRequestVendorData.NXT_ROLE_REQUEST, + OFRoleRequestVendorData.getInstantiable()); + niciraVendorId.registerVendorDataType(roleRequestVendorData); + OFBasicVendorDataType roleReplyVendorData = + new OFBasicVendorDataType(OFRoleReplyVendorData.NXT_ROLE_REPLY, + OFRoleReplyVendorData.getInstantiable()); + niciraVendorId.registerVendorDataType(roleReplyVendorData); + + initialized = true; + } +} diff --git a/src/main/java/org/openflow/vendor/nicira/OFRoleReplyVendorData.java b/src/main/java/org/openflow/vendor/nicira/OFRoleReplyVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..fa28c71fb5df6b9a083439f01cec911a1c5f0e69 --- /dev/null +++ b/src/main/java/org/openflow/vendor/nicira/OFRoleReplyVendorData.java @@ -0,0 +1,66 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* 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.openflow.vendor.nicira; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Subclass of OFVendorData representing the vendor data associated with + * a role reply vendor extension. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFRoleReplyVendorData extends OFRoleVendorData { + + protected static Instantiable<OFVendorData> instantiable = + new Instantiable<OFVendorData>() { + public OFVendorData instantiate() { + return new OFRoleReplyVendorData(); + } + }; + + /** + * @return a subclass of Instantiable<OFVendorData> that instantiates + * an instance of OFRoleReplyVendorData. + */ + public static Instantiable<OFVendorData> getInstantiable() { + return instantiable; + } + + /** + * The data type value for a role reply + */ + public static final int NXT_ROLE_REPLY = 11; + + /** + * Construct a role reply vendor data with an unspecified role value. + */ + public OFRoleReplyVendorData() { + super(NXT_ROLE_REPLY); + } + + /** + * Construct a role reply vendor data with the specified role value. + * @param role the role value for the role reply. Should be one of + * NX_ROLE_OTHER, NX_ROLE_MASTER or NX_ROLE_SLAVE. + */ + public OFRoleReplyVendorData(int role) { + super(NXT_ROLE_REPLY, role); + } +} diff --git a/src/main/java/org/openflow/vendor/nicira/OFRoleRequestVendorData.java b/src/main/java/org/openflow/vendor/nicira/OFRoleRequestVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..e7dbe71bda712da557d18678032e9267ba80d308 --- /dev/null +++ b/src/main/java/org/openflow/vendor/nicira/OFRoleRequestVendorData.java @@ -0,0 +1,66 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* 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.openflow.vendor.nicira; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Subclass of OFVendorData representing the vendor data associated with + * a role request vendor extension. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFRoleRequestVendorData extends OFRoleVendorData { + + protected static Instantiable<OFVendorData> instantiable = + new Instantiable<OFVendorData>() { + public OFVendorData instantiate() { + return new OFRoleRequestVendorData(); + } + }; + + /** + * @return a subclass of Instantiable<OFVendorData> that instantiates + * an instance of OFRoleRequestVendorData. + */ + public static Instantiable<OFVendorData> getInstantiable() { + return instantiable; + } + + /** + * The data type value for a role request + */ + public static final int NXT_ROLE_REQUEST = 10; + + /** + * Construct a role request vendor data with an unspecified role value. + */ + public OFRoleRequestVendorData() { + super(NXT_ROLE_REQUEST); + } + + /** + * Construct a role request vendor data with the specified role value. + * @param role the role value for the role request. Should be one of + * NX_ROLE_OTHER, NX_ROLE_MASTER or NX_ROLE_SLAVE. + */ + public OFRoleRequestVendorData(int role) { + super(NXT_ROLE_REQUEST, role); + } +} diff --git a/src/main/java/org/openflow/vendor/nicira/OFRoleVendorData.java b/src/main/java/org/openflow/vendor/nicira/OFRoleVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..e7c8bf2135b875a74bd14c0ad5393529a3e6e53b --- /dev/null +++ b/src/main/java/org/openflow/vendor/nicira/OFRoleVendorData.java @@ -0,0 +1,113 @@ +/** +* Copyright 2011, Big Switch Networks, Inc. +* Originally created by David Erickson & Rob Sherwood, Stanford University +* +* 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.openflow.vendor.nicira; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Class that represents the vendor data in the role request + * extension implemented by Open vSwitch to support high availability. + * + * @author Rob Vaterlaus (rob.vaterlaus@bigswitch.com) + */ +public class OFRoleVendorData extends OFNiciraVendorData { + + /** + * Role value indicating that the controller is in the OTHER role. + */ + public static final int NX_ROLE_OTHER = 0; + + /** + * Role value indicating that the controller is in the MASTER role. + */ + public static final int NX_ROLE_MASTER = 1; + + /** + * Role value indicating that the controller is in the SLAVE role. + */ + public static final int NX_ROLE_SLAVE = 2; + + protected int role; + + /** + * Construct an uninitialized OFRoleVendorData + */ + public OFRoleVendorData() { + super(); + } + + /** + * Construct an OFRoleVendorData with the specified data type + * (i.e. either request or reply) and an unspecified role. + * @param dataType + */ + public OFRoleVendorData(int dataType) { + super(dataType); + } + + /** + * Construct an OFRoleVendorData with the specified data type + * (i.e. either request or reply) and role (i.e. one of of + * master, slave, or other). + * @param dataType either role request or role reply data type + */ + public OFRoleVendorData(int dataType, int role) { + super(dataType); + this.role = role; + } + /** + * @return the role value of the role vendor data + */ + public int getRole() { + return role; + } + + /** + * @param role the role value of the role vendor data + */ + public void setRole(int role) { + this.role = role; + } + + /** + * @return the total length of the role vendor data + */ + @Override + public int getLength() { + return super.getLength() + 4; + } + + /** + * Read the role 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 + */ + public void readFrom(ChannelBuffer data, int length) { + super.readFrom(data, length); + role = data.readInt(); + } + + /** + * Write the role vendor data to the ChannelBuffer + * @param data the channel buffer to which we're serializing + */ + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(role); + } +} diff --git a/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorData.java b/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..9be821f2ace4fb624a0a2314e0b997ec9d086f14 --- /dev/null +++ b/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorData.java @@ -0,0 +1,98 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* 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.openflow.vendor.openflow; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Base class for vendor data corresponding to extensions to OpenFlow 1.0. + * Based on org.openflow.vendor.nicira + * + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFOpenFlowVendorData implements OFVendorData { + + public static final int OF_VENDOR_ID = 0x000026e1; + + /** + * The value of the integer data type at the beginning of the vendor data + */ + protected int dataType; + + /** + * Construct empty (i.e. unspecified data type) OpenFlow vendor data. + */ + public OFOpenFlowVendorData() { + } + + /** + * Construct OpenFlow vendor data with the specified data type + * @param dataType the data type value at the beginning of the vendor data. + */ + public OFOpenFlowVendorData(int dataType) { + this.dataType = dataType; + } + + /** + * Get the data type value at the beginning of the vendor data + * @return the integer data type value + */ + public int getDataType() { + return dataType; + } + + /** + * Set the data type value + * @param dataType the integer data type value at the beginning of the + * vendor data. + */ + 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/org/openflow/vendor/openflow/OFOpenFlowVendorExtensions.java b/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorExtensions.java new file mode 100644 index 0000000000000000000000000000000000000000..3fa10298cbc0ad4307368983cd157e51116e20ef --- /dev/null +++ b/src/main/java/org/openflow/vendor/openflow/OFOpenFlowVendorExtensions.java @@ -0,0 +1,47 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* 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.openflow.vendor.openflow; + +import org.openflow.protocol.vendor.OFBasicVendorDataType; +import org.openflow.protocol.vendor.OFBasicVendorId; +import org.openflow.protocol.vendor.OFVendorId; + +public class OFOpenFlowVendorExtensions { + private static boolean initialized = false; + + public static synchronized void initialize() { + if (initialized) + return; + + // Configure openflowj to be able to parse the OpenFlow extensions. + OFBasicVendorId openflowVendorId = + new OFBasicVendorId(OFOpenFlowVendorData.OF_VENDOR_ID, 4); + OFVendorId.registerVendorId(openflowVendorId); + + OFBasicVendorDataType queueModifyVendorData = + new OFBasicVendorDataType(OFQueueModifyVendorData.OFP_EXT_QUEUE_MODIFY, + OFQueueModifyVendorData.getInstantiable()); + openflowVendorId.registerVendorDataType(queueModifyVendorData); + + OFBasicVendorDataType queueDeleteVendorData = + new OFBasicVendorDataType(OFQueueDeleteVendorData.OFP_EXT_QUEUE_DELETE, + OFQueueModifyVendorData.getInstantiable()); + openflowVendorId.registerVendorDataType(queueDeleteVendorData); + + initialized = true; + } +} diff --git a/src/main/java/org/openflow/vendor/openflow/OFQueueDeleteVendorData.java b/src/main/java/org/openflow/vendor/openflow/OFQueueDeleteVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..4fc52bacb337733d19647ab89f3ee04f553ef018 --- /dev/null +++ b/src/main/java/org/openflow/vendor/openflow/OFQueueDeleteVendorData.java @@ -0,0 +1,52 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* 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.openflow.vendor.openflow; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Class that represents the vendor data in the queue delete request + * + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFQueueDeleteVendorData extends OFQueueVendorData { + + protected static Instantiable<OFVendorData> instantiable = + new Instantiable<OFVendorData>() { + public OFVendorData instantiate() { + return new OFQueueDeleteVendorData(); + } + }; + + /** + * @return a subclass of Instantiable<OFVendorData> that instantiates + * an instance of OFQueueDeleteVendorData. + */ + public static Instantiable<OFVendorData> getInstantiable() { + return instantiable; + } + + /** + * The data type value for a queue delete request + */ + public static final int OFP_EXT_QUEUE_DELETE = 1; + + public OFQueueDeleteVendorData() { + super(OFP_EXT_QUEUE_DELETE); + } +} diff --git a/src/main/java/org/openflow/vendor/openflow/OFQueueModifyVendorData.java b/src/main/java/org/openflow/vendor/openflow/OFQueueModifyVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..0d2f31b524c22f2e1152735eb5db26d8ae210914 --- /dev/null +++ b/src/main/java/org/openflow/vendor/openflow/OFQueueModifyVendorData.java @@ -0,0 +1,52 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* 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.openflow.vendor.openflow; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Class that represents the vendor data in the queue modify request + * + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFQueueModifyVendorData extends OFQueueVendorData { + + protected static Instantiable<OFVendorData> instantiable = + new Instantiable<OFVendorData>() { + public OFVendorData instantiate() { + return new OFQueueModifyVendorData(); + } + }; + + /** + * @return a subclass of Instantiable<OFVendorData> that instantiates + * an instance of OFQueueModifyVendorData. + */ + public static Instantiable<OFVendorData> getInstantiable() { + return instantiable; + } + + /** + * The data type value for a queue modify request + */ + public static final int OFP_EXT_QUEUE_MODIFY = 0; + + public OFQueueModifyVendorData() { + super(OFP_EXT_QUEUE_MODIFY); + } +} diff --git a/src/main/java/org/openflow/vendor/openflow/OFQueueVendorData.java b/src/main/java/org/openflow/vendor/openflow/OFQueueVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..eeae9aace6436f42de2c26151aba1f125e26d83b --- /dev/null +++ b/src/main/java/org/openflow/vendor/openflow/OFQueueVendorData.java @@ -0,0 +1,119 @@ +/** +* Copyright 2012, Andrew Ferguson, Brown University +* +* 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.openflow.vendor.openflow; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.OFPacketQueue; + +/** + * Class that represents the vendor data in a queue modify or delete request + * + * @author Andrew Ferguson (adf@cs.brown.edu) + */ +public class OFQueueVendorData extends OFOpenFlowVendorData { + public static int MINIMUM_LENGTH = 8; + + protected short portNumber; + protected List<OFPacketQueue> queues = new ArrayList<OFPacketQueue>(); + + public OFQueueVendorData(int dataType) { + super(dataType); + } + + /** + * @return the portNumber + */ + public short getPortNumber() { + return portNumber; + } + + /** + * @param port the port on which the queue is + */ + public void setPortNumber(short portNumber) { + this.portNumber = portNumber; + } + + + /** + * @return the queues + */ + public List<OFPacketQueue> getQueues() { + return queues; + } + + /** + * @param queues the queues to modify or delete + */ + public void setQueues(List<OFPacketQueue> queues) { + this.queues = queues; + } + + /** + * @return the total length of the queue modify or delete msg + */ + @Override + public int getLength() { + int queuesLength = 0; + + for (OFPacketQueue queue : queues) { + queuesLength += queue.getLength(); + } + + return super.getLength() + MINIMUM_LENGTH + queuesLength; + } + + /** + * Read the queue message 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 + */ + public void readFrom(ChannelBuffer data, int length) { + super.readFrom(data, length); + portNumber = data.readShort(); + data.readInt(); // pad + data.readShort(); // pad + + int availLength = (length - MINIMUM_LENGTH); + this.queues.clear(); + + while (availLength > 0) { + OFPacketQueue queue = new OFPacketQueue(); + queue.readFrom(data); + queues.add(queue); + availLength -= queue.getLength(); + } + } + + /** + * Write the queue message data to the ChannelBuffer + * @param data the channel buffer to which we're serializing + */ + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.portNumber); + data.writeInt(0); // pad + data.writeShort(0); // pad + + for (OFPacketQueue queue : queues) { + queue.writeTo(data); + } + } +} diff --git a/src/main/java/org/sdnplatform/sync/ISyncService.java b/src/main/java/org/sdnplatform/sync/ISyncService.java index 48feead4a3c378e61147292203db3ccaa6435900..75f3948a8e7b20d669492573b3dc616f8d9c54a7 100644 --- a/src/main/java/org/sdnplatform/sync/ISyncService.java +++ b/src/main/java/org/sdnplatform/sync/ISyncService.java @@ -40,8 +40,6 @@ public interface ISyncService extends IFloodlightService { */ UNSYNCHRONIZED } - - public short getLocalNodeId(); /** * Create a store with the given store name and scope diff --git a/src/main/java/org/sdnplatform/sync/internal/SyncTorture.java b/src/main/java/org/sdnplatform/sync/internal/SyncTorture.java new file mode 100644 index 0000000000000000000000000000000000000000..74e4ae96648089a28dd38758b817886a01f57f63 --- /dev/null +++ b/src/main/java/org/sdnplatform/sync/internal/SyncTorture.java @@ -0,0 +1,196 @@ +package org.sdnplatform.sync.internal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.sdnplatform.sync.IStoreClient; +import org.sdnplatform.sync.ISyncService; +import org.sdnplatform.sync.Versioned; +import org.sdnplatform.sync.ISyncService.Scope; +import org.sdnplatform.sync.error.SyncException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +import net.floodlightcontroller.core.module.FloodlightModuleContext; +import net.floodlightcontroller.core.module.FloodlightModuleException; +import net.floodlightcontroller.core.module.IFloodlightModule; +import net.floodlightcontroller.core.module.IFloodlightService; +import net.floodlightcontroller.debugcounter.IDebugCounterService; + +/** + * A floodlight module that will start up and start doing horrible, + * horrible things to the sync service. + * @author readams + */ +public class SyncTorture implements IFloodlightModule { + protected static final Logger logger = + LoggerFactory.getLogger(SyncTorture.class); + + private static final String SYNC_STORE_NAME = + SyncTorture.class.getCanonicalName() + ".torture"; + + ISyncService syncService; + IDebugCounterService debugCounter; + + int numWorkers = 2; + int keysPerWorker = 1024*1024; + int iterations = 0; + int delay = 0; + + @Override + public Collection<Class<? extends IFloodlightService>> + getModuleServices() { + return null; + } + + @Override + public Map<Class<? extends IFloodlightService>, IFloodlightService> + getServiceImpls() { + return null; + } + + @Override + public Collection<Class<? extends IFloodlightService>> + getModuleDependencies() { + Collection<Class<? extends IFloodlightService>> l = + new ArrayList<Class<? extends IFloodlightService>>(); + l.add(ISyncService.class); + l.add(IDebugCounterService.class); + + return l; + } + + @Override + public void init(FloodlightModuleContext context) + throws FloodlightModuleException { + syncService = context.getServiceImpl(ISyncService.class); + debugCounter = context.getServiceImpl(IDebugCounterService.class); + + try { + syncService.registerStore(SYNC_STORE_NAME, Scope.GLOBAL); + } catch (SyncException e) { + throw new FloodlightModuleException(e); + } + + Map<String,String> config = context.getConfigParams(this); + if (config.containsKey("numWorkers")) { + numWorkers = Integer.parseInt(config.get("numWorkers")); + } + if (config.containsKey("keysPerWorker")) { + keysPerWorker = Integer.parseInt(config.get("keysPerWorker")); + } + if (config.containsKey("iterations")) { + iterations = Integer.parseInt(config.get("iterations")); + } + if (config.containsKey("delay")) { + delay = Integer.parseInt(config.get("delay")); + } + } + + @Override + public void startUp(FloodlightModuleContext context) + throws FloodlightModuleException { + try { + final IStoreClient<String, TortureValue> storeClient = + syncService.getStoreClient(SYNC_STORE_NAME, + String.class, + TortureValue.class); + for (int i = 0; i < numWorkers; i++) { + Thread thread = new Thread(new TortureWorker(storeClient, i), + "Torture-" + i); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } + } catch (Exception e) { + throw new FloodlightModuleException(e); + } + } + + protected static class TortureValue { + private String string; + private int integer; + private boolean bool; + + public TortureValue() { + super(); + } + + public TortureValue(String string, int integer, boolean bool) { + super(); + this.string = string; + this.integer = integer; + this.bool = bool; + } + + public String getString() { + return string; + } + public void setString(String string) { + this.string = string; + } + public int getInteger() { + return integer; + } + public void setInteger(int integer) { + this.integer = integer; + } + public boolean isBool() { + return bool; + } + public void setBool(boolean bool) { + this.bool = bool; + } + } + + protected class TortureWorker implements Runnable { + final IStoreClient<String, TortureValue> storeClient; + final int workerId; + final List<TortureValue> values; + + public TortureWorker(IStoreClient<String, TortureValue> storeClient, + int workerId) { + super(); + this.storeClient = storeClient; + this.workerId = workerId; + values = new ArrayList<TortureValue>(); + for (int i = 0; i < keysPerWorker; i++) { + values.add(new TortureValue(workerId+":"+i, 0, true)); + } + } + + @Override + public void run() { + if (delay > 0) { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { } + } + int i = 0; + while (iterations == 0 || i++ < iterations) { + long start = System.currentTimeMillis(); + try { + for (TortureValue v : values) { + Versioned<TortureValue> vv = + storeClient.get(v.getString()); + v.setInteger(v.getInteger() + 1); + v.setBool(!v.isBool()); + vv.setValue(v); + storeClient.put(v.getString(), vv); + } + } catch (Exception e) { + logger.error("Error in worker: ", e); + } + long iterend = System.currentTimeMillis(); + debugCounter.flushCounters(); + logger.info("Completed iteration of {} values in {}ms" + + " ({}/s)", + new Object[]{values.size(), (iterend-start), + 1000*values.size()/(iterend-start)}); + } + + } + } +} diff --git a/src/main/java/org/sdnplatform/sync/internal/config/ClusterConfig.java b/src/main/java/org/sdnplatform/sync/internal/config/ClusterConfig.java index 416f4ebaacddf5455a40e404ceefe251526a2617..27addf7fe1ff448d154af8222d561604a31e86b3 100644 --- a/src/main/java/org/sdnplatform/sync/internal/config/ClusterConfig.java +++ b/src/main/java/org/sdnplatform/sync/internal/config/ClusterConfig.java @@ -15,7 +15,6 @@ import org.sdnplatform.sync.error.SyncException; * @author readams */ public class ClusterConfig { - public static final short NODE_ID_UNCONFIGURED = Short.MAX_VALUE; private HashMap<Short, Node> allNodes = new HashMap<Short, Node>(); diff --git a/src/main/java/org/sdnplatform/sync/internal/util/ByteArray.java b/src/main/java/org/sdnplatform/sync/internal/util/ByteArray.java index 733f03af894c09f3e310ed84f7d5feada55a41b0..5a9473562e4d226fb7e8f5f22e805074bcd90129 100644 --- a/src/main/java/org/sdnplatform/sync/internal/util/ByteArray.java +++ b/src/main/java/org/sdnplatform/sync/internal/util/ByteArray.java @@ -4,7 +4,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; -import org.projectfloodlight.openflow.util.HexString; +import org.openflow.util.HexString; /** * A byte array container that provides an equals and hashCode pair based on the diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java index e5b3347805af7289e2e8abe90234f38df54b0ee8..b06589a30a6493bd22d7da347a7bab7e6d4fd642 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java @@ -95,7 +95,7 @@ import org.sdnplatform.sync.test.MockSyncService; public class ControllerTest extends FloodlightTestCase { - private OLD__Controller controller; + private Controller controller; private MockThreadPoolService tp; private MockSyncService syncService; private IStoreClient<Long, SwitchSyncRepresentation> storeClient; @@ -115,7 +115,7 @@ public class ControllerTest extends FloodlightTestCase { FloodlightProvider cm = new FloodlightProvider(); fmc.addConfigParam(cm, "role", role.toString()); - controller = (OLD__Controller)cm.getServiceImpls().get(IFloodlightProviderService.class); + controller = (Controller)cm.getServiceImpls().get(IFloodlightProviderService.class); fmc.addService(IFloodlightProviderService.class, controller); MemoryStorageSource memstorage = new MemoryStorageSource(); @@ -165,7 +165,7 @@ public class ControllerTest extends FloodlightTestCase { cm.startUp(fmc); storeClient = - syncService.getStoreClient(OLD__Controller.SWITCH_SYNC_STORE_NAME, + syncService.getStoreClient(Controller.SWITCH_SYNC_STORE_NAME, Long.class, SwitchSyncRepresentation.class); @@ -204,7 +204,7 @@ public class ControllerTest extends FloodlightTestCase { controller.isUpdateQueueEmptyForTesting()); } - public OLD__Controller getController() { + public Controller getController() { return controller; } @@ -2048,11 +2048,11 @@ public class ControllerTest extends FloodlightTestCase { private Map<String,Object> getFakeControllerIPRow(String id, String controllerId, String type, int number, String discoveredIP ) { HashMap<String, Object> row = new HashMap<String,Object>(); - row.put(OLD__Controller.CONTROLLER_INTERFACE_ID, id); - row.put(OLD__Controller.CONTROLLER_INTERFACE_CONTROLLER_ID, controllerId); - row.put(OLD__Controller.CONTROLLER_INTERFACE_TYPE, type); - row.put(OLD__Controller.CONTROLLER_INTERFACE_NUMBER, number); - row.put(OLD__Controller.CONTROLLER_INTERFACE_DISCOVERED_IP, discoveredIP); + row.put(Controller.CONTROLLER_INTERFACE_ID, id); + row.put(Controller.CONTROLLER_INTERFACE_CONTROLLER_ID, controllerId); + row.put(Controller.CONTROLLER_INTERFACE_TYPE, type); + row.put(Controller.CONTROLLER_INTERFACE_NUMBER, number); + row.put(Controller.CONTROLLER_INTERFACE_DISCOVERED_IP, discoveredIP); return row; } @@ -2139,7 +2139,7 @@ public class ControllerTest extends FloodlightTestCase { synchronized(listener) { // Insert a first entry controller.getStorageSourceService() - .insertRow(OLD__Controller.CONTROLLER_INTERFACE_TABLE_NAME, + .insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row1", "c1", "Ethernet", 0, "1.1.1.1")); expectedCurMap.clear(); expectedAddedMap.clear(); @@ -2151,7 +2151,7 @@ public class ControllerTest extends FloodlightTestCase { // Add an interface that we want to ignore. controller.getStorageSourceService() - .insertRow(OLD__Controller.CONTROLLER_INTERFACE_TABLE_NAME, + .insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row2", "c1", "Ethernet", 1, "1.1.1.2")); // TODO: do a different check. This call will have to wait for the timeout controller.processUpdateQueueForTesting(); @@ -2160,7 +2160,7 @@ public class ControllerTest extends FloodlightTestCase { // Add another entry controller.getStorageSourceService() - .insertRow(OLD__Controller.CONTROLLER_INTERFACE_TABLE_NAME, + .insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.2")); expectedCurMap.clear(); expectedAddedMap.clear(); @@ -2174,7 +2174,7 @@ public class ControllerTest extends FloodlightTestCase { // Update an entry controller.getStorageSourceService() - .updateRow(OLD__Controller.CONTROLLER_INTERFACE_TABLE_NAME, + .updateRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, "row3", getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.3")); expectedCurMap.clear(); expectedAddedMap.clear(); @@ -2188,7 +2188,7 @@ public class ControllerTest extends FloodlightTestCase { // Delete an entry controller.getStorageSourceService() - .deleteRow(OLD__Controller.CONTROLLER_INTERFACE_TABLE_NAME, "row3"); + .deleteRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, "row3"); expectedCurMap.clear(); expectedAddedMap.clear(); expectedRemovedMap.clear(); @@ -2204,13 +2204,13 @@ public class ControllerTest extends FloodlightTestCase { HashMap<String,String> expectedCurMap = new HashMap<String, String>(); controller.getStorageSourceService() - .insertRow(OLD__Controller.CONTROLLER_INTERFACE_TABLE_NAME, + .insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row1", "c1", "Ethernet", 0, "1.1.1.1")); controller.getStorageSourceService() - .insertRow(OLD__Controller.CONTROLLER_INTERFACE_TABLE_NAME, + .insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row2", "c1", "Ethernet", 1, "1.1.1.2")); controller.getStorageSourceService() - .insertRow(OLD__Controller.CONTROLLER_INTERFACE_TABLE_NAME, + .insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.2")); expectedCurMap.put("c1", "1.1.1.1"); expectedCurMap.put("c2", "2.2.2.2"); diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerTest.java b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerTest.java index bc4490e73aa34e96c0dc716783999a8e8f59a54d..ed01868b0035eeae062f8f8f17afa2930a3c5ed2 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerTest.java @@ -66,7 +66,7 @@ import static org.junit.Assert.*; public class OFChannelHandlerTest { private static final short CORE_PRIORITY = 4242; private static final short ACCESS_PRIORITY = 42; - private OLD__Controller controller; + private Controller controller; private IThreadPoolService threadPool; private IDebugCounterService debugCounterService; private OFChannelHandler handler; @@ -110,7 +110,7 @@ public class OFChannelHandlerTest { @Before public void setUp() throws Exception { - controller = createMock(OLD__Controller.class); + controller = createMock(Controller.class); threadPool = createMock(IThreadPoolService.class); ctx = createMock(ChannelHandlerContext.class); channelStateEvent = createMock(ChannelStateEvent.class); @@ -125,8 +125,8 @@ public class OFChannelHandlerTest { // TODO: should mock IDebugCounterService and make sure // the expected counters are updated. debugCounterService = new DebugCounter(); - OLD__Controller.Counters counters = - new OLD__Controller.Counters(); + Controller.Counters counters = + new Controller.Counters(); counters.createCounters(debugCounterService); expect(controller.getCounters()).andReturn(counters).anyTimes(); replay(controller); @@ -442,7 +442,7 @@ public class OFChannelHandlerTest { Iterator<IResultSet> it = null; if (cfg.isPresent) { - storageResultSet.getBoolean(OLD__Controller.SWITCH_CONFIG_CORE_SWITCH); + storageResultSet.getBoolean(Controller.SWITCH_CONFIG_CORE_SWITCH); expectLastCall().andReturn(cfg.isCoreSwitch).atLeastOnce(); it = Collections.singletonList(storageResultSet).iterator(); } else { @@ -452,7 +452,7 @@ public class OFChannelHandlerTest { storageResultSet.close(); expectLastCall().atLeastOnce(); expect(storageResultSet.iterator()).andReturn(it).atLeastOnce(); - storageSource.getRow(OLD__Controller.SWITCH_CONFIG_TABLE_NAME, cfg.dpid); + storageSource.getRow(Controller.SWITCH_CONFIG_TABLE_NAME, cfg.dpid); expectLastCall().andReturn(storageResultSet).atLeastOnce(); replay(storageResultSet, storageSource); } diff --git a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java index 1e5e4643e894e868ebffff36c7cdad986d5e41c4..c3cc59ed3c6cb5d50b2abbf047bd54d5a221c883 100644 --- a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java +++ b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java @@ -30,7 +30,7 @@ import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.IFloodlightProviderService.Role; import net.floodlightcontroller.core.ImmutablePort; -import net.floodlightcontroller.core.internal.OLD__Controller; +import net.floodlightcontroller.core.internal.Controller; import net.floodlightcontroller.debugcounter.IDebugCounterService; import net.floodlightcontroller.threadpool.IThreadPoolService; @@ -357,7 +357,7 @@ public class OFMessageDamperMockSwitch implements IOFSwitch { } @Override - public void setFloodlightProvider(OLD__Controller controller) { + public void setFloodlightProvider(Controller controller) { fail("Unexpected method call"); // TODO Auto-generated method stub diff --git a/src/test/java/org/openflow/protocol/BasicFactoryTest.java b/src/test/java/org/openflow/protocol/BasicFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..350d4be39c3f1148c7a6c4191085bbe8bb9132a5 --- /dev/null +++ b/src/test/java/org/openflow/protocol/BasicFactoryTest.java @@ -0,0 +1,134 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import static org.junit.Assert.assertArrayEquals; + +import java.util.List; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.action.MockVendorAction; +import org.openflow.protocol.action.MockVendorActionFactory; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionVendorGeneric; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.factory.MessageParseException; +import org.openflow.protocol.factory.OFVendorActionRegistry; +import org.openflow.util.U16; + +public class BasicFactoryTest extends TestCase { + + public void testCreateAndParse() throws MessageParseException { + BasicFactory factory = BasicFactory.getInstance(); + OFMessage m = factory.getMessage(OFType.HELLO); + m.setVersion((byte) 1); + m.setType(OFType.ECHO_REQUEST); + m.setLength(U16.t(8)); + m.setXid(0xdeadbeef); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + ChannelBuffer bb2 = ChannelBuffers.dynamicBuffer(); + m.writeTo(bb); + bb2.writeBytes(bb, bb.readableBytes()-1); + TestCase.assertNull(factory.parseMessage(bb2)); + bb2.writeByte(bb.readByte()); + List<OFMessage> message = factory.parseMessage(bb2); + TestCase.assertNotNull(message); + TestCase.assertEquals(message.size(), 1); + TestCase.assertTrue(message.get(0).getType() == OFType.ECHO_REQUEST); + } + + public void testInvalidMsgParse() throws MessageParseException { + BasicFactory factory = BasicFactory.getInstance(); + OFMessage m = factory.getMessage(OFType.HELLO); + m.setVersion((byte) 1); + m.setType(OFType.ECHO_REQUEST); + m.setLength(U16.t(16)); + m.setXid(0xdeadbeef); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + m.writeTo(bb); + List<OFMessage> message = factory.parseMessage(bb); + TestCase.assertNull(message); + } + + public void testCurrouptedMsgParse() throws MessageParseException { + BasicFactory factory = BasicFactory.getInstance(); + OFMessage m = factory.getMessage(OFType.HELLO); + m.setVersion((byte) 1); + m.setType(OFType.ERROR); + m.setLength(U16.t(8)); + m.setXid(0xdeadbeef); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + m.writeTo(bb); + try { + factory.parseMessage(bb); + } + catch(Exception e) { + TestCase.assertEquals(MessageParseException.class, e.getClass()); + } + } + + public void testCustomVendorAction() throws MessageParseException { + BasicFactory factory = BasicFactory.getInstance(); + OFVendorActionRegistry.getInstance().register( + MockVendorAction.VENDOR_ID, new MockVendorActionFactory()); + + + byte[] deadBeefMessage = { + (byte) 0xff, (byte) 0xff, // action vendor + 0x00, 0x10, // length + (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte)0xef, // deadbeaf + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08 // pad + }; + + ChannelBuffer buf = ChannelBuffers.copiedBuffer(deadBeefMessage); + + List<OFAction> actions = factory.parseActions(buf,deadBeefMessage.length); + assertEquals(1, actions.size()); + OFAction ofAction = actions.get(0); + assertTrue("Action should be MockVendorAction, but is "+ofAction.getClass(), ofAction instanceof MockVendorAction); + assertArrayEquals( new byte[] { 1,2,3,4,5,6,7,8}, ((MockVendorAction)ofAction).getMockData()); + + + } + + public void testGenericVendorAction() throws MessageParseException { + byte[] nonDeadBeefMessage = { + (byte) 0xff, (byte) 0xff, // action vendor + 0x00, 0x10, // length + (byte) 0x7e, (byte) 0xe7, (byte) 0xbe, (byte)0xef, // deadbeaf + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08 // pad + }; + + BasicFactory factory = BasicFactory.getInstance(); + OFVendorActionRegistry.getInstance().register( + MockVendorAction.VENDOR_ID, new MockVendorActionFactory()); + + ChannelBuffer buf = ChannelBuffers.copiedBuffer(nonDeadBeefMessage); + + List<OFAction> actions = factory.parseActions(buf,nonDeadBeefMessage.length); + assertEquals(1, actions.size()); + OFAction ofAction = actions.get(0); + assertTrue("Action should be OFActionVendorGeneric, but is "+ofAction.getClass(), ofAction instanceof OFActionVendorGeneric); + } + +} diff --git a/src/test/java/org/openflow/protocol/OFActionTypeTest.java b/src/test/java/org/openflow/protocol/OFActionTypeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ed8386cd745dd7db691e334d6bd64fec81e67745 --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFActionTypeTest.java @@ -0,0 +1,37 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + + +import org.junit.Test; +import org.openflow.protocol.action.OFActionType; + +import junit.framework.TestCase; + + +public class OFActionTypeTest extends TestCase { + @Test + public void testMapping() throws Exception { + TestCase.assertEquals(OFActionType.OUTPUT, + OFActionType.valueOf((short) 0)); + TestCase.assertEquals(OFActionType.OPAQUE_ENQUEUE, + OFActionType.valueOf((short) 11)); + TestCase.assertEquals(OFActionType.VENDOR, + OFActionType.valueOf((short) 0xffff)); + } +} diff --git a/src/test/java/org/openflow/protocol/OFBarrierReplyTest.java b/src/test/java/org/openflow/protocol/OFBarrierReplyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7e447bb071bf477448823d085db1d6a9e7e72701 --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFBarrierReplyTest.java @@ -0,0 +1,36 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + +public class OFBarrierReplyTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFBarrierReply msg = (OFBarrierReply) messageFactory + .getMessage(OFType.BARRIER_REPLY); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.BARRIER_REPLY, msg.getType()); + } +} diff --git a/src/test/java/org/openflow/protocol/OFBarrierRequestTest.java b/src/test/java/org/openflow/protocol/OFBarrierRequestTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3aa9cb4a789f6ea2202878a6d92da898d76a4490 --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFBarrierRequestTest.java @@ -0,0 +1,36 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + +public class OFBarrierRequestTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFBarrierRequest msg = (OFBarrierRequest) messageFactory + .getMessage(OFType.BARRIER_REQUEST); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.BARRIER_REQUEST, msg.getType()); + } +} diff --git a/src/test/java/org/openflow/protocol/OFErrorTest.java b/src/test/java/org/openflow/protocol/OFErrorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f4a57269826d81cf17b47b547e4b7588afeb873f --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFErrorTest.java @@ -0,0 +1,88 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.util.List; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.OFError.OFErrorType; +import org.openflow.protocol.OFError.OFHelloFailedCode; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.factory.MessageParseException; +import org.openflow.protocol.factory.OFMessageFactory; +import org.openflow.util.OFTestCase; + +public class OFErrorTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFError msg = (OFError) messageFactory.getMessage(OFType.ERROR); + msg.setMessageFactory(messageFactory); + msg.setErrorType(OFErrorType.OFPET_HELLO_FAILED.getValue()); + msg.setErrorCode((short) OFHelloFailedCode.OFPHFC_INCOMPATIBLE + .ordinal()); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFErrorType.OFPET_HELLO_FAILED.getValue(), + msg.getErrorType()); + TestCase.assertEquals((short) OFHelloFailedCode.OFPHFC_INCOMPATIBLE + .ordinal(), msg.getErrorType()); + TestCase.assertNull(msg.getOffendingMsg()); + + msg.setOffendingMsg(new OFHello()); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFErrorType.OFPET_HELLO_FAILED.getValue(), + msg.getErrorType()); + TestCase.assertEquals((short) OFHelloFailedCode.OFPHFC_INCOMPATIBLE + .ordinal(), msg.getErrorType()); + TestCase.assertNotNull(msg.getOffendingMsg()); + TestCase.assertEquals(OFHello.MINIMUM_LENGTH, + msg.getOffendingMsg().length); + } + + public void testGarbageAtEnd() throws MessageParseException { + // This is a OFError msg (12 bytes), that encaps a OFVendor msg (24 + // bytes) + // AND some zeros at the end (40 bytes) for a total of 76 bytes + // THIS is what an NEC sends in reply to Nox's VENDOR request + byte[] oferrorRaw = { 0x01, 0x01, 0x00, 0x4c, 0x00, 0x00, 0x10, + (byte) 0xcc, 0x00, 0x01, 0x00, 0x01, 0x01, 0x04, 0x00, 0x18, + 0x00, 0x00, 0x10, (byte) 0xcc, 0x00, 0x00, 0x23, 0x20, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 }; + OFMessageFactory factory = BasicFactory.getInstance(); + ChannelBuffer oferrorBuf = + ChannelBuffers.wrappedBuffer(oferrorRaw); + List<OFMessage> msg = factory.parseMessage(oferrorBuf); + TestCase.assertNotNull(msg); + TestCase.assertEquals(msg.size(), 1); + TestCase.assertEquals(76, msg.get(0).getLengthU()); + ChannelBuffer out = ChannelBuffers.dynamicBuffer(); + msg.get(0).writeTo(out); + TestCase.assertEquals(76, out.readableBytes()); + } +} diff --git a/src/test/java/org/openflow/protocol/OFFeaturesReplyTest.java b/src/test/java/org/openflow/protocol/OFFeaturesReplyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..62e491a995a3f232be6027043144f6544d70e1d8 --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFFeaturesReplyTest.java @@ -0,0 +1,62 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + + +public class OFFeaturesReplyTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFFeaturesReply ofr = (OFFeaturesReply) messageFactory + .getMessage(OFType.FEATURES_REPLY); + List<OFPhysicalPort> ports = new ArrayList<OFPhysicalPort>(); + OFPhysicalPort port = new OFPhysicalPort(); + port.setHardwareAddress(new byte[6]); + port.setName("eth0"); + ports.add(port); + ofr.setPorts(ports); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + ofr.writeTo(bb); + ofr.readFrom(bb); + TestCase.assertEquals(1, ofr.getPorts().size()); + TestCase.assertEquals("eth0", ofr.getPorts().get(0).getName()); + + // test a 15 character name + ofr.getPorts().get(0).setName("012345678901234"); + bb.clear(); + ofr.writeTo(bb); + ofr.readFrom(bb); + TestCase.assertEquals("012345678901234", ofr.getPorts().get(0).getName()); + + // test a 16 character name getting truncated + ofr.getPorts().get(0).setName("0123456789012345"); + bb.clear(); + ofr.writeTo(bb); + ofr.readFrom(bb); + TestCase.assertEquals("012345678901234", ofr.getPorts().get(0).getName()); + } +} diff --git a/src/test/java/org/openflow/protocol/OFFlowRemovedTest.java b/src/test/java/org/openflow/protocol/OFFlowRemovedTest.java new file mode 100644 index 0000000000000000000000000000000000000000..11746e72411a663170e7ebbf4a8571fb68f8b13f --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFFlowRemovedTest.java @@ -0,0 +1,43 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.OFFlowRemoved.OFFlowRemovedReason; +import org.openflow.util.OFTestCase; + +public class OFFlowRemovedTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFFlowRemoved msg = (OFFlowRemoved) messageFactory + .getMessage(OFType.FLOW_REMOVED); + msg.setMatch(new OFMatch()); + byte[] hwAddr = new byte[6]; + msg.getMatch().setDataLayerDestination(hwAddr); + msg.getMatch().setDataLayerSource(hwAddr); + msg.setReason(OFFlowRemovedReason.OFPRR_DELETE); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.FLOW_REMOVED, msg.getType()); + TestCase.assertEquals(OFFlowRemovedReason.OFPRR_DELETE, msg.getReason()); + } +} diff --git a/src/test/java/org/openflow/protocol/OFGetConfigReplyTest.java b/src/test/java/org/openflow/protocol/OFGetConfigReplyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c1f1f671a6767bb11dc3993c65fc21845d0820ab --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFGetConfigReplyTest.java @@ -0,0 +1,38 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + +public class OFGetConfigReplyTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFSetConfig msg = (OFSetConfig) messageFactory + .getMessage(OFType.SET_CONFIG); + msg.setFlags((short) 1); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.SET_CONFIG, msg.getType()); + TestCase.assertEquals((short)1, msg.getFlags()); + } +} diff --git a/src/test/java/org/openflow/protocol/OFGetConfigRequestTest.java b/src/test/java/org/openflow/protocol/OFGetConfigRequestTest.java new file mode 100644 index 0000000000000000000000000000000000000000..94d9036e68dd1e0b6573148e068fb4380f9946ef --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFGetConfigRequestTest.java @@ -0,0 +1,36 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + +public class OFGetConfigRequestTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFGetConfigRequest msg = (OFGetConfigRequest) messageFactory + .getMessage(OFType.GET_CONFIG_REQUEST); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.GET_CONFIG_REQUEST, msg.getType()); + } +} diff --git a/src/test/java/org/openflow/protocol/OFMatchTest.java b/src/test/java/org/openflow/protocol/OFMatchTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fd7863acc12d49e89054ced08111def1c5559b6d --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFMatchTest.java @@ -0,0 +1,92 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import junit.framework.TestCase; + +public class OFMatchTest extends TestCase { + public void testFromString() { + OFMatch correct = new OFMatch(); + OFMatch tester = new OFMatch(); + + // Various combinations of "all"/"any" + tester.fromString("OFMatch[]"); + // correct is already wildcarded + TestCase.assertEquals(correct, tester); + tester.fromString("all"); + TestCase.assertEquals(correct, tester); + tester.fromString("ANY"); + TestCase.assertEquals(correct, tester); + tester.fromString(""); + TestCase.assertEquals(correct, tester); + tester.fromString("[]"); + TestCase.assertEquals(correct, tester); + + // ip_src + correct.setWildcards(~OFMatch.OFPFW_NW_SRC_MASK); + correct.setNetworkSource(0x01010203); + tester.fromString("nw_src=1.1.2.3"); + TestCase.assertEquals(correct.getNetworkSourceMaskLen(), tester + .getNetworkSourceMaskLen()); + TestCase.assertEquals(correct, tester); + tester.fromString("IP_sRc=1.1.2.3"); + TestCase.assertEquals(correct.getNetworkSourceMaskLen(), tester + .getNetworkSourceMaskLen()); + TestCase.assertEquals(correct, tester); + + // 0xVlan + correct = new OFMatch(); + correct.setDataLayerVirtualLan((short)65535); + correct.setWildcards(~OFMatch.OFPFW_DL_VLAN); + tester = new OFMatch(); + tester.fromString("dl_vlan=0xffff"); + TestCase.assertEquals(correct, tester); + } + + public void testToString() { + OFMatch match = new OFMatch(); + match.fromString("nw_dst=3.4.5.6/8"); + TestCase.assertEquals(8, match.getNetworkDestinationMaskLen()); + String correct = "OFMatch[nw_dst=3.0.0.0/8]"; + String tester = match.toString(); + + TestCase.assertEquals(correct, tester); + tester = "OFMatch[dl_type=35020]"; + correct = "OFMatch[dl_type=0x88cc]"; + match = new OFMatch(); + match.fromString(tester); + TestCase.assertEquals(correct, match.toString()); + OFMatch match2 = new OFMatch(); + match2.fromString(correct); + TestCase.assertEquals(match, match2); + } + + public void testClone() { + OFMatch match1 = new OFMatch(); + OFMatch match2 = match1.clone(); + TestCase.assertEquals(match1, match2); + match2.setNetworkProtocol((byte) 4); + match2.setWildcards(match2.getWildcards() & ~OFMatch.OFPFW_NW_PROTO); + TestCase.assertNotSame(match1, match2); + } + + public void testIpToString() { + String test = OFMatch.ipToString(-1); + TestCase.assertEquals("255.255.255.255", test); + } +} diff --git a/src/test/java/org/openflow/protocol/OFMessageContextStoreTest.java b/src/test/java/org/openflow/protocol/OFMessageContextStoreTest.java new file mode 100644 index 0000000000000000000000000000000000000000..60a9e732b05ba2d03ccbf1c4b9793634cafdeb8a --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFMessageContextStoreTest.java @@ -0,0 +1,31 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import junit.framework.TestCase; + +public class OFMessageContextStoreTest extends TestCase { + public void testStoreAndGet() { + OFMessage msg = new OFMessage(); + OFMessageContextStore<String> store = new OFMessageContextStore<String>(msg, this.getName()); + String key = "mykey"; + String value = "myvalue"; + store.put(key, value); + TestCase.assertEquals(value, store.get(key)); + } +} diff --git a/src/test/java/org/openflow/protocol/OFPacketOutTest.java b/src/test/java/org/openflow/protocol/OFPacketOutTest.java new file mode 100644 index 0000000000000000000000000000000000000000..55b54552fe03b279f20f076dd8857994dded0e09 --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFPacketOutTest.java @@ -0,0 +1,45 @@ +package org.openflow.protocol; + +import org.junit.Test; + +public class OFPacketOutTest { + + @Test(expected = IllegalArgumentException.class) + public void testBothBufferIdAndPayloadSet() { + OFPacketOut packetOut = new OFPacketOut(); + packetOut.setBufferId(12); + packetOut.setPacketData(new byte[] { 1, 2, 3 }); + } + + @Test + public void testOnlyBufferIdSet() { + OFPacketOut packetOut = new OFPacketOut(); + packetOut.setBufferId(12); + packetOut.setPacketData(null); + packetOut.setPacketData(new byte[] {}); + packetOut.validate(); + } + + @Test(expected = IllegalStateException.class) + public void testNeitherBufferIdNorPayloadSet() { + OFPacketOut packetOut = new OFPacketOut(); + packetOut.setBufferId(OFPacketOut.BUFFER_ID_NONE); + packetOut.setPacketData(null); + packetOut.validate(); + } + + @Test(expected = IllegalStateException.class) + public void testNeitherBufferIdNorPayloadSet2() { + OFPacketOut packetOut = new OFPacketOut(); + packetOut.setBufferId(OFPacketOut.BUFFER_ID_NONE); + packetOut.setPacketData(new byte[] {}); + packetOut.validate(); + } + + @Test(expected = IllegalStateException.class) + public void testNeitherBufferIdNorPayloadSet3() { + OFPacketOut packetOut = new OFPacketOut(); + packetOut.validate(); + } + +} diff --git a/src/test/java/org/openflow/protocol/OFPortConfigTest.java b/src/test/java/org/openflow/protocol/OFPortConfigTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9b115ebc40027a15bfe2f1589d39855bc1051ba2 --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFPortConfigTest.java @@ -0,0 +1,39 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + +public class OFPortConfigTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFPortMod msg = (OFPortMod) messageFactory + .getMessage(OFType.PORT_MOD); + msg.setHardwareAddress(new byte[6]); + msg.portNumber = 1; + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.PORT_MOD, msg.getType()); + TestCase.assertEquals(1, msg.getPortNumber()); + } +} diff --git a/src/test/java/org/openflow/protocol/OFPortStatusTest.java b/src/test/java/org/openflow/protocol/OFPortStatusTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4fab64e03568194784f6b7eaf4518ece9a5017c0 --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFPortStatusTest.java @@ -0,0 +1,44 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.OFPortStatus.OFPortReason; +import org.openflow.util.OFTestCase; + +public class OFPortStatusTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFPortStatus msg = (OFPortStatus) messageFactory + .getMessage(OFType.PORT_STATUS); + msg.setDesc(new OFPhysicalPort()); + msg.getDesc().setHardwareAddress(new byte[6]); + msg.getDesc().setName("eth0"); + msg.setReason((byte) OFPortReason.OFPPR_ADD.ordinal()); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.PORT_STATUS, msg.getType()); + TestCase.assertEquals((byte) OFPortReason.OFPPR_ADD.ordinal(), msg + .getReason()); + TestCase.assertNotNull(msg.getDesc()); + } +} diff --git a/src/test/java/org/openflow/protocol/OFSetConfigTest.java b/src/test/java/org/openflow/protocol/OFSetConfigTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2a9e86f7720fb44baf87acaf487d901a2218e026 --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFSetConfigTest.java @@ -0,0 +1,38 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.util.OFTestCase; + +public class OFSetConfigTest extends OFTestCase { + public void testWriteRead() throws Exception { + OFGetConfigReply msg = (OFGetConfigReply) messageFactory + .getMessage(OFType.GET_CONFIG_REPLY); + msg.setFlags((short) 1); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(OFType.GET_CONFIG_REPLY, msg.getType()); + TestCase.assertEquals((short)1, msg.getFlags()); + } +} diff --git a/src/test/java/org/openflow/protocol/OFStatisticsReplyTest.java b/src/test/java/org/openflow/protocol/OFStatisticsReplyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1885cae7b3d4731ccae5bfbe4fdb295951b1a760 --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFStatisticsReplyTest.java @@ -0,0 +1,77 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.util.List; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.factory.OFMessageFactory; +import org.openflow.protocol.statistics.OFStatisticsType; +import org.openflow.util.OFTestCase; + +public class OFStatisticsReplyTest extends OFTestCase { + public void testOFFlowStatisticsReply() throws Exception { + byte[] packet = new byte[] { 0x01, 0x11, 0x01, 0x2c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, (byte) 0xff, + (byte) 0xff, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, (byte) 0xa6, + (byte) 0xa6, 0x00, (byte) 0xff, (byte) 0xff, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + (byte) 0xc4, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x08, 0x06, + 0x00, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x03, 0x0a, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x2f, (byte) 0xfa, 0x40, (byte) 0xff, (byte) 0xff, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, (byte) 0xff, (byte) 0xff, 0x00, 0x62, 0x08, + 0x00, 0x00, 0x01, 0x62, 0x37, 0x0a, 0x00, 0x00, 0x02, 0x0a, + 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, (byte) 0xc5, 0x2a, (byte) 0x80, (byte) 0xff, + (byte) 0xff, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xc4, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x00 }; + + OFMessageFactory factory = BasicFactory.getInstance(); + ChannelBuffer packetBuf = ChannelBuffers.wrappedBuffer(packet); + List<OFMessage> msg = factory.parseMessage(packetBuf); + TestCase.assertNotNull(msg); + TestCase.assertEquals(msg.size(), 1); + TestCase.assertTrue(msg.get(0) instanceof OFStatisticsReply); + OFStatisticsReply sr = (OFStatisticsReply) msg.get(0); + TestCase.assertEquals(OFStatisticsType.FLOW, sr.getStatisticType()); + TestCase.assertEquals(3, sr.getStatistics().size()); + } +} diff --git a/src/test/java/org/openflow/protocol/OFStatisticsRequestTest.java b/src/test/java/org/openflow/protocol/OFStatisticsRequestTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fceb8955eb755a12bb03c68bd1102513874b06ab --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFStatisticsRequestTest.java @@ -0,0 +1,79 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.util.List; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.factory.OFMessageFactory; +import org.openflow.protocol.statistics.OFFlowStatisticsRequest; +import org.openflow.protocol.statistics.OFStatisticsType; +import org.openflow.protocol.statistics.OFVendorStatistics; +import org.openflow.util.OFTestCase; + +public class OFStatisticsRequestTest extends OFTestCase { + public void testOFFlowStatisticsRequest() throws Exception { + byte[] packet = new byte[] { 0x01, 0x10, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x01, 0x00, 0x00, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + (byte) 0xff, 0x00, (byte) 0xff, (byte) 0xff }; + + OFMessageFactory factory = BasicFactory.getInstance(); + ChannelBuffer packetBuf = ChannelBuffers.wrappedBuffer(packet); + List<OFMessage> msg = factory.parseMessage(packetBuf); + TestCase.assertNotNull(msg); + TestCase.assertEquals(msg.size(), 1); + TestCase.assertTrue(msg.get(0) instanceof OFStatisticsRequest); + OFStatisticsRequest sr = (OFStatisticsRequest) msg.get(0); + TestCase.assertEquals(OFStatisticsType.FLOW, sr.getStatisticType()); + TestCase.assertEquals(1, sr.getStatistics().size()); + TestCase.assertTrue(sr.getStatistics().get(0) instanceof OFFlowStatisticsRequest); + } + + public void testOFStatisticsRequestVendor() throws Exception { + byte[] packet = new byte[] { 0x01, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x63, (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x20, + (byte) 0xe0, 0x00, 0x11, 0x00, 0x0c, 0x29, (byte) 0xc5, + (byte) 0x95, 0x57, 0x02, 0x25, 0x5c, (byte) 0xca, 0x00, 0x02, + (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x50, 0x04, + 0x00, 0x00, 0x00, 0x00, (byte) 0xff, 0x00, 0x00, 0x00, + (byte) 0xff, (byte) 0xff, 0x4e, 0x20 }; + + OFMessageFactory factory = BasicFactory.getInstance(); + ChannelBuffer packetBuf = ChannelBuffers.wrappedBuffer(packet); + List<OFMessage> msg = factory.parseMessage(packetBuf); + TestCase.assertNotNull(msg); + TestCase.assertEquals(msg.size(), 1); + TestCase.assertTrue(msg.get(0) instanceof OFStatisticsRequest); + OFStatisticsRequest sr = (OFStatisticsRequest) msg.get(0); + TestCase.assertEquals(OFStatisticsType.VENDOR, sr.getStatisticType()); + TestCase.assertEquals(1, sr.getStatistics().size()); + TestCase.assertTrue(sr.getStatistics().get(0) instanceof OFVendorStatistics); + TestCase.assertEquals(68, ((OFVendorStatistics)sr.getStatistics().get(0)).getLength()); + } +} diff --git a/src/test/java/org/openflow/protocol/OFStatisticsTypeTest.java b/src/test/java/org/openflow/protocol/OFStatisticsTypeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d44ef7f8e4553f3556efb0bb52ad193578c07bbf --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFStatisticsTypeTest.java @@ -0,0 +1,37 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + + +import junit.framework.TestCase; + +import org.junit.Test; +import org.openflow.protocol.statistics.OFStatisticsType; + + +public class OFStatisticsTypeTest extends TestCase { + @Test + public void testMapping() throws Exception { + TestCase.assertEquals(OFStatisticsType.DESC, + OFStatisticsType.valueOf((short) 0, OFType.STATS_REQUEST)); + TestCase.assertEquals(OFStatisticsType.QUEUE, + OFStatisticsType.valueOf((short) 5, OFType.STATS_REQUEST)); + TestCase.assertEquals(OFStatisticsType.VENDOR, + OFStatisticsType.valueOf((short) 0xffff, OFType.STATS_REQUEST)); + } +} diff --git a/src/test/java/org/openflow/protocol/OFTypeTest.java b/src/test/java/org/openflow/protocol/OFTypeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c6bf0a347454064d99f0f4ec1064d17cca23da9a --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFTypeTest.java @@ -0,0 +1,39 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + + +import junit.framework.TestCase; + +import org.junit.Test; + + +public class OFTypeTest extends TestCase { + + public void testOFTypeCreate() throws Exception { + OFType foo = OFType.HELLO; + Class<? extends OFMessage> c = foo.toClass(); + TestCase.assertEquals(c, OFHello.class); + } + + @Test + public void testMapping() throws Exception { + TestCase.assertEquals(OFType.HELLO, OFType.valueOf((byte) 0)); + TestCase.assertEquals(OFType.BARRIER_REPLY, OFType.valueOf((byte) 19)); + } +} diff --git a/src/test/java/org/openflow/protocol/OFVendorTest.java b/src/test/java/org/openflow/protocol/OFVendorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c0385f4415824d1c79408e93ac31de4be40f3e52 --- /dev/null +++ b/src/test/java/org/openflow/protocol/OFVendorTest.java @@ -0,0 +1,226 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.protocol; + +import java.util.Arrays; + +import junit.framework.TestCase; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.vendor.OFByteArrayVendorData; +import org.openflow.protocol.vendor.OFBasicVendorDataType; +import org.openflow.protocol.vendor.OFBasicVendorId; +import org.openflow.protocol.vendor.OFVendorData; +import org.openflow.protocol.vendor.OFVendorId; +import org.openflow.util.OFTestCase; + +public class OFVendorTest extends OFTestCase { + + public static int ACME_VENDOR_ID = 0x00112233; + + static class AcmeVendorData implements OFVendorData { + protected int dataType; + + @Override + public int getLength() { + return 4; + } + + @Override + public void readFrom(ChannelBuffer data, int length) { + dataType = data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + data.writeInt(dataType); + } + } + + static class AcmeVendorData1 extends AcmeVendorData { + public short flags; + public short value; + + public static int DATA_TYPE = 1; + + public AcmeVendorData1() { + } + + public AcmeVendorData1(short flags, short value) { + this.dataType = DATA_TYPE; + this.flags = flags; + this.value = value; + } + + public short getFlags() { + return flags; + } + + public short getValue() { + return value; + } + + @Override + public int getLength() { + return 8; + } + + @Override + public void readFrom(ChannelBuffer data, int length) { + super.readFrom(data, length); + flags = data.readShort(); + value = data.readShort(); + + } + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(flags); + data.writeShort(value); + } + + public static Instantiable<OFVendorData> getInstantiable() { + return new Instantiable<OFVendorData>() { + @Override + public OFVendorData instantiate() { + return new AcmeVendorData1(); + } + }; + } + } + + static class AcmeVendorData2 extends AcmeVendorData { + public int type; + public int subtype; + + public static int DATA_TYPE = 2; + + public AcmeVendorData2() { + } + + public AcmeVendorData2(int type, int subtype) { + this.dataType = DATA_TYPE; + this.type = type; + this.subtype = subtype; + } + + public int getType() { + return type; + } + + public int getSubtype() { + return subtype; + } + + @Override + public int getLength() { + return 12; + } + + @Override + public void readFrom(ChannelBuffer data, int length) { + super.readFrom(data, length); + type = data.readShort(); + subtype = data.readShort(); + + } + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(type); + data.writeShort(subtype); + } + + public static Instantiable<OFVendorData> getInstantiable() { + return new Instantiable<OFVendorData>() { + @Override + public OFVendorData instantiate() { + return new AcmeVendorData2(); + } + }; + } + } + + { + OFBasicVendorId acmeVendorId = new OFBasicVendorId(ACME_VENDOR_ID, 4); + OFVendorId.registerVendorId(acmeVendorId); + OFBasicVendorDataType acmeVendorData1 = new OFBasicVendorDataType( + AcmeVendorData1.DATA_TYPE, AcmeVendorData1.getInstantiable()); + acmeVendorId.registerVendorDataType(acmeVendorData1); + OFBasicVendorDataType acmeVendorData2 = new OFBasicVendorDataType( + AcmeVendorData2.DATA_TYPE, AcmeVendorData2.getInstantiable()); + acmeVendorId.registerVendorDataType(acmeVendorData2); + } + + private OFVendor makeVendorMessage(int vendor) { + OFVendor msg = (OFVendor) messageFactory.getMessage(OFType.VENDOR); + msg.setVendorDataFactory(BasicFactory.getInstance()); + msg.setVendor(vendor); + return msg; + } + + public void testWriteRead() throws Exception { + OFVendor msg = makeVendorMessage(1); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + TestCase.assertEquals(1, msg.getVendor()); + } + + public void testVendorData() throws Exception { + OFVendor msg = makeVendorMessage(ACME_VENDOR_ID); + OFVendorData vendorData = new AcmeVendorData1((short)11, (short)22); + msg.setVendorData(vendorData); + msg.setLengthU(OFVendor.MINIMUM_LENGTH + vendorData.getLength()); + ChannelBuffer bb = ChannelBuffers.dynamicBuffer(); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + assertEquals(ACME_VENDOR_ID, msg.getVendor()); + AcmeVendorData1 vendorData1 = (AcmeVendorData1) msg.getVendorData(); + assertEquals(11, vendorData1.getFlags()); + assertEquals(22, vendorData1.getValue()); + + vendorData = new AcmeVendorData2(33, 44); + msg.setVendorData(vendorData); + msg.setLengthU(OFVendor.MINIMUM_LENGTH + vendorData.getLength()); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + assertEquals(ACME_VENDOR_ID, msg.getVendor()); + AcmeVendorData2 vendorData2 = (AcmeVendorData2) msg.getVendorData(); + assertEquals(33, vendorData2.getType()); + assertEquals(44, vendorData2.getSubtype()); + + final int DUMMY_VENDOR_ID = 55; + msg.setVendor(DUMMY_VENDOR_ID); + byte[] genericVendorDataBytes = new byte[] {0x55, 0x66}; + vendorData = new OFByteArrayVendorData(genericVendorDataBytes); + msg.setVendorData(vendorData); + msg.setLengthU(OFVendor.MINIMUM_LENGTH + vendorData.getLength()); + bb.clear(); + msg.writeTo(bb); + msg.readFrom(bb); + assertEquals(DUMMY_VENDOR_ID, msg.getVendor()); + OFByteArrayVendorData genericVendorData = (OFByteArrayVendorData) msg.getVendorData(); + assertTrue(Arrays.equals(genericVendorDataBytes, genericVendorData.getBytes())); + } +} diff --git a/src/test/java/org/openflow/protocol/WildcardsTest.java b/src/test/java/org/openflow/protocol/WildcardsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5bf8d12314a070bc301a8ffda6a36ad02ff7d6a0 --- /dev/null +++ b/src/test/java/org/openflow/protocol/WildcardsTest.java @@ -0,0 +1,162 @@ +package org.openflow.protocol; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.EnumSet; + +import org.junit.Test; +import org.openflow.protocol.Wildcards.Flag; + +public class WildcardsTest { + + @Test + public void testBasic() { + int[] intMasks = { 0, 0x3820e0, OFMatch.OFPFW_ALL_SANITIZED }; + for (int i : intMasks) { + Wildcards w = Wildcards.of(i); + assertEquals(i, w.getInt()); + } + } + + @Test + public void testAllSanitize() { + Wildcards w = Wildcards.of(OFMatch.OFPFW_ALL); + assertEquals(OFMatch.OFPFW_ALL_SANITIZED, w.getInt()); + assertTrue(w.isFull()); + assertFalse(w.isExact()); + } + + @Test + public void testAll() { + Wildcards all = Wildcards.FULL; + assertTrue(all.isFull()); + assertFalse(all.isExact()); + assertEquals(0, all.getNwDstMask()); + assertEquals(0, all.getNwSrcMask()); + + // unsetting flags from NONE is a no-op + Wildcards stillAll = all.wildcard(Flag.IN_PORT); + assertTrue(stillAll.isFull()); + assertEquals(all, stillAll); + + // so is setting a >= 32 netmask + + stillAll = all.withNwSrcMask(0); + assertTrue(stillAll.isFull()); + assertEquals(all, stillAll); + + stillAll = all.withNwDstMask(0); + assertTrue(stillAll.isFull()); + assertEquals(all, stillAll); + } + + @Test + public void testNone() { + Wildcards none = Wildcards.EXACT; + assertTrue(none.isExact()); + assertEquals(32, none.getNwDstMask()); + assertEquals(32, none.getNwSrcMask()); + + // unsetting flags from NONE is a no-op + Wildcards stillNone = none.matchOn(Flag.IN_PORT); + assertTrue(stillNone.isExact()); + assertEquals(none, stillNone); + + // so is setting a >= 32 netmask + stillNone = none.withNwSrcMask(32); + assertTrue(stillNone.isExact()); + assertEquals(none, stillNone); + + stillNone = none.withNwDstMask(32); + assertTrue(stillNone.isExact()); + assertEquals(none, stillNone); + } + + @Test + public void testSetOneFlag() { + Wildcards none = Wildcards.EXACT; + assertTrue(none.isExact()); + assertFalse(none.isWildcarded(Flag.DL_SRC)); + Wildcards one = none.wildcard(Flag.DL_SRC); + assertFalse(one.isExact()); + assertTrue(one.isWildcarded(Flag.DL_SRC)); + assertEquals(OFMatch.OFPFW_DL_SRC, one.getInt()); + assertEquals(EnumSet.of(Flag.DL_SRC), one.getWildcardedFlags()); + } + + @Test + public void testSetTwoFlags() { + Wildcards none = Wildcards.EXACT; + + // set two flags + Wildcards two = none.wildcard(Flag.DL_SRC, Flag.DL_DST); + assertFalse(two.isExact()); + assertTrue(two.isWildcarded(Flag.DL_SRC)); + assertTrue(two.isWildcarded(Flag.DL_DST)); + assertEquals(OFMatch.OFPFW_DL_SRC | OFMatch.OFPFW_DL_DST, two.getInt()); + assertEquals(EnumSet.of(Flag.DL_SRC, Flag.DL_DST), two.getWildcardedFlags()); + + // unset dl_dst + Wildcards gone = two.matchOn(Flag.DL_DST); + assertFalse(gone.isExact()); + assertTrue(gone.isWildcarded(Flag.DL_SRC)); + assertFalse(gone.isWildcarded(Flag.DL_DST)); + assertEquals(OFMatch.OFPFW_DL_SRC, gone.getInt()); + assertEquals(EnumSet.of(Flag.DL_SRC), gone.getWildcardedFlags()); + } + + @Test + public void testSetNwSrc() { + Wildcards none = Wildcards.EXACT; + assertEquals(32, none.getNwSrcMask()); + + // unsetting flags from NONE is a no-op + Wildcards nwSet = none.withNwSrcMask(8); + assertFalse(nwSet.isExact()); + assertEquals(EnumSet.noneOf(Flag.class), nwSet.getWildcardedFlags()); + assertEquals(8, nwSet.getNwSrcMask()); + assertEquals((32 - 8) << OFMatch.OFPFW_NW_SRC_SHIFT, nwSet.getInt()); + } + + @Test + public void testSetNwDst() { + Wildcards none = Wildcards.EXACT; + assertEquals(32, none.getNwDstMask()); + + // unsetting flags from NONE is a no-op + Wildcards nwSet = none.withNwDstMask(8); + assertFalse(nwSet.isExact()); + assertEquals(EnumSet.noneOf(Flag.class), nwSet.getWildcardedFlags()); + assertEquals(8, nwSet.getNwDstMask()); + assertEquals((32 - 8) << OFMatch.OFPFW_NW_DST_SHIFT, nwSet.getInt()); + } + + @Test + public void testToString() { + String s = Wildcards.FULL.toString(); + assertNotNull(s); + assertTrue(s.length() > 0); + } + + @Test + public void testInvert() { + assertEquals(Wildcards.FULL, Wildcards.EXACT.inverted()); + + Wildcards some = Wildcards.of(Flag.DL_VLAN, Flag.DL_VLAN_PCP); + Wildcards inv = some.inverted(); + + for(Flag f : Flag.values()) { + boolean shouldBeSet = (f == Flag.DL_VLAN || f == Flag.DL_VLAN_PCP); + + assertEquals("Flag " + f + " " + + (shouldBeSet ? "should be set " : "should not be set"), + shouldBeSet, some.isWildcarded(f)); + assertEquals(!(f == Flag.DL_VLAN || f == Flag.DL_VLAN_PCP), inv.isWildcarded(f)); + } + assertEquals(0, inv.getNwDstMask()); + assertEquals(0, inv.getNwSrcMask()); + } +} diff --git a/src/test/java/org/openflow/protocol/action/MockVendorAction.java b/src/test/java/org/openflow/protocol/action/MockVendorAction.java new file mode 100644 index 0000000000000000000000000000000000000000..49b69fb8181d4a6dd6959a40786d643fe05ead20 --- /dev/null +++ b/src/test/java/org/openflow/protocol/action/MockVendorAction.java @@ -0,0 +1,41 @@ +package org.openflow.protocol.action; + +import org.jboss.netty.buffer.ChannelBuffer; + + +public class MockVendorAction extends OFActionVendor { + public static final int VENDOR_ID = 0xdeadbeef; + + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + private byte[] mockData; + + public byte[] getMockData() { + return mockData; + } + + public void setMockData(byte[] mockData) { + this.mockData = mockData; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + + int dataLength = getLength() - MINIMUM_LENGTH; + if(dataLength > 0) { + mockData = new byte[dataLength]; + data.readBytes(mockData); + } else { + mockData = EMPTY_BYTE_ARRAY; + } + + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeBytes(mockData); + } + + +} diff --git a/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java b/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..bbc254ca0074cc9e418c5541f976d000074cf0b1 --- /dev/null +++ b/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java @@ -0,0 +1,15 @@ +package org.openflow.protocol.action; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.factory.OFVendorActionFactory; + +public class MockVendorActionFactory implements OFVendorActionFactory { + + @Override + public OFActionVendor readFrom(ChannelBuffer data) { + MockVendorAction action = new MockVendorAction(); + action.readFrom(data); + return action; + } + +} diff --git a/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java b/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..31ad675d6a0a708c5a69a7ae3b0dcb21bc24e5c5 --- /dev/null +++ b/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java @@ -0,0 +1,17 @@ +package org.openflow.protocol.action; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.openflow.protocol.factory.OFVendorActionRegistry; + +public class OFVendorActionRegistryTest { + + @Test + public void test() { + MockVendorActionFactory factory = new MockVendorActionFactory(); + OFVendorActionRegistry.getInstance().register(MockVendorAction.VENDOR_ID, factory); + assertEquals(factory, OFVendorActionRegistry.getInstance().get(MockVendorAction.VENDOR_ID)); + } + +} diff --git a/src/test/java/org/openflow/util/HexStringTest.java b/src/test/java/org/openflow/util/HexStringTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a8f8ba4b89760e797255da01e3cecb162f0da41b --- /dev/null +++ b/src/test/java/org/openflow/util/HexStringTest.java @@ -0,0 +1,88 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +import org.junit.Test; + +import junit.framework.TestCase; + +/** + * Does hexstring conversion work? + * + * @author Rob Sherwood (rob.sherwood@stanford.edu) + * + */ + +public class HexStringTest extends TestCase { + + @Test + public void testMarshalling() throws Exception { + String dpidStr = "00:00:00:23:20:2d:16:71"; + long dpid = HexString.toLong(dpidStr); + String testStr = HexString.toHexString(dpid); + TestCase.assertEquals(dpidStr, testStr); + } + + @Test + public void testToLong() { + String dpidStr = "3e:1f:01:fc:72:8c:63:31"; + long valid = 0x3e1f01fc728c6331L; + long testLong = HexString.toLong(dpidStr); + TestCase.assertEquals(valid, testLong); + } + + @Test + public void testToLongMSB() { + String dpidStr = "ca:7c:5e:d1:64:7a:95:9b"; + long valid = -3856102927509056101L; + long testLong = HexString.toLong(dpidStr); + TestCase.assertEquals(valid, testLong); + } + + @Test + public void testToLongError() { + String dpidStr = "09:08:07:06:05:04:03:02:01"; + try { + HexString.toLong(dpidStr); + fail("HexString.toLong() should have thrown a NumberFormatException"); + } + catch (NumberFormatException expected) { + // do nothing + } + } + + @Test + public void testToStringBytes() { + byte[] dpid = { 0, 0, 0, 0, 0, 0, 0, -1 }; + String valid = "00:00:00:00:00:00:00:ff"; + String testString = HexString.toHexString(dpid); + TestCase.assertEquals(valid, testString); + } + + @Test + public void testFromHexStringError() { + String invalidStr = "00:00:00:00:00:00:ffff"; + try { + HexString.fromHexString(invalidStr); + fail("HexString.fromHexString() should have thrown a NumberFormatException"); + } + catch (NumberFormatException expected) { + // do nothing + } + } +} diff --git a/src/test/java/org/openflow/util/OFTestCase.java b/src/test/java/org/openflow/util/OFTestCase.java new file mode 100644 index 0000000000000000000000000000000000000000..07bee2d589a1901ad1f77afc3a2ee262a48be429 --- /dev/null +++ b/src/test/java/org/openflow/util/OFTestCase.java @@ -0,0 +1,36 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +import org.openflow.protocol.factory.BasicFactory; +import org.openflow.protocol.factory.OFMessageFactory; + +import junit.framework.TestCase; + +public class OFTestCase extends TestCase { + public OFMessageFactory messageFactory; + + @Override + protected void setUp() throws Exception { + super.setUp(); + messageFactory = BasicFactory.getInstance(); + } + + public void test() throws Exception { + } +} diff --git a/src/test/java/org/openflow/util/U16Test.java b/src/test/java/org/openflow/util/U16Test.java new file mode 100644 index 0000000000000000000000000000000000000000..ba87e7b122d5cdb101a6d5a74898a612cb57fcdd --- /dev/null +++ b/src/test/java/org/openflow/util/U16Test.java @@ -0,0 +1,33 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +import junit.framework.TestCase; + +public class U16Test extends TestCase { + /** + * Tests that we correctly translate unsigned values in and out of a short + * @throws Exception + */ + public void test() throws Exception { + int val = 0xffff; + TestCase.assertEquals((short)-1, U16.t(val)); + TestCase.assertEquals((short)32767, U16.t(0x7fff)); + TestCase.assertEquals(val, U16.f((short)-1)); + } +} diff --git a/src/test/java/org/openflow/util/U32Test.java b/src/test/java/org/openflow/util/U32Test.java new file mode 100644 index 0000000000000000000000000000000000000000..223c1034e74cef66403f3c786a30f771a88b970b --- /dev/null +++ b/src/test/java/org/openflow/util/U32Test.java @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +import junit.framework.TestCase; + +public class U32Test extends TestCase { + /** + * Tests that we correctly translate unsigned values in and out of an int + * @throws Exception + */ + public void test() throws Exception { + long val = 0xffffffffL; + TestCase.assertEquals(-1, U32.t(val)); + TestCase.assertEquals(val, U32.f(-1)); + } +} diff --git a/src/test/java/org/openflow/util/U64Test.java b/src/test/java/org/openflow/util/U64Test.java new file mode 100644 index 0000000000000000000000000000000000000000..0a97e302f78e84700a0407b5770766eea339ffd4 --- /dev/null +++ b/src/test/java/org/openflow/util/U64Test.java @@ -0,0 +1,34 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +import java.math.BigInteger; + +import junit.framework.TestCase; + +public class U64Test extends TestCase { + /** + * Tests that we correctly translate unsigned values in and out of a long + * @throws Exception + */ + public void test() throws Exception { + BigInteger val = new BigInteger("ffffffffffffffff", 16); + TestCase.assertEquals(-1, U64.t(val)); + TestCase.assertEquals(val, U64.f(-1)); + } +} diff --git a/src/test/java/org/openflow/util/U8Test.java b/src/test/java/org/openflow/util/U8Test.java new file mode 100644 index 0000000000000000000000000000000000000000..2c06c4db31f56699dbcac37d1f4296d1ad16342d --- /dev/null +++ b/src/test/java/org/openflow/util/U8Test.java @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +import junit.framework.TestCase; + +public class U8Test extends TestCase { + /** + * Tests that we correctly translate unsigned values in and out of a byte + * @throws Exception + */ + public void test() throws Exception { + short val = 0xff; + TestCase.assertEquals(-1, U8.t(val)); + TestCase.assertEquals(val, U8.f((byte)-1)); + } +} diff --git a/src/test/java/org/openflow/util/UnsignedTest.java b/src/test/java/org/openflow/util/UnsignedTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7cdf7391bccd390ca941338cf99563dbb9da229c --- /dev/null +++ b/src/test/java/org/openflow/util/UnsignedTest.java @@ -0,0 +1,83 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* 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.openflow.util; + +import java.math.BigInteger; +import java.nio.ByteBuffer; + +import junit.framework.TestCase; + +public class UnsignedTest extends TestCase { + public static String ULONG_MAX = "18446744073709551615"; + + /** + * Tests that we correctly extract an unsigned long into a BigInteger + * @throws Exception + */ + public void testGetUnsignedLong() throws Exception { + ByteBuffer bb = ByteBuffer.allocate(8); + bb.put((byte)0xff).put((byte)0xff).put((byte)0xff).put((byte)0xff); + bb.put((byte)0xff).put((byte)0xff).put((byte)0xff).put((byte)0xff); + bb.position(0); + bb.limit(8); + BigInteger bi = Unsigned.getUnsignedLong(bb); + BigInteger uLongMax = new BigInteger(ULONG_MAX); + for (int i = 0; i < uLongMax.bitCount(); ++i) { + TestCase.assertTrue("Bit: " + i + " should be: " + uLongMax.testBit(i), + uLongMax.testBit(i) == bi.testBit(i)); + } + TestCase.assertEquals(ULONG_MAX, bi.toString()); + + bb = ByteBuffer.allocate(10); + bb.put((byte)0x00); + bb.put((byte)0xff).put((byte)0xff).put((byte)0xff).put((byte)0xff); + bb.put((byte)0xff).put((byte)0xff).put((byte)0xff).put((byte)0xff); + bb.put((byte)0x00); + bb.position(0); + bb.limit(10); + bi = Unsigned.getUnsignedLong(bb, 1); + uLongMax = new BigInteger(ULONG_MAX); + for (int i = 0; i < uLongMax.bitCount(); ++i) { + TestCase.assertTrue("Bit: " + i + " should be: " + uLongMax.testBit(i), + uLongMax.testBit(i) == bi.testBit(i)); + } + TestCase.assertEquals(ULONG_MAX, bi.toString()); + } + + /** + * Tests that we correctly put an unsigned long into a ByteBuffer + * @throws Exception + */ + public void testPutUnsignedLong() throws Exception { + ByteBuffer bb = ByteBuffer.allocate(8); + BigInteger uLongMax = new BigInteger(ULONG_MAX); + Unsigned.putUnsignedLong(bb, uLongMax); + for (int i = 0; i < 8; ++i) { + TestCase.assertTrue("Byte: " + i + " should be 0xff, was: " + bb.get(i), + (bb.get(i) & (short)0xff) == 0xff); + } + + bb = ByteBuffer.allocate(10); + Unsigned.putUnsignedLong(bb, uLongMax, 1); + int offset = 1; + for (int i = 0; i < 8; ++i) { + TestCase.assertTrue("Byte: " + i + " should be 0xff, was: " + + bb.get(offset+i), (bb.get(offset+i) & (short)0xff) == 0xff); + } + } +}