/* * Copyright 2016 christopher.metter. * * 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 de.uniwuerzburg.info3.ofcprobe.vswitch.runner; import java.io.IOException; import java.net.ConnectException; import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import org.openflow.protocol.OFHello; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.uniwuerzburg.info3.ofcprobe.vswitch.connection.IOFConnection; import de.uniwuerzburg.info3.ofcprobe.vswitch.connection.OFConnection1_zero; import de.uniwuerzburg.info3.ofcprobe.vswitch.main.Main; import de.uniwuerzburg.info3.ofcprobe.vswitch.main.config.Config; import de.uniwuerzburg.info3.ofcprobe.vswitch.main.config.RunnerConfig; /** * This Class reasambles a Thread who has controll over a defined count of * ofSwitch objects. * * @author Christopher Metter(christopher.metter@informatik.uni-wuerzburg.de) */ public class OFSwitchRunner implements Runnable { /** * The debug instance for this object */ private static final Logger logger = LoggerFactory.getLogger(OFSwitchRunner.class); /** * List of 'connected' ofSwitches */ private List<IOFConnection> switches; /** * the DPID of the first ofSwitch */ private int startdpid; /** * the Amount of ofSwitches that have to be initiated */ private int countswitches; /** * The Selector for the connectionhandling */ private Selector selector; /** * the OpenFlow Protocol Version used in this Run */ private int openflow_version; /** * When has this Instance been Initiated? */ private Date initialized; /** * The Configuration for this Session */ private Config config; /** * Has the BenchingSession already ended? */ private boolean sessionEnded; /** * Already one ofSwitch connected ? */ private boolean selectorInitialized; /** * The corresponding starting Thread */ private Main main; /** * Creates a new OFSwitchRunner with the Parameters of the provided * config-object. * * @param config the config-object */ public OFSwitchRunner(Config config, Main main) { this.main = main; //initialize Lists this.switches = new ArrayList<IOFConnection>(); this.sessionEnded = false; this.selectorInitialized = false; this.config = config; RunnerConfig runConfig = config.getRunnerConfig(); this.startdpid = runConfig.getStartDpid(); this.countswitches = runConfig.getCountswitches(); this.openflow_version = config.getOFVersion(); // Here the OpenFlowJ Library Version is chosen // Only supports OpenFlow Version 1.0 right now! if (this.openflow_version == 1) { init1_zero(); } } /** * Is switch connected? * * @param switchNo SwitchNo 1 -> index 0! * @return true-> this switch is Connected, false-> this switch is not * connected! */ public boolean isConnected(int switchNo) { if (switchNo - 1 < this.switches.size()) { return this.switches.get(switchNo - 1).getChannel().isConnected(); } else { return false; } } /** * All Switches ready for input? * * @return true if All ofSwitches Ready */ public boolean isReady() { Date now = new Date(); boolean allConnected = true; for (IOFConnection ofSwitch : this.switches) { if (ofSwitch.getChannel() != null) { if (!ofSwitch.getChannel().isConnected()) { allConnected = false; } } } if (!allConnected || now.getTime() - this.initialized.getTime() < 10000) { return false; } else { return true; } } /** * Init Switches for OpenFlow Protocol version 1.0 */ private void init1_zero() { this.initialized = new Date(); // For loop that instantiates the ofSwitch Objects for (int i = 0; i < this.countswitches; i++) { int dpid = i + this.startdpid; // Method names should speak for whats happening this.config.getSwitchConfig().setDpid(dpid); IOFConnection ofcon = new OFConnection1_zero(this, this.config); // add ofSwitch to our Collection this.switches.add(ofcon); } logger.info("All {} Switches have been initiated!", this.countswitches); logger.info("Controller-Address: {}", this.config.getSwitchConfig().getContAddress().toString()); } /** * Configure this Channel. * * @param chan the Channel to Configure * @throws IOException is processed by calling Method */ private SocketChannel configureChannel(SocketChannel chan) throws IOException { chan.configureBlocking(false); if (this.config.getSwitchConfig().disableNagle()) { // Disable Nagle's Alogrithm logger.trace("Nagle Algorithm Disabled!"); chan.socket().setTcpNoDelay(true); } return chan; } /** * Initializes the Selector * * @return the initialized Selector * @throws IOException handled by calling Method */ private Selector initSelector() throws IOException { // Create a new selector Selector socketSelector = SelectorProvider.provider().openSelector(); return socketSelector; } /** * Connection handling happens here. */ @Override public void run() { while (!Thread.interrupted()) { try { // check if handshakes done (10sek after initialization) if (isReady()) { // and then start sending the generated and queued packets queueProcessing(); } // the actual connection handling if (this.selector != null) { this.selector.selectNow(); Iterator<SelectionKey> selectedKeys = this.selector.selectedKeys().iterator(); // Process all keys while (selectedKeys.hasNext()) { SelectionKey key = (SelectionKey) selectedKeys.next(); selectedKeys.remove(); // Check whether key is a valid one if (!key.isValid()) { continue; } // Try to process key try { processKey(key); } catch (ConnectException e) { logger.error("Cannot connect to Controller! Is it started yet?"); logger.error("Exiting ..."); System.exit(1); } catch (IOException e) { logger.error("{}", e); } } } } catch (ClosedSelectorException e) { logger.error("Selector has been closed while operating!"); } catch (IOException e) { logger.error("{}", e); } } } /** * Process PacketInQueue of every ofSwitch */ private void queueProcessing() { boolean packetOutQueueEmpty = false; while (!packetOutQueueEmpty) { if (this.sessionEnded) { break; } packetOutQueueEmpty = true; for (IOFConnection ofSwitch : this.switches) { if (!outQueue(ofSwitch)) { packetOutQueueEmpty = false; } } for (IOFConnection ofSwitch : this.switches) { ofSwitch.receive(); } } } /** * Process outGoing Queue of a ofSwitch * * @param ofSwitch will be processed * @return flag */ private boolean outQueue(IOFConnection ofSwitch) { boolean packetOutQueueEmpty; if (ofSwitch.hasPacketInQueued()) { packetOutQueueEmpty = false; ofSwitch.sendPacketIn(); } else { packetOutQueueEmpty = true; } return packetOutQueueEmpty; } /** * KeyProcessor. * * @param key SelectionKey * @throws IOException */ private void processKey(SelectionKey key) throws IOException { try { //Get the Channel of this Key SocketChannel chan = (SocketChannel) key.channel(); if (key.isReadable() && chan.isOpen() && key.isValid()) { // Get the IOFConnection/the Switch for this Channel IOFConnection toRead = (IOFConnection) key.attachment(); if (toRead != null) { // Process Incoming Message toRead.receive(); } } if (key.isWritable() && chan.isOpen() && key.isValid()) { //write queuedMsgs(this.packetQueues) to channel write(chan, key); } if (key.isConnectable()) { // connect selected Channel connect(chan, key); } } catch (CancelledKeyException e) { logger.error("Selector has been closed while operating!"); } } /** * Method which writes queued Payloads from queuePacketInEvent(..) to the * Socket of the corresponding ofSwitch * * @param chan the channel (and with it connected: the ofSwitch) * @param key SelectionKey */ private void write(SocketChannel chan, SelectionKey key) { } /** * Finish Connection for SocketChannel * * @param chan * @param key * @throws IOException */ private void connect(SocketChannel chan, SelectionKey key) throws IOException { if (chan.finishConnect()) { key.interestOps(key.interestOps() ^ SelectionKey.OP_CONNECT); key.interestOps(key.interestOps() | SelectionKey.OP_READ); } } /** * Get Switches of this OFSwitchRunner * * @return List containing ofSwitches of this OFswitchRunner */ public List<IOFConnection> getSwitches() { return this.switches; } /** * Send to all connected ofSwitches the evaluate command */ public void evaluate() { for (IOFConnection ofswitch : this.switches) { ofswitch.evaluate(); } } /** * Send all connected ofSwitches the report command */ public void report() { for (IOFConnection ofswitch : this.switches) { ofswitch.report(); } } /** * Set flag in all switches so that packets comming in/going out are now * processed by the Statistic modules. */ public void startBenching() { for (IOFConnection ofswitch : this.switches) { ofswitch.startSession(); } } /** * Closes Connection between OpenFlowController and vSwitch */ public void endSession() { this.sessionEnded = true; for (IOFConnection ofswitch : this.switches) { ofswitch.stopSession(); } } /** * The Selector for this Thread. * * @return the selector */ public Selector getSelector() { return selector; } /** * Last Time a Packet needed for Benching from a controller came in * * @return time in millis as long */ public long lastPacketInTime() { long lastPacketInTime = new Date().getTime(); for (IOFConnection ofswitch : this.switches) { if (lastPacketInTime > ofswitch.lastPacketInTime()) { lastPacketInTime = ofswitch.lastPacketInTime(); } } return lastPacketInTime; } /** * Have all instantiated ofSwitches had OpenFlow Messages? Needed e.g. for * NOX: after successfull TCP Session, NOX does NOTHING, so each 'switch' * has to send a OFHello for Handshake with Floodlight sending a OFHello at * any time follows an immediate Disconnect from the Controller. */ public void alrdyOpenFlowed() { for (IOFConnection ofSwitch : this.switches) { alrdyOpenFlowed(ofSwitch); } } /** * Has provided ofSwitch had OpenFlow Messages? Needed e.g. for NOX: after * successfull TCP Session, NOX does NOTHING, so each 'switch' has to send a * OFHello for Handshake with Floodlight sending a OFHello at any time * follows an immediate Disconnect from the Controller. * * @param ofSwitch */ public void alrdyOpenFlowed(IOFConnection ofSwitch) { if (!ofSwitch.hadOFComm()) { ofSwitch.send(new OFHello()); } } /** * Connects Provided ofSwitch and then adds it to the Selector * * @param ofSwitch ofSwitch to connect */ public void initOFSwitchConnections(IOFConnection ofSwitch) { if (!this.selectorInitialized) { try { this.selector = initSelector(); this.selectorInitialized = true; } catch (IOException e) { logger.error("Selectorinit Failed: {}", e); } } // Create a new non-blocking socket channel SocketChannel chan; try { chan = SocketChannel.open(); chan = configureChannel(chan); // Set this Channel to an OfSwitch -> all Communication of this ofSwitch will happen over this channel ofSwitch.setChannel(chan); // Connect Socket; chan.connect(this.config.getSwitchConfig().getContAddress()); // Register the socket channel, indicating an interest in // accepting new connections and attaching the ofSwitch object chan.register(this.selector, SelectionKey.OP_CONNECT, ofSwitch); } catch (IOException e) { // Auto-generated catch block logger.error("ofSwitch Connect Failed: {}", e); } } /** * Gets the Main Method * * @return */ public Main getMain() { return this.main; } /** * Gets you the ofSwitch with DPID=dpid * * @param dpid the DPID * @return the ofSwitch */ public IOFConnection getOfSwitch(long dpid) { Iterator<IOFConnection> iter = this.switches.iterator(); while (iter.hasNext()) { IOFConnection ofSwitch = iter.next(); if (ofSwitch.getDpid() == dpid) { return ofSwitch; } } return null; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + countswitches; result = prime * result + ((initialized == null) ? 0 : initialized.hashCode()); result = prime * result + ((main == null) ? 0 : main.hashCode()); result = prime * result + openflow_version; result = prime * result + (selectorInitialized ? 1231 : 1237); result = prime * result + (sessionEnded ? 1231 : 1237); result = prime * result + startdpid; 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; } OFSwitchRunner other = (OFSwitchRunner) obj; if (countswitches != other.countswitches) { return false; } if (initialized == null) { if (other.initialized != null) { return false; } } else if (!initialized.equals(other.initialized)) { return false; } if (main == null) { if (other.main != null) { return false; } } else if (!main.equals(other.main)) { return false; } if (openflow_version != other.openflow_version) { return false; } if (selectorInitialized != other.selectorInitialized) { return false; } if (sessionEnded != other.sessionEnded) { return false; } if (startdpid != other.startdpid) { return false; } return true; } }