@Override public TableId setMaxTableForTableMissFlow(TableId max) { if (max.getValue() >= nTables) { maxTableToGetTableMissFlow = TableId.of(nTables - 1 < 0 ? 0 : nTables - 1); } else { maxTableToGetTableMissFlow = max; } return maxTableToGetTableMissFlow; }
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; OFFlowStatsEntryVer10 other = (OFFlowStatsEntryVer10) obj; if (tableId == null) { if (other.tableId != null) return false; } else if (!tableId.equals(other.tableId)) return false; if (match == null) { if (other.match != null) return false; } else if (!match.equals(other.match)) return false; if (durationSec != other.durationSec) return false; if (durationNsec != other.durationNsec) return false; if (priority != other.priority) return false; if (idleTimeout != other.idleTimeout) return false; if (hardTimeout != other.hardTimeout) return false; if (cookie == null) { if (other.cookie != null) return false; } else if (!cookie.equals(other.cookie)) return false; if (packetCount == null) { if (other.packetCount != null) return false; } else if (!packetCount.equals(other.packetCount)) return false; if (byteCount == null) { if (other.byteCount != null) return false; } else if (!byteCount.equals(other.byteCount)) return false; if (actions == null) { if (other.actions != null) return false; } else if (!actions.equals(other.actions)) return false; return true; }
@Override public OFFlowStatsEntry readFrom(ByteBuf bb) throws OFParseError { int start = bb.readerIndex(); int length = U16.f(bb.readShort()); if (length < MINIMUM_LENGTH) throw new OFParseError( "Wrong length: Expected to be >= " + MINIMUM_LENGTH + ", was: " + length); if (bb.readableBytes() + (bb.readerIndex() - start) < length) { // Buffer does not have all data yet bb.readerIndex(start); return null; } if (logger.isTraceEnabled()) logger.trace("readFrom - length={}", length); TableId tableId = TableId.readByte(bb); // pad: 1 bytes bb.skipBytes(1); long durationSec = U32.f(bb.readInt()); long durationNsec = U32.f(bb.readInt()); int priority = U16.f(bb.readShort()); int idleTimeout = U16.f(bb.readShort()); int hardTimeout = U16.f(bb.readShort()); Set<OFFlowModFlags> flags = OFFlowModFlagsSerializerVer14.readFrom(bb); int importance = U16.f(bb.readShort()); // pad: 2 bytes bb.skipBytes(2); U64 cookie = U64.ofRaw(bb.readLong()); U64 packetCount = U64.ofRaw(bb.readLong()); U64 byteCount = U64.ofRaw(bb.readLong()); Match match = ChannelUtilsVer14.readOFMatch(bb); List<OFInstruction> instructions = ChannelUtils.readList(bb, length - (bb.readerIndex() - start), OFInstructionVer14.READER); OFFlowStatsEntryVer14 flowStatsEntryVer14 = new OFFlowStatsEntryVer14( tableId, durationSec, durationNsec, priority, idleTimeout, hardTimeout, flags, importance, cookie, packetCount, byteCount, match, instructions); if (logger.isTraceEnabled()) logger.trace("readFrom - read={}", flowStatsEntryVer14); return flowStatsEntryVer14; }
@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((tableId == null) ? 0 : tableId.hashCode()); result = prime * result + ((match == null) ? 0 : match.hashCode()); result = prime * (int) (durationSec ^ (durationSec >>> 32)); result = prime * (int) (durationNsec ^ (durationNsec >>> 32)); result = prime * result + priority; result = prime * result + idleTimeout; result = prime * result + hardTimeout; result = prime * result + ((cookie == null) ? 0 : cookie.hashCode()); result = prime * result + ((packetCount == null) ? 0 : packetCount.hashCode()); result = prime * result + ((byteCount == null) ? 0 : byteCount.hashCode()); result = prime * result + ((actions == null) ? 0 : actions.hashCode()); return result; }
@Override public OFFlowMod buildFlowMod() { Match match = buildMatch(); List<OFAction> deferredActions = buildActions(treatment.deferred()); List<OFAction> immediateActions = buildActions(treatment.immediate()); List<OFInstruction> instructions = Lists.newLinkedList(); if (immediateActions.size() > 0) { instructions.add(factory().instructions().applyActions(immediateActions)); } if (treatment.clearedDeferred()) { instructions.add(factory().instructions().clearActions()); } if (deferredActions.size() > 0) { instructions.add(factory().instructions().writeActions(deferredActions)); } if (treatment.tableTransition() != null) { instructions.add(buildTableGoto(treatment.tableTransition())); } if (treatment.writeMetadata() != null) { instructions.add(buildMetadata(treatment.writeMetadata())); } if (treatment.metered() != null) { instructions.add(buildMeter(treatment.metered())); } long cookie = flowRule().id().value(); OFFlowMod fm = factory() .buildFlowModify() .setXid(xid) .setCookie(U64.of(cookie)) .setBufferId(OFBufferId.NO_BUFFER) .setInstructions(instructions) .setMatch(match) .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) .setPriority(flowRule().priority()) .setTableId(TableId.of(flowRule().tableId())) .build(); return fm; }
@Override public OFFlowDelete buildFlowDel() { Match match = buildMatch(); long cookie = flowRule().id().value(); OFFlowDelete fm = factory() .buildFlowDelete() .setXid(xid) .setCookie(U64.of(cookie)) .setBufferId(OFBufferId.NO_BUFFER) .setMatch(match) .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) .setPriority(flowRule().priority()) .setTableId(TableId.of(flowRule().tableId())) .build(); return fm; }
// send openflow flow stats request message with getting the specific flow entry(fe) to a given // switch sw private void ofFlowStatsRequestFlowSend(FlowEntry fe) { // set find match Match match = FlowModBuilder.builder(fe, sw.factory(), Optional.empty(), Optional.of(driverService)) .buildMatch(); // set find tableId TableId tableId = TableId.of(fe.tableId()); // set output port Instruction ins = fe.treatment() .allInstructions() .stream() .filter(i -> (i.type() == Instruction.Type.OUTPUT)) .findFirst() .orElse(null); OFPort ofPort = OFPort.NO_MASK; if (ins != null) { Instructions.OutputInstruction out = (Instructions.OutputInstruction) ins; ofPort = OFPort.of((int) ((out.port().toLong()))); } OFFlowStatsRequest request = sw.factory() .buildFlowStatsRequest() .setMatch(match) .setTableId(tableId) .setOutPort(ofPort) .build(); synchronized (this) { if (getFlowMissingXid() != NO_FLOW_MISSING_XID) { log.debug( "ofFlowStatsRequestFlowSend: previous FlowStatsRequestAll does not be processed yet," + " set no flow missing xid anyway, for {}", sw.getStringId()); setFlowMissingXid(NO_FLOW_MISSING_XID); } sw.sendMsg(request); } }
private OFInstruction buildTableGoto(Instructions.TableTypeTransition i) { OFInstruction instruction = factory().instructions().gotoTable(TableId.of(i.tableId())); return instruction; }
/** 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 Collection<TableId> tables; protected short nTables; protected final DatapathId datapathId; private Map<TableId, TableFeatures> tableFeaturesByTableId; 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 SwitchStatus status; public static final int OFSWITCH_APP_ID = ident(5); private TableId maxTableToGetTableMissFlow = TableId.of(4); /* this should cover most HW switches that have a couple SW flow tables */ 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.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); this.tableFeaturesByTableId = new HashMap<TableId, TableFeatures>(); this.tables = new ArrayList<TableId>(); } private static int ident(int i) { return i; } @Override public OFFactory getOFFactory() { return factory; } /** * Manages the ports of this switch. * * <p>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. * * <p>Port names are stored as-is but they are compared case-insensitive * * <p>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. * * <p>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 * * <p>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.LINK_DOWN) && !p.getConfig().contains(OFPortConfig.PORT_DOWN)) { 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. * * <p>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. * * <p>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. * * <p>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.LINK_DOWN) && !prevPort.getConfig().contains(OFPortConfig.PORT_DOWN)) && (newPort.getState().contains(OFPortState.LINK_DOWN) || newPort.getConfig().contains(OFPortConfig.PORT_DOWN))) { events.add(new PortChangeEvent(newPort, PortChangeType.DOWN)); } else if ((prevPort.getState().contains(OFPortState.LINK_DOWN) || prevPort.getConfig().contains(OFPortConfig.PORT_DOWN)) && (!newPort.getState().contains(OFPortState.LINK_DOWN) && !newPort.getConfig().contains(OFPortConfig.PORT_DOWN))) { 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 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> 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) * * <p>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); } // Enabled = not down admin (config) or phys (state) if (!p.getConfig().contains(OFPortConfig.PORT_DOWN) && !p.getState().contains(OFPortState.LINK_DOWN)) { 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) { log.trace( "Channel: {}, Connected: {}", connections.get(OFAuxId.MAIN).getRemoteInetAddress(), connections.get(OFAuxId.MAIN).isConnected()); if (isActive()) { connections.get(OFAuxId.MAIN).write(m); switchManager.handleOutgoingMessage(this, m); } else { log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString()); } } /** * 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) { if (isActive()) { this.getConnection(category).write(m); switchManager.handleOutgoingMessage(this, m); } else { log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString()); } } @Override public void write(Iterable<OFMessage> msglist, LogicalOFMessageCategory category) { if (isActive()) { this.getConnection(category).write(msglist); for (OFMessage m : msglist) { switchManager.handleOutgoingMessage(this, m); } } else { log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString()); } } @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) { if (isActive()) { connections.get(OFAuxId.MAIN).write(msglist); for (OFMessage m : msglist) { switchManager.handleOutgoingMessage(this, m); } } else { log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString()); } } @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()); } log.debug("~~~~~~~SWITCH DISCONNECTED~~~~~~"); // Remove all counters from the store 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) { /* OF1.3+ Per-table actions are set later in the OFTableFeaturesRequest/Reply */ this.actions = featuresReply.getActions(); } this.nTables = 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 void processOFTableFeatures(List<OFTableFeaturesStatsReply> replies) { /* * Parse out all the individual replies for each table. */ for (OFTableFeaturesStatsReply reply : replies) { /* * Add or update the features for a particular table. */ List<OFTableFeatures> tfs = reply.getEntries(); for (OFTableFeatures tf : tfs) { tableFeaturesByTableId.put(tf.getTableId(), TableFeatures.of(tf)); tables.add(tf.getTableId()); log.trace( "Received TableFeatures for TableId {}, TableName {}", tf.getTableId().toString(), tf.getName()); } } } @Override public Collection<OFPortDesc> getSortedPorts() { 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.BLOCKED) && !p.getState().contains(OFPortState.LINK_DOWN) && !p.getState().contains(OFPortState.STP_BLOCK)); } @Override public boolean portEnabled(String portName) { OFPortDesc p = portManager.getPort(portName); if (p == null) return false; return (!p.getState().contains(OFPortState.BLOCKED) && !p.getState().contains(OFPortState.LINK_DOWN) && !p.getState().contains(OFPortState.STP_BLOCK)); } @Override public DatapathId getId() { if (datapathId == null) throw new RuntimeException("Features reply has not yet been set"); return datapathId; } /* (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 addInternalStatsReplyListener( connections.get(OFAuxId.MAIN).writeStatsRequest(request), request); } @Override public <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> writeStatsRequest( OFStatsRequest<REPLY> request, LogicalOFMessageCategory category) { return addInternalStatsReplyListener( getConnection(category).writeStatsRequest(request), request); } /** * Append a listener to receive an OFStatsReply and update the internal OFSwitch data structures. * * <p>This presently taps into the following stats request messages to listen for the * corresponding reply: -- OFTableFeaturesStatsRequest * * <p>Extend this to tap into and update other OFStatsType messages. * * @param future * @param request * @return */ private <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> addInternalStatsReplyListener( final ListenableFuture<List<REPLY>> future, OFStatsRequest<REPLY> request) { switch (request.getStatsType()) { case TABLE_FEATURES: /* case YOUR_CASE_HERE */ future.addListener( new Runnable() { /* * We know the reply will be a list of OFStatsReply. */ @SuppressWarnings("unchecked") @Override public void run() { /* * The OFConnection handles REPLY_MORE for us in the case there * are multiple OFStatsReply messages with the same XID. */ try { List<? extends OFStatsReply> replies = future.get(); if (!replies.isEmpty()) { /* * By checking only the 0th element, we assume all others are the same type. * TODO If not, what then? */ switch (replies.get(0).getStatsType()) { case TABLE_FEATURES: processOFTableFeatures((List<OFTableFeaturesStatsReply>) future.get()); break; /* case YOUR_CASE_HERE */ default: throw new Exception( "Received an invalid OFStatsReply of " + replies.get(0).getStatsType().toString() + ". Expected TABLE_FEATURES."); } } } catch (Exception e) { e.printStackTrace(); } } }, MoreExecutors.sameThreadExecutor()); /* No need for another thread. */ default: break; } return future; /* either unmodified or with an additional listener */ } @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 || this.role == OFControllerRole.ROLE_EQUAL); } @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; } /** This performs a copy on each 'get'. Use sparingly for good performance. */ @Override public Collection<TableId> getTables() { return new ArrayList<TableId>(tables); } @Override public short getNumTables() { return this.nTables; } @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 void setSwitchProperties(SwitchDescription description) { this.description = description; } @Override public SwitchStatus getStatus() { return status; } @Override public void setStatus(SwitchStatus switchStatus) { this.status = switchStatus; } @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; } @Override public TableFeatures getTableFeatures(TableId table) { return tableFeaturesByTableId.get(table); } @Override public TableId getMaxTableForTableMissFlow() { return maxTableToGetTableMissFlow; } @Override public TableId setMaxTableForTableMissFlow(TableId max) { if (max.getValue() >= nTables) { maxTableToGetTableMissFlow = TableId.of(nTables - 1 < 0 ? 0 : nTables - 1); } else { maxTableToGetTableMissFlow = max; } return maxTableToGetTableMissFlow; } }