/*
* 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.connection;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import de.uniwuerzburg.info3.ofcprobe.util.AddressPositions;
import de.uniwuerzburg.info3.ofcprobe.util.Util;
import de.uniwuerzburg.info3.ofcprobe.vswitch.connection.buffer.BufferID;
import de.uniwuerzburg.info3.ofcprobe.vswitch.connection.buffer.SwitchBufferBitSet;
import de.uniwuerzburg.info3.ofcprobe.vswitch.connection.flowtable.OFFlowModHandler;
import de.uniwuerzburg.info3.ofcprobe.vswitch.main.config.Config;
import de.uniwuerzburg.info3.ofcprobe.vswitch.main.config.Topology;
import de.uniwuerzburg.info3.ofcprobe.vswitch.runner.OFSwitchRunner;
import de.uniwuerzburg.info3.ofcprobe.vswitch.statistics.ControllerCPUnRAMMonitor;
import de.uniwuerzburg.info3.ofcprobe.vswitch.statistics.IStatistics;
import de.uniwuerzburg.info3.ofcprobe.vswitch.statistics.PacketsPerSecond;
import de.uniwuerzburg.info3.ofcprobe.vswitch.statistics.RoundTripTime;
import de.uniwuerzburg.info3.ofcprobe.vswitch.statistics.TimeStampLogger;
import de.uniwuerzburg.info3.ofcprobe.vswitch.statistics.special.QueueLengthMonitor;
import de.uniwuerzburg.info3.ofcprobe.vswitch.trafficgen.arping.Device;
import org.openflow.io.OFMessageAsyncStream;
import org.openflow.protocol.OFBarrierReply;
import org.openflow.protocol.OFEchoReply;
import org.openflow.protocol.OFFeaturesReply;
import org.openflow.protocol.OFFlowMod;
import org.openflow.protocol.OFGetConfigReply;
import org.openflow.protocol.OFHello;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketOut;
import org.openflow.protocol.OFPhysicalPort;
import org.openflow.protocol.OFPort;
import org.openflow.protocol.OFSetConfig;
import org.openflow.protocol.OFStatisticsRequest;
import org.openflow.protocol.OFType;
import org.openflow.protocol.OFVendor;
import org.openflow.protocol.OFPacketIn.OFPacketInReason;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionOutput;
import org.openflow.protocol.action.OFActionType;
import org.openflow.protocol.factory.BasicFactory;
import org.openflow.util.HexString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Throwables;
import java.nio.BufferUnderflowException;
/**
* The Connection Handler aka ofSwitch for OpenFlow Version 1.0
*
* @author Christopher Metter(christopher.metter@informatik.uni-wuerzburg.de)
*
*/
public class OFConnection1_zero implements IOFConnection {
private Set<BufferID> savedBuffIds;
private Map<BufferID, byte[]> savedPayloads;
/**
* Last Used TransactionID for Packet_INs
*/
private int lastXid = 1;
/**
* The OpenFlow Version spoken by this ofSwitch Implementation
*/
private static byte OFP_VERSION = 0x01;
/**
* List of connected Statistic Modules
*/
private List<IStatistics> statistics;
/**
* The QueueLengthMonitor
*/
private QueueLengthMonitor queueLengthMonitor;
/**
* DataPathId
*/
private long dpid;
/**
* Debugger
*/
private static final Logger logger = LoggerFactory.getLogger(OFConnection1_zero.class);
/**
* Amount of Ports
*/
private int ports;
/**
* BufferSize
*/
private int bufferSize;
/**
* The Socket on which this ofSwitch communicates with the Controller.
*/
private SocketChannel socket;
/**
* Generated OFConfig-reply
*/
private OFGetConfigReply config_reply;
/**
* Generated OFFeaturesReply
*/
private OFFeaturesReply feat_reply;
/**
* Generated Config
*/
private Config config;
/**
* The OFMessageAsynStream used to write/read OFMessages of the Channel
*/
private OFMessageAsyncStream ofStream;
/**
* The Buffer used by this ofSwitch
*/
private SwitchBufferBitSet buffer;
/**
* The SessionCount, used for Foldername
*/
private int session;
/**
* If False -> OFMessages to send are only transmitted to Statistic methods
*/
private boolean sendFlag;
/**
* The Thread to this ofSwitch
*/
private OFSwitchRunner runner;
/**
* Date to save last PacketIN time
*/
private Date lastPacketIn;
/**
* Boolean to save info that this benchingsessions has ended.
*/
private boolean sessionStopped;
/**
* Flag to enable/disable batchSending of queued PacketIn msgs
*/
private boolean batchSending;
/**
* Flag for crashhandling(e.g. connection closed etc)
*/
private boolean crashed;
/**
* The PayloadQueue, only the TrafficGen writes here
*/
private List<QueuedPacket> incomingListTrafficGenerator;
/**
* The PayloadQueue of this Thread, only this Thread writes here.
*/
private List<QueuedPacket> incomingListSwitchRunner;
/**
* Bool to check if this ofSwitch already had OFComm
*/
private boolean hadOFComm;
/**
* How long to connection Delayed establishment delayed from initialzisation
*/
private long conDelay;
/**
* How long to Delay start from 'normal' Switches in Millis
*/
private long startDelay;
/**
* How long to Delay stop from 'normal' Switches in Millis
*/
private long stopDelay;
/**
* The Topology
*/
private Topology topology;
private int iat;
private int fillThreshold;
private OFFlowModHandler flowmod_handler;
private OFStatsHandler stats_handler;
private String dpidString;
private String pcapFileName;
private String iatDistri;
private double iatDistriPara1;
private double iatDistriPara2;
/**
* Constructor.
*
* @param starter the corresponding Thread/
* @param config the Configuration
*/
public OFConnection1_zero(OFSwitchRunner starter, Config config) {
// initialize Class Variables
this.sessionStopped = false;
this.statistics = new ArrayList<IStatistics>();
this.incomingListTrafficGenerator = new ArrayList<>();
this.incomingListSwitchRunner = new ArrayList<>();
this.crashed = false;
this.hadOFComm = false;
this.conDelay = 0;
this.startDelay = 0;
this.stopDelay = 0;
this.topology = null;
this.savedBuffIds = new HashSet<>();
this.savedPayloads = new HashMap<>();
this.flowmod_handler = new OFFlowModHandler(config);
this.stats_handler = new OFStatsHandler(config, this.flowmod_handler.getFlowTable());
// Load Values from Config
this.runner = starter;
this.config = config;
this.sendFlag = this.config.getSwitchConfig().getSendFlag();
this.batchSending = this.config.getSwitchConfig().getBatchSending();
// this.controller = this.config.getSwitchConfig().getContAddress();
this.dpid = this.config.getSwitchConfig().getDpid();
this.dpidString = this.config.getSwitchConfig().getDPIDString();
this.ports = this.config.getSwitchConfig().getPortCountperSwitch();
this.bufferSize = this.config.getSwitchConfig().getBuffersPerSwitch();
this.buffer = new SwitchBufferBitSet(this.bufferSize);
this.session = this.config.getSwitchConfig().getSession();
this.iat = config.getTrafficGenConfig().getIAT();
this.fillThreshold = config.getTrafficGenConfig().getFillThreshold();
this.pcapFileName = "notSet!";
this.iatDistri = "none!";
this.queueLengthMonitor = null;
if (config.hasTopology()) {
this.topology = config.getTopology();
}
if (this.config.checkForIndividualSettings()) {
loadIndividualSettings();
}
// Initialize Statistic Modules
initializeStatistics();
}
private void loadIndividualSettings() {
Properties props = new Properties();
String configFile = config.getIndividualSwitchSettingsFileName();
try {
props.load(new BufferedInputStream(new FileInputStream(configFile)));
this.conDelay = Long.parseLong(props.getProperty(dpidString + ".conDelay", "0"));
this.startDelay = Long.parseLong(props.getProperty(dpidString + ".start", "0"));
this.stopDelay = Long.parseLong(props.getProperty(dpidString + ".stop", "0"));
if (this.conDelay > 0) {
logger.info("Switch #{} successfully loaded Individual Switch ConDelay: {}", dpidString, this.conDelay);
}
if (this.startDelay > 0) {
logger.info("Switch #{} successfully loaded Individual Switch StartDelay: {}", dpidString, this.startDelay);
}
if (this.stopDelay != 0) {
logger.info("Switch #{} successfully loaded Individual Switch StopDelay: {}", dpidString, this.stopDelay);
}
this.iat = Integer.parseInt(props.getProperty(dpidString + ".iat", Integer.toString(config.getTrafficGenConfig().getIAT())));
if (this.iat != this.config.getTrafficGenConfig().getIAT()) {
logger.info("Switch #{} successfully loaded Individual Switch IAT: {}", dpidString, this.iat);
}
this.fillThreshold = Integer.parseInt(props.getProperty(dpidString + ".fillthreshold", Integer.toString(this.fillThreshold)));
if (this.fillThreshold != this.config.getTrafficGenConfig().getFillThreshold()) {
logger.info("Switch #{} successfully loaded Individual Switch Fillthreshold: {}", dpidString, this.fillThreshold);
}
if (this.fillThreshold != this.config.getTrafficGenConfig().getFillThreshold() || this.iat != this.config.getTrafficGenConfig().getIAT()) {
double targetPacketCount = (1000 / this.iat) * this.fillThreshold * this.config.getTrafficGenConfig().getCountPerEvent();
logger.info("Switch #{} Target Packets Generated per Second: {}", dpidString, targetPacketCount);
}
this.pcapFileName = props.getProperty(dpidString + ".pcapFile", "notSet!");
this.iatDistri = props.getProperty(dpidString + ".iatDistribution", "none");
this.iatDistriPara1 = Double.parseDouble(props.getProperty(dpidString + ".iatDistributionParamter1", "100.0"));
this.iatDistriPara2 = Double.parseDouble(props.getProperty(dpidString + ".iatDistributionParamter2", "1.0"));
} catch (FileNotFoundException e) {
logger.error("Could not find Switch-Config File!");
System.exit(-1);
} catch (NumberFormatException e) {
logger.error("Wrong Switch-ConfigFile Format!");
System.exit(-1);
} catch (IOException e) {
// Auto-generated catch block
e.printStackTrace();
}
}
/**
* Initializes the Statistic Modules, will be dynamically in the Future
*/
private void initializeStatistics() {
List<String> stats = this.config.getStatConfig().getStatModules();
NumberFormat formatter = new DecimalFormat("#000");
String dpidString = formatter.format(this.dpid);
Iterator<String> statIter = stats.iterator();
boolean snmpStarted = false;
while (statIter.hasNext()) {
String stat = statIter.next();
if (stat.equals("PPS")) {
// PPS Module
IStatistics pps = new PacketsPerSecond(this.config);
pps.setReportFile("./statistics/" + formatter.format(this.session) + "/pps." + dpidString + ".txt");
this.statistics.add(pps);
}
if (stat.equals("RTT")) {
// RTT Module
IStatistics rtt = new RoundTripTime(this.config);
rtt.setReportFile("./statistics/" + formatter.format(this.session) + "/rtt." + dpidString + ".txt");
this.statistics.add(rtt);
}
if (stat.equals("CPU") || stat.equals("RAM")) {
if (!snmpStarted) {
snmpStarted = true;
// Controller CPU Monitor
IStatistics cpuRam = new ControllerCPUnRAMMonitor(this.config);
cpuRam.setReportFile("./statistics/" + formatter.format(this.session) + "/cpu." + dpidString + ".txt");
cpuRam.setReportFile("./statistics/" + formatter.format(this.session) + "/ram." + dpidString + ".txt");
this.statistics.add(cpuRam);
}
}
if (stat.equals("TSL")) {
// Time Stamp Stuff for MJ
IStatistics timeStampLogger = new TimeStampLogger(this.config);
timeStampLogger.setReportFile("./statistics/" + formatter.format(this.session) + "/tsl." + dpidString + ".txt");
this.statistics.add(timeStampLogger);
}
if (stat.equals("QLM")) {
// QueueLength Monitor Modules
this.queueLengthMonitor = new QueueLengthMonitor(this.config);
this.queueLengthMonitor.setReportFile("./statistics/" + formatter.format(this.session) + "/qlm." + dpidString + ".txt");
}
}
}
/**
* Sets features for the FeatureRequest/Reply.
*
* @param portCount Count of Ports.
*/
private void setFeatures(int portCount, int bufferCount) {
this.feat_reply = new OFFeaturesReply();
this.feat_reply.setDatapathId(this.dpid);
this.feat_reply.setPorts(getPorts(portCount));
this.feat_reply.setCapabilities(199); // Following Values have
this.feat_reply.setActions(4095); // been taken
this.feat_reply.setBuffers(bufferCount); // from
this.feat_reply.setTables((byte) -1); // Open vSwitch
}
/**
* Builds some Ports for our Switch.
*
* @param portCount Count of Ports
* @return List of these Ports.
*/
private List<OFPhysicalPort> getPorts(int portCount) {
List<OFPhysicalPort> portList = new ArrayList<>();
long lastMAC = 0x000000000001L;
int MACAddress_WIDTH = 6;
for (int i = 0; i < portCount; i++) {
OFPhysicalPort port = new OFPhysicalPort();
byte[] hwAddress = new byte[6];
Util.insertLong(hwAddress, lastMAC++, 0, MACAddress_WIDTH);
port.setHardwareAddress(hwAddress);
port.setName("eth" + i);
port.setPortNumber((short) (i + 1));
portList.add(port);
}
return portList;
}
/**
* Safe config set by Controller.
*
* @param config OFSetConfig
*/
private void setConfig(OFSetConfig config) {
this.config_reply = new OFGetConfigReply();
this.config_reply.setFlags(config.getFlags());
this.config_reply.setMissSendLength(config.getMissSendLength());
}
/**
* Build a VENDOR OF Message.
*
* @return OFVendor reply
*/
private OFVendor buildVendorReply() {
OFVendor vendor = new OFVendor();
vendor.setVendor(42);
String data = "OFCProbe vSwitch";
vendor.setData(data.getBytes());
return vendor;
}
/**
* Checks if provided Port is local Port
*
* @param port
* @return true --> port is local
*/
private OFPhysicalPort getLocalPort(OFPacketOut po) {
// Action port check
short port = (short) -42;
if (po.getActions().size() == 1) {
OFAction action = po.getActions().get(0);
if (action.getType().equals(OFActionType.OUTPUT)) {
OFActionOutput outputAction = (OFActionOutput) action;
port = outputAction.getPort();
}
}
List<OFPhysicalPort> phyPorts = this.feat_reply.getPorts();
for (OFPhysicalPort phyPort : phyPorts) {
if (phyPort.getPortNumber() == port) {
return phyPort;
}
}
return null;
}
@Override
public void send(OFMessage out) {
packetOut(out);
out.computeLength();
if (this.sendFlag && !this.crashed) {
try {
if (!out.getType().equals(OFType.PACKET_IN)) // if (this.dpidString.equals("001"))
{
logger.trace("[Switch#{}]: Outgoing: {}", this.dpidString, out.toString());
}
this.ofStream.write(out);
while (this.ofStream.needsFlush()) {
this.ofStream.flush();
}
} catch (IOException e) {
this.runner.endSession(); // Tryin to get at least some data out of this
this.runner.evaluate();
this.runner.report();
logger.error("[Switch#{}]: " + Throwables.getStackTraceAsString(e), this.dpidString);
logger.error("[Switch#{}]: Connection has been closed by Remotehost - Has Controller Crashed?", this.dpidString);
logger.error("[Switch#{}]: Saving Data and Exiting ...", this.dpidString);
this.crashed = true;
} catch (NotYetConnectedException e) {
logger.warn("[Switch#{}]: Connection has not yet been connected! Will retry in 5sec", this.dpidString);
try {
Thread.sleep(5000);
send(out);
} catch (InterruptedException ie) {
logger.error("[Switch#{}]: Couldn't sleep... Going suicidal now!", this.dpidString);
System.exit(1);
}
}
}
}
@Override
public void receive() {
if (this.ofStream != null && !this.crashed) {
try {
List<OFMessage> ofmessages = this.ofStream.read();
if (ofmessages != null) {
if (!ofmessages.isEmpty()) {
Iterator<OFMessage> ofiter = ofmessages.iterator();
while (ofiter.hasNext()) {
receive(ofiter.next());
}
}
}
} catch (IOException e) {
this.runner.endSession(); // Tryin to get at least some data out of this
this.runner.evaluate();
this.runner.report();
this.crashed = true;
// If One of the Hosts(Controller or OFCProbe) is busy, Handshake is likely to fail and e.g. Floodlight will drop the Connection and so we land here
logger.debug("[Switch#{}]: " + Throwables.getStackTraceAsString(e), this.dpidString);
logger.error("[Switch#{}]: Connection has been closed by Remotehost - Has Controller Crashed?", this.dpidString);
logger.error("[Switch#{}]: Exiting ...", this.dpidString);
// System.exit(1);
} catch (BufferUnderflowException b) {
logger.debug("[Switch#{}]: " + Throwables.getStackTraceAsString(b), this.dpidString);
logger.error("[Switch#{}]: BufferUnderflowException! Maybe Controller tried other OpenFlowProtocl Version?", this.dpidString);
}
}
}
/**
* Process incoming OFMessage and answer to it, if needed
*
* @param incoming OFMessage incoming
*/
private void receive(OFMessage incoming) {
packetIn(incoming);
logger.trace("[Switch#{}]: Incoming Packet: {}", this.dpidString, incoming.toString());
logger.trace("[Switch#{}]: Flow Table size: {}", this.dpidString, this.flowmod_handler.getFlowTable().size());
// Create Reply depending on incoming Message
// Don't forget to setXid = incoming.getXid()
switch (incoming.getType()) {
case BARRIER_REPLY:
break;
case BARRIER_REQUEST:
OFBarrierReply barrier_reply = new OFBarrierReply();
barrier_reply.setXid(incoming.getXid());
send(barrier_reply);
break;
case ECHO_REPLY:
break;
case ECHO_REQUEST:
OFEchoReply echo_reply = new OFEchoReply();
echo_reply.setXid(incoming.getXid());
send(echo_reply);
break;
case ERROR:
break;
case FEATURES_REPLY:
break;
case FEATURES_REQUEST:
setFeatures(this.ports, this.bufferSize);
this.feat_reply.setXid(incoming.getXid());
send(this.feat_reply);
break;
case FLOW_MOD:
OFFlowMod flow_mod = (OFFlowMod) incoming;
List<OFMessage> replies = this.flowmod_handler.handleOFFlowMod(flow_mod);
for (OFMessage msg : replies) {
send(msg);
}
break;
case FLOW_REMOVED:
break;
case GET_CONFIG_REPLY:
break;
case GET_CONFIG_REQUEST:
OFGetConfigReply conf_reply = this.config_reply;
if (conf_reply != null) {
conf_reply.setXid(incoming.getXid());
} else {
conf_reply = new OFGetConfigReply();
conf_reply.setFlags((short) 0);
conf_reply.setMissSendLength((short) -1);
}
send(conf_reply);
break;
case HELLO:
this.hadOFComm = true;
OFHello hello = new OFHello();
hello.setXid(incoming.getXid());
send(hello);
break;
case PACKET_IN:
break;
case PACKET_OUT:
OFPacketOut packet_out = (OFPacketOut) incoming;
if (this.config.hasTopology() && isLLDP(packet_out)) {
short localPort = getLocalPort(packet_out).getPortNumber();
long connectedOfSwitch = this.topology.getConnectedOfSwitch(this.dpid, localPort);
IOFConnection ofSwitch = this.runner.getMain().getIOFConByDpid(connectedOfSwitch);
if (ofSwitch != null) {
ofSwitch.queuePacketIn(packet_out.getPacketData(), this.topology.getInPort(connectedOfSwitch, this.dpid), false);
}
}
BufferID buffID = new BufferID(packet_out.getBufferId());
if (this.savedBuffIds.contains(buffID)) {
handleBuffid(buffID, packet_out);
}
this.buffer.freeBuffer(packet_out.getBufferId());
break;
case PORT_MOD:
break;
case PORT_STATUS:
break;
case QUEUE_CONFIG_REPLY:
break;
case QUEUE_CONFIG_REQUEST:
break;
case SET_CONFIG:
OFSetConfig set_config = (OFSetConfig) incoming;
setConfig(set_config);
break;
case STATS_REPLY:
break;
case STATS_REQUEST:
OFStatisticsRequest stat_req = (OFStatisticsRequest) incoming;
OFMessage stats_rep = this.stats_handler.buildStatsReply(stat_req);
send(stats_rep);
break;
case VENDOR:
OFVendor vendor = buildVendorReply();
vendor.setXid(incoming.getXid());
send(vendor);
break;
default:
break;
}
}
/**
* Handles a saved Payload to a saved buffid
*
* @param buffId the buffid of the packet
* @param in_port port#
*/
private void handleBuffid(BufferID buffId, OFPacketOut packet_out) {
short in_port = packet_out.getInPort();
ArrayList<Short> outports = new ArrayList<>();
for (OFAction action : packet_out.getActions()) {
if (action.getType().equals(OFActionType.OUTPUT)) {
outports.add(((OFActionOutput) action).getPort());
}
}
byte[] payload = this.savedPayloads.remove(buffId);
this.savedBuffIds.remove(buffId);
if (isArp(payload)) {
logger.trace("[Switch#{}]: ARP Detected", this.dpidString);
if (!isArp4me(payload, buffId)) {
logger.trace("[Switch#{}]: ARP is not 4 me", this.dpidString);
List<Long> targetSwitches = new ArrayList<>();
for (short outport : outports) {
if (outport == OFPort.OFPP_FLOOD.getValue()) {
targetSwitches = this.topology.getConnectedOfSwitches(this.dpid);
break;
}
targetSwitches.add(this.topology.getConnectedOfSwitch(this.dpid, outport));
}
Long sourceSwitch = this.topology.getConnectedOfSwitch(this.dpid, in_port);
targetSwitches.remove(sourceSwitch);
// logger.info("[Switch#{}]: targetSwitches: {}", this.dpidString, targetSwitches.size());
if (targetSwitches.size() > 0) {
for (long dpid : targetSwitches) {
IOFConnection ofSwitch = this.runner.getMain().getIOFConByDpid(dpid);
if (ofSwitch != null) {
short port = this.topology.getInPort(ofSwitch.getDpid(), this.dpid);
logger.trace("[Switch#{}]: Switch#{} has now ARP queued on Port#{}", this.dpidString, ofSwitch.getDpid(), port);
ofSwitch.queuePacketIn(payload, port, true);
}
}
} else {
logger.trace("[Switch#{}]: ARP has no TargetSwitch!", this.dpidString);
}
}
}
if (isTCPSyn(payload)) {
logger.trace("[Switch#{}]: TCPSyN Detected", this.dpidString);
if (!isTCPSyN4me(payload, buffId)) {
logger.trace("[Switch#{}]: TCPSyN is not 4 me", this.dpidString);
List<Long> targetSwitches = new ArrayList<>();
for (short outport : outports) {
if (outport == OFPort.OFPP_FLOOD.getValue()) {
targetSwitches = this.topology.getConnectedOfSwitches(this.dpid);
break;
}
targetSwitches.add(this.topology.getConnectedOfSwitch(this.dpid, outport));
}
Long sourceSwitch = this.topology.getConnectedOfSwitch(this.dpid, in_port);
targetSwitches.remove(sourceSwitch);
// logger.info("[Switch#{}]: targetSwitches: {}", this.dpidString, targetSwitches.size());
if (targetSwitches.size() > 0) {
for (long dpid : targetSwitches) {
IOFConnection ofSwitch = this.runner.getMain().getIOFConByDpid(dpid);
short port = this.topology.getInPort(ofSwitch.getDpid(), this.dpid);
if (ofSwitch != null) {
logger.trace("[Switch#{}]: Switch#{} has now TCPSyN queued on Port#{}", this.dpidString, ofSwitch.getDpid(), port);
ofSwitch.queuePacketIn(payload, port, true);
}
}
} else {
logger.trace("[Switch#{}]: TCPSyN has no TargetSwitch!", this.dpidString);
}
}
}
}
/**
* Checks if packet is an TCPSyn
*
* @param packet the packet
* @return the bool
*/
private boolean isTCPSyn(byte[] packet) {
byte[] type = Util.getBytes(packet, AddressPositions.ETHER_TYPE, 2);
String typeStr = Util.asString(type);
if (typeStr.equals("0800")) {
byte[] protocol = Util.getBytes(packet, AddressPositions.IP_PROTOCOL, 1);
String protocolStr = Util.asString(protocol);
if (protocolStr.equals("06")) {
byte[] flags = Util.getBytes(packet, AddressPositions.TCP_FLAGS, 2);
String flagsStr = Util.asString(flags);
if (flagsStr.equals("8002")) {
return true;
}
}
}
return false;
}
/**
* Checks if packet is an ARP
*
* @param packet the packet
* @return the bool
*/
private boolean isArp(byte[] packet) {
byte[] protocol = Util.getBytes(packet, AddressPositions.ETHER_TYPE, 2);
String protocolStr = Util.asString(protocol);
if (protocolStr.equals("0806")) {
return true;
} else {
return false;
}
}
/**
* Checks if arp is 4 this switch
*
* @param packet the packet
* @return the bool
*/
private boolean isArp4me(byte[] packet, BufferID buffId) {
byte[] dstIP = Util.getBytes(packet, AddressPositions.ARP_IP_DST, 4);
String dstIPString = Util.fromIPv4Address(Util.toIPv4Address(dstIP));
logger.trace("[Switch#{}]: ARP DST-IP: {}", this.dpidString, dstIPString);
Device target = this.config.getTopology().getHostMapping().getDeviceToIp(dstIPString);
if (target != null) {
logger.trace("[Switch#{}]: Arp Target: {} " + this.config.getTopology().getHostMapping().getMacToDevice(target), this.dpidString, target.toString());
if (target.getOfSwitch().equals(this)) {
if (this.feat_reply.getPortMap().keySet().contains(target.getPort())) {
byte[] arpReply = arpReplyBuilder(packet);
queuePacketIn(arpReply, target.getPort(), false);
logger.trace("[Switch#{}]: ARP Reply queued!", this.dpidString);
return true;
}
}
} else {
logger.warn("[Switch#{}]: isArp4me: Target null", this.dpidString);
}
logger.trace("[Switch#{}]: ARP (BuffID: {};TargetDevice: {}) not 4 me: {}", this.dpidString, buffId.getBuffId(), target, dstIPString);
return false;
}
/**
* Checks if arp is 4 this switch
*
* @param packet the packet
* @return the bool
*/
private boolean isTCPSyN4me(byte[] packet, BufferID buffId) {
byte[] dstIP = Util.getBytes(packet, AddressPositions.IP_DST, 4);
String dstIPString = Util.fromIPv4Address(Util.toIPv4Address(dstIP));
logger.trace("[Switch#{}]: TCPSyN DST-IP: {}", this.dpidString, dstIPString);
Device target = this.config.getTopology().getHostMapping().getDeviceToIp(dstIPString);
if (target != null) {
logger.trace("[Switch#{}]: TCPSyN Target: {} " + this.config.getTopology().getHostMapping().getMacToDevice(target), this.dpidString, target.toString());
if (target.getOfSwitch().equals(this)) {
if (this.feat_reply.getPortMap().keySet().contains(target.getPort())) {
byte[] TCPSyNReply = TCPSyNaCKBuilder(packet);
queuePacketIn(TCPSyNReply, target.getPort(), false);
logger.trace("[Switch#{}]: TCP SYN/ACK queued!", this.dpidString);
return true;
}
}
} else {
logger.warn("[Switch#{}]: isTCPSyN4me: Target null", this.dpidString);
}
logger.trace("[Switch#{}]: TCP (BuffID: {};TargetDevice: {}) not 4 me: {}", this.dpidString, buffId.getBuffId(), target, dstIPString);
return false;
}
private byte[] TCPSyNaCKBuilder(byte[] packet) {
byte[] dstMAC = Util.getBytes(packet, AddressPositions.ETHER_MAC_SRC, 6);
byte[] dstIP = Util.getBytes(packet, AddressPositions.IP_SRC, 4);
byte[] srcMAC = Util.getBytes(packet, AddressPositions.ETHER_MAC_DST, 6);
byte[] srcIP = Util.getBytes(packet, AddressPositions.IP_DST, 4);
packet = Util.insertByteArray(packet, dstMAC, AddressPositions.ETHER_MAC_DST);
packet = Util.insertByteArray(packet, srcMAC, AddressPositions.ETHER_MAC_SRC);
packet = Util.insertByteArray(packet, srcIP, AddressPositions.IP_SRC);
packet = Util.insertByteArray(packet, dstIP, AddressPositions.IP_DST);
byte[] synAck = Util.toByteArray("12");
packet = Util.insertByteArray(packet, synAck, AddressPositions.TCP_FLAGS);
return packet;
}
/**
* Builds an arp reply for an arp for this switch
*
* @param packet the packet
* @return the arpreply
*/
private byte[] arpReplyBuilder(byte[] packet) {
byte[] srcIP = Util.getBytes(packet, AddressPositions.ARP_IP_DST, 4);
String srcIPstring = Util.fromIPv4Address(Util.toIPv4Address(srcIP));
String macString = this.topology.getHostMapping().getMacToIp(srcIPstring);
byte[] srcMac = HexString.fromHexString(macString);
byte[] dstIP = Util.getBytes(packet, AddressPositions.ARP_IP_SRC, 4);
byte[] dstMac = Util.getBytes(packet, AddressPositions.ETHER_MAC_SRC, 6);
byte[] opCode = Util.toByteArray("0002");
packet = Util.insertByteArray(packet, dstMac, AddressPositions.ETHER_MAC_DST);//eth
packet = Util.insertByteArray(packet, srcMac, AddressPositions.ETHER_MAC_SRC);
packet = Util.insertByteArray(packet, srcMac, AddressPositions.ARP_MAC_SRC);//arp
packet = Util.insertByteArray(packet, srcIP, AddressPositions.ARP_IP_SRC);
packet = Util.insertByteArray(packet, dstMac, AddressPositions.ARP_MAC_DST);
packet = Util.insertByteArray(packet, dstIP, AddressPositions.ARP_IP_DST);
packet = Util.insertByteArray(packet, opCode, AddressPositions.ARP_OPCODE);
return packet;
}
/**
* Check if OFPacketOut is LLDP Packet
*
* @param po incoming OFPacketOut
* @return true --> packet is LLDP
*/
private boolean isLLDP(OFPacketOut po) {
short ofp_none = OFPort.OFPP_NONE.getValue();
logger.trace("[Switch#{}]: Checking for LLDP! " + ofp_none + " " + po.getInPort(), this.dpidString);
// PO.InPort Check
if (po.getInPort() != ofp_none) {
return false;
}
// Check if OFPhysicalPort is local
OFPhysicalPort localPort = getLocalPort(po);
if (localPort == null) {
return false;
}
// PO.Actions check
List<OFAction> lldp_actions = new ArrayList<>();
lldp_actions.add(new OFActionOutput(localPort.getPortNumber(), (short) 0));
if (!po.getActions().equals(lldp_actions)) {
// wrong actionSet or port not from this switch
return false;
}
// PacketData check
byte[] data = po.getPacketData();
byte[] srcMac = Util.getBytes(data, AddressPositions.ETHER_MAC_SRC, 6);
String srcMacString = HexString.toHexString(srcMac);
byte[] dstMac = Util.getBytes(data, AddressPositions.ETHER_MAC_DST, 6);
String dstMacString = HexString.toHexString(dstMac);
byte[] portMac = localPort.getHardwareAddress();
String portMacString = HexString.toHexString(portMac);
// DST MAC reserved for LLDP
String LLDP_STANDARD_DST_MAC_STRING = "01:80:c2:00:00:0e";
// DST MAC has to be LLDP_MAC
if (!dstMacString.equals(LLDP_STANDARD_DST_MAC_STRING)) {
// Wrong destination mac (!= lldp mac)
return false;
}
// SRC MAC has to be MAC of localPort
if (!srcMacString.equals(portMacString)) {
// Wrong src mac (!= lldp_port mac)
return false;
}
// Ethertype has to be set to "0x88cc"
String lldpTypeString = "88cc";
byte[] etherType = Util.getBytes(data, AddressPositions.ETHER_TYPE, 2);
if (!lldpTypeString.equals(Util.asString(etherType))) {
return false;
}
logger.trace("[Switch#{}]: OFPacketOut is LLDP!", this.dpidString);
return true;
}
@Override
public byte getOFVersion() {
return OFP_VERSION;
}
@Override
public void setChannel(SocketChannel chan) throws IOException {
this.socket = chan;
this.ofStream = new OFMessageAsyncStream(chan, new BasicFactory());
}
@Override
public SocketChannel getChannel() {
return this.socket;
}
/**
* Interface for Packet In for Statistics.
*/
private void packetIn(OFMessage in) {
this.lastPacketIn = new Date();
for (IStatistics stat : this.statistics) {
stat.packetIn(in);
}
}
/**
* Interface for Packet In for Statistics.
*/
private void packetOut(OFMessage out) {
for (IStatistics stat : this.statistics) {
stat.packetOut(out);
}
}
@Override
public int getNextFreeBufferId() {
return this.buffer.getNextFreeBufferId();
}
@Override
public int packetInQueueLength() {
if (this.queueLengthMonitor != null) {
this.queueLengthMonitor.newQueueLength(this.incomingListSwitchRunner.size());
}
return this.incomingListTrafficGenerator.size();
}
@Override
public void queuePacketIn(byte[] payload, short port, boolean safeFlag) {
synchronized (incomingListTrafficGenerator) {
incomingListTrafficGenerator.add(new QueuedPacket(payload, port, safeFlag));
}
}
/**
* Here the TrafficGenerator queues new Packets.
*
* @param payloads a List of byte[]-payloads.
*/
public void queuePacketInS(Collection<byte[]> payloads) {
List<QueuedPacket> packetQueue = new ArrayList<QueuedPacket>();
Iterator<byte[]> iter = payloads.iterator();
while (iter.hasNext()) {
packetQueue.add(new QueuedPacket(iter.next(), (short) 2, false));
}
synchronized (incomingListTrafficGenerator) {
incomingListTrafficGenerator.addAll(packetQueue);
}
}
/**
* Swap Pointers of the two PacketInQueues
*/
private void swapLists() {
synchronized (incomingListTrafficGenerator) {
List<QueuedPacket> tmp = incomingListTrafficGenerator;
incomingListTrafficGenerator = incomingListSwitchRunner;
incomingListSwitchRunner = tmp;
}
}
@Override
public boolean hasPacketInQueued() {
if (this.incomingListSwitchRunner.isEmpty()) {
synchronized (incomingListTrafficGenerator) {
return !incomingListTrafficGenerator.isEmpty();
}
} else {
return true;
}
}
@Override
public void sendPacketIn() {
if (this.sessionStopped) {
return;
}
if (this.incomingListSwitchRunner.isEmpty()) {
swapLists();
}
if (this.lastXid <= 0) {
this.lastXid = 1;
}
if (!this.crashed && this.socket != null) {
// Create newPacketIn msg dummy
OFPacketIn newOFPacketIn = new OFPacketIn();
newOFPacketIn.setReason(OFPacketInReason.NO_MATCH);
if (this.batchSending) {
// Sending imminent -> change channel to Write
this.socket.keyFor(this.runner.getSelector()).interestOps(SelectionKey.OP_WRITE);
for (QueuedPacket packet : incomingListSwitchRunner) {
if (packet != null) {
// Fill newPacketIn msg dummy with bufferId and payload
newOFPacketIn.setInPort(packet.getPort());
int buffid = getNextFreeBufferId();
newOFPacketIn.setBufferId(buffid);
int xid = this.lastXid++;
newOFPacketIn.setXid(xid);
newOFPacketIn.setPacketData(packet.getPayload());
newOFPacketIn.setLengthU(OFPacketIn.MINIMUM_LENGTH + packet.getPayload().length);
newOFPacketIn.setTotalLength((short) packet.getPayload().length);
if (packet.getSafeFlag()) {
BufferID buff = new BufferID(buffid);
this.savedBuffIds.add(buff);
this.savedPayloads.put(buff, packet.getPayload());
logger.trace("[Switch#{}]: Packet saved! Buffid: {} - Port: {}", this.dpidString, buffid, packet.getPort());
// System.out.println(this.toString() + ": saved ARP DST-IP: " + Util.fromIPvAddressBytes(Util.getBytes(packet.getPayload(), AddressPositions.ARP_IP_DST, 4)));
}
// Send Packet via ofSwitch
send(newOFPacketIn);
}
}
// Sending done -> change channel back to read
this.socket.keyFor(this.runner.getSelector()).interestOps(SelectionKey.OP_READ);
incomingListSwitchRunner.clear();
} else {
if (!incomingListSwitchRunner.isEmpty()) {
QueuedPacket packet = incomingListSwitchRunner.remove(0);
if (packet != null) {
// Fill newPacketIn msg dummy with bufferId and payload
newOFPacketIn.setInPort(packet.getPort());
int buffid = getNextFreeBufferId();
newOFPacketIn.setBufferId(buffid);
newOFPacketIn.setXid(this.lastXid++);
newOFPacketIn.setPacketData(packet.getPayload());
newOFPacketIn.setLengthU(OFPacketIn.MINIMUM_LENGTH + packet.getPayload().length);
if (packet.getSafeFlag()) {
this.savedBuffIds.add(new BufferID(buffid));
this.savedPayloads.put(new BufferID(buffid), packet.getPayload());
logger.trace("[Switch#{}]: Packet saved! Buffid: {} - Port: {}", this.dpidString, buffid, packet.getPort());
}
// Sending imminent -> change channel to Write
this.socket.keyFor(this.runner.getSelector()).interestOps(SelectionKey.OP_WRITE);
// Send Packet via ofSwitch
send(newOFPacketIn);
// Sending done -> change channel back to read
this.socket.keyFor(this.runner.getSelector()).interestOps(SelectionKey.OP_READ);
}
}
}
if (this.incomingListSwitchRunner.isEmpty()) {
swapLists();
}
}
}
@Override
public long lastPacketInTime() {
return this.lastPacketIn.getTime();
}
@Override
public void startSession() {
for (IStatistics stat : this.statistics) {
stat.start();
}
}
@Override
public void evaluate() {
if (this.queueLengthMonitor != null) {
this.queueLengthMonitor.evaluate();
}
for (IStatistics stat : this.statistics) {
stat.evaluate();
}
}
@Override
public void report() {
if (this.queueLengthMonitor != null) {
this.queueLengthMonitor.report();
}
for (IStatistics stat : this.statistics) {
stat.report();
}
}
@Override
public void stopSession() {
this.sessionStopped = true;
for (IStatistics stat : this.statistics) {
stat.stop();
}
this.lastPacketIn = new Date();
}
@Override
public OFSwitchRunner getRunner() {
return this.runner;
}
/**
* toStringStuff
*
* @return Stuff which describes this ofSwitch
*/
@Override
public String toString() {
NumberFormat formatter = new DecimalFormat("#000");
String output = new String();
output += "DPID:" + formatter.format(this.dpid) + ";Ports:" + this.ports;
return output;
}
@Override
public boolean hadOFComm() {
return this.hadOFComm;
}
@Override
public long getConDelay() {
return this.conDelay;
}
@Override
public long getStartDelay() {
return this.startDelay;
}
@Override
public long getStopDelay() {
return this.stopDelay;
}
@Override
public long getDpid() {
return this.dpid;
}
@Override
public List<OFPhysicalPort> getPorts() {
return this.feat_reply.getPorts();
}
@Override
public int getIAT() {
return this.iat;
}
@Override
public int getFillThreshold() {
return this.fillThreshold;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (batchSending ? 1231 : 1237);
result = prime * result + bufferSize;
result = prime * result
+ ((config_reply == null) ? 0 : config_reply.hashCode());
result = prime * result + (crashed ? 1231 : 1237);
result = prime * result + (int) (dpid ^ (dpid >>> 32));
result = prime * result
+ ((dpidString == null) ? 0 : dpidString.hashCode());
result = prime * result
+ ((feat_reply == null) ? 0 : feat_reply.hashCode());
result = prime * result + fillThreshold;
result = prime * result
+ ((flowmod_handler == null) ? 0 : flowmod_handler.hashCode());
result = prime * result + (hadOFComm ? 1231 : 1237);
result = prime * result + iat;
result = prime
* result
+ ((incomingListSwitchRunner == null) ? 0
: incomingListSwitchRunner.hashCode());
result = prime
* result
+ ((incomingListTrafficGenerator == null) ? 0
: incomingListTrafficGenerator.hashCode());
result = prime * result
+ ((lastPacketIn == null) ? 0 : lastPacketIn.hashCode());
result = prime * result + lastXid;
result = prime * result
+ ((pcapFileName == null) ? 0 : pcapFileName.hashCode());
result = prime * result + ports;
result = prime * result + ((runner == null) ? 0 : runner.hashCode());
result = prime * result
+ ((savedBuffIds == null) ? 0 : savedBuffIds.hashCode());
result = prime * result
+ ((savedPayloads == null) ? 0 : savedPayloads.hashCode());
result = prime * result + (sendFlag ? 1231 : 1237);
result = prime * result + session;
result = prime * result + (sessionStopped ? 1231 : 1237);
result = prime * result + (int) (conDelay ^ (conDelay >>> 32));
result = prime * result
+ ((statistics == null) ? 0 : statistics.hashCode());
result = prime * result
+ ((stats_handler == null) ? 0 : stats_handler.hashCode());
result = prime * result + (int) (stopDelay ^ (stopDelay >>> 32));
result = prime * result
+ ((topology == null) ? 0 : topology.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;
}
OFConnection1_zero other = (OFConnection1_zero) obj;
if (batchSending != other.batchSending) {
return false;
}
if (bufferSize != other.bufferSize) {
return false;
}
if (config_reply == null) {
if (other.config_reply != null) {
return false;
}
} else if (!config_reply.equals(other.config_reply)) {
return false;
}
if (crashed != other.crashed) {
return false;
}
if (dpid != other.dpid) {
return false;
}
if (dpidString == null) {
if (other.dpidString != null) {
return false;
}
} else if (!dpidString.equals(other.dpidString)) {
return false;
}
if (feat_reply == null) {
if (other.feat_reply != null) {
return false;
}
} else if (!feat_reply.equals(other.feat_reply)) {
return false;
}
if (fillThreshold != other.fillThreshold) {
return false;
}
if (flowmod_handler == null) {
if (other.flowmod_handler != null) {
return false;
}
} else if (!flowmod_handler.equals(other.flowmod_handler)) {
return false;
}
if (hadOFComm != other.hadOFComm) {
return false;
}
if (iat != other.iat) {
return false;
}
if (incomingListSwitchRunner == null) {
if (other.incomingListSwitchRunner != null) {
return false;
}
} else if (!incomingListSwitchRunner
.equals(other.incomingListSwitchRunner)) {
return false;
}
if (incomingListTrafficGenerator == null) {
if (other.incomingListTrafficGenerator != null) {
return false;
}
} else if (!incomingListTrafficGenerator
.equals(other.incomingListTrafficGenerator)) {
return false;
}
if (lastPacketIn == null) {
if (other.lastPacketIn != null) {
return false;
}
} else if (!lastPacketIn.equals(other.lastPacketIn)) {
return false;
}
if (lastXid != other.lastXid) {
return false;
}
if (pcapFileName == null) {
if (other.pcapFileName != null) {
return false;
}
} else if (!pcapFileName.equals(other.pcapFileName)) {
return false;
}
if (ports != other.ports) {
return false;
}
if (runner == null) {
if (other.runner != null) {
return false;
}
} else if (!runner.equals(other.runner)) {
return false;
}
if (savedBuffIds == null) {
if (other.savedBuffIds != null) {
return false;
}
} else if (!savedBuffIds.equals(other.savedBuffIds)) {
return false;
}
if (savedPayloads == null) {
if (other.savedPayloads != null) {
return false;
}
} else if (!savedPayloads.equals(other.savedPayloads)) {
return false;
}
if (sendFlag != other.sendFlag) {
return false;
}
if (session != other.session) {
return false;
}
if (sessionStopped != other.sessionStopped) {
return false;
}
if (conDelay != other.conDelay) {
return false;
}
if (statistics == null) {
if (other.statistics != null) {
return false;
}
} else if (!statistics.equals(other.statistics)) {
return false;
}
if (stats_handler == null) {
if (other.stats_handler != null) {
return false;
}
} else if (!stats_handler.equals(other.stats_handler)) {
return false;
}
if (stopDelay != other.stopDelay) {
return false;
}
if (topology == null) {
if (other.topology != null) {
return false;
}
} else if (!topology.equals(other.topology)) {
return false;
}
return true;
}
@Override
public String getPcapFileName() {
return this.pcapFileName;
}
@Override
public String getDistribution() {
return this.iatDistri;
}
@Override
public double getDistributionPara1() {
return this.iatDistriPara1;
}
@Override
public double getDistributionPara2() {
return this.iatDistriPara2;
}
}