/*
* 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.nio.ByteBuffer;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import de.uniwuerzburg.info3.ofcprobe.vswitch.connection.flowtable.OFFlowTable;
import de.uniwuerzburg.info3.ofcprobe.vswitch.connection.flowtable.OFFlowTableEntry;
import de.uniwuerzburg.info3.ofcprobe.vswitch.main.config.Config;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPort;
import org.openflow.protocol.OFStatisticsReply;
import org.openflow.protocol.OFStatisticsRequest;
import org.openflow.protocol.statistics.OFAggregateStatisticsReply;
import org.openflow.protocol.statistics.OFAggregateStatisticsRequest;
import org.openflow.protocol.statistics.OFDescriptionStatistics;
import org.openflow.protocol.statistics.OFFlowStatisticsReply;
import org.openflow.protocol.statistics.OFFlowStatisticsRequest;
import org.openflow.protocol.statistics.OFPortStatisticsReply;
import org.openflow.protocol.statistics.OFPortStatisticsRequest;
import org.openflow.protocol.statistics.OFStatistics;
import org.openflow.protocol.statistics.OFStatisticsType;
import org.openflow.protocol.statistics.OFTableStatistics;
import org.openflow.protocol.statistics.OFVendorStatistics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Christopher Metter(christopher.metter@informatik.uni-wuerzburg.de)
*
*/
public class OFStatsHandler {
/**
* logger
*/
private static final Logger logger = LoggerFactory.getLogger(OFStatsHandler.class);
/**
* The DPID as String
*/
private String dpid;
/**
* The OFFlowTable
*/
private OFFlowTable flow_table;
/**
* Randomize Statistics Fields
*/
private boolean randomizeFlag;
/**
* The Config
*/
private Config config;
/**
* Constructor
*
* @param config the config
* @param flow_table the flow_table
*/
public OFStatsHandler(Config config, OFFlowTable flow_table) {
this.config = config;
NumberFormat dpidFormatter = new DecimalFormat("#000");
this.dpid = dpidFormatter.format(config.getSwitchConfig().getDpid());
this.flow_table = flow_table;
this.randomizeFlag = config.getSwitchConfig().getRandomizeStats();
}
/**
* Build Stats Reply for incoming StatRequest
*
* @param incomingStat the incoing StatRequest
* @return Stats Reply
*/
public OFMessage buildStatsReply(OFStatisticsRequest incomingStat) {
OFStatisticsType incomingStatType = incomingStat.getStatisticType();
OFStatisticsReply stat_reply = new OFStatisticsReply();
List<OFStatistics> statistics = new ArrayList<>();
// Important to set StatisticType!!!!-> else Crash!
switch (incomingStatType) {
case AGGREGATE:
logger.trace("[Switch#{}]: NEW IMPLEMENTED STAT_REQUEST: {}", this.dpid, incomingStatType.toString());
stat_reply.setStatisticType(OFStatisticsType.AGGREGATE);
if (!incomingStat.getStatistics().isEmpty()) {
OFAggregateStatisticsRequest aggReq = (OFAggregateStatisticsRequest) incomingStat.getStatistics().get(0);
OFAggregateStatisticsReply aggReply = aggregateReplyBuilder(aggReq);
statistics.add(aggReply);
}
break;
case DESC:
stat_reply.setStatisticType(OFStatisticsType.DESC);
OFDescriptionStatistics descStat = descStatBuilder(incomingStat);
statistics.add(descStat);
break;
case FLOW:
logger.trace("[Switch#{}]: NEW IMPLEMENTED STAT_REQUEST: {}", this.dpid, incomingStatType.toString());
stat_reply.setStatisticType(OFStatisticsType.FLOW);
if (!incomingStat.getStatistics().isEmpty()) {
OFFlowStatisticsRequest flowReq = (OFFlowStatisticsRequest) incomingStat.getStatistics().get(0);
List<OFFlowStatisticsReply> flowReplyList = flowReplyBuilder(flowReq);
statistics.addAll(flowReplyList);
}
break;
case PORT:
logger.trace("[Switch#{}]: STAT_REQUEST: {}", this.dpid, incomingStatType.toString());
stat_reply.setStatisticType(OFStatisticsType.PORT);
if (!incomingStat.getStatistics().isEmpty()) {
OFPortStatisticsRequest stati = (OFPortStatisticsRequest) incomingStat.getStatistics().get(0);
List<OFPortStatisticsReply> portReply = portReplyBuilder(stati);
statistics.addAll(portReply);
}
break;
case QUEUE:
logger.trace("[Switch#{}]: NOT IMPLEMENTED STAT_REQUEST: {}", this.dpid, incomingStatType.toString());
stat_reply.setStatisticType(OFStatisticsType.QUEUE);
break;
case TABLE:
logger.trace("[Switch#{}]: NEW IMPLEMENTED STAT_REQUEST: {}", this.dpid, incomingStatType.toString());
stat_reply.setStatisticType(OFStatisticsType.TABLE);
List<OFTableStatistics> tableStats = tableStatBuilder();
statistics.addAll(tableStats);
break;
case VENDOR:
logger.trace("[Switch#{}]: NOT FULLY IMPLEMENTED STAT_REQUEST: {}", this.dpid, incomingStatType.toString());
stat_reply.setStatisticType(OFStatisticsType.VENDOR);
OFVendorStatistics vendorStat = vendorReplyBuilder();
statistics.add(vendorStat);
break;
default:
logger.error("[Switch#{}]: NON-DEFINED STAT_REQUEST: {}", this.dpid, incomingStatType.toString());
break;
}
stat_reply.setXid(incomingStat.getXid());
stat_reply.setStatistics(statistics);
return stat_reply;
}
/**
* FlowStatisticsReply Builder for incoming OFFlowStatisticsRequest
*
* @param flowReq incoming OFFlowStatisticsRequest
* @return List of OFFlowStatisticsReplies
*/
private List<OFFlowStatisticsReply> flowReplyBuilder(OFFlowStatisticsRequest flowReq) {
List<OFFlowStatisticsReply> flowReplyList = new ArrayList<OFFlowStatisticsReply>();
short port = flowReq.getOutPort();
Map<OFMatch, OFFlowTableEntry> matchedFlows = this.flow_table.getMatchingFlows(flowReq.getMatch(), false);
if (!matchedFlows.isEmpty()) {
Iterator<Map.Entry<OFMatch, OFFlowTableEntry>> matchIter = matchedFlows.entrySet().iterator();
while (matchIter.hasNext()) {
Map.Entry<OFMatch, OFFlowTableEntry> entry = matchIter.next();
OFFlowTableEntry tableEntry = entry.getValue();
OFFlowStatisticsReply flowReply = new OFFlowStatisticsReply();
flowReply.setActions(tableEntry.getActions());
flowReply.setCookie(tableEntry.getCookie());
flowReply.setDurationNanoseconds(tableEntry.getNanoDuration());
flowReply.setDurationSeconds(tableEntry.getSecondDuration());
flowReply.setHardTimeout(tableEntry.getHardTimeOut());
flowReply.setIdleTimeout(tableEntry.getIdleTimeOut());
flowReply.setMatch(entry.getKey());
flowReply.setPriority(tableEntry.getPriority());
if (this.randomizeFlag) {
long newByteCount = randomLong();
flowReply.setByteCount(newByteCount);
flowReply.setPacketCount(newByteCount / 1024);
} else {
flowReply.setByteCount(tableEntry.getByteCounter());
flowReply.setPacketCount(tableEntry.getPacketCounter());
}
flowReply.setTableId((byte) 42);
if (port != OFPort.OFPP_NONE.getValue()) {
if (entry.getValue().actionsContainOutport(port)) {
flowReplyList.add(flowReply);
}
} else {
flowReplyList.add(flowReply);
}
}
}
return flowReplyList;
}
/**
* OFTableStatistics builder
*
* @return the list
*/
private List<OFTableStatistics> tableStatBuilder() {
List<OFTableStatistics> tableStats = new ArrayList<>();
for (int i = 0; i < this.flow_table.size(); i++) {
OFTableStatistics tableStat = new OFTableStatistics();
tableStat.setActiveCount(42);
tableStat.setLookupCount(42);
tableStat.setMatchedCount(42);
tableStat.setMaximumEntries(this.flow_table.capacity());
tableStat.setName("toblerOne");
tableStat.setTableId((byte) 42);
tableStats.add(tableStat);
}
return tableStats;
}
/**
* VendorReplyBuilder
*
* @return the OFVendor MSG
*/
private OFVendorStatistics vendorReplyBuilder() {
OFVendorStatistics vendorStat = new OFVendorStatistics();
vendorStat.setLength(4);
ByteBuffer buf = ByteBuffer.allocate(4);
buf.putInt(42);
buf.flip();
vendorStat.readFrom(buf);
return vendorStat;
}
/**
* PortReplyBuilder for the OF_STATISTICS_PORT_REQUEST
*
* @param portRequest the incoming portRequest
* @return the List of OfPortStatsReplies
*/
private List<OFPortStatisticsReply> portReplyBuilder(OFPortStatisticsRequest portRequest) {
List<OFPortStatisticsReply> replyList = new ArrayList<>();
if (portRequest.getPortNumber() != OFPort.OFPP_NONE.getValue()) {
OFPortStatisticsReply portReply = new OFPortStatisticsReply();
portReply.setPortNumber(portRequest.getPortNumber());
if (this.randomizeFlag) {
portReply.setCollisions(randomInt());
portReply.setReceiveBytes(randomInt());
portReply.setReceiveCRCErrors(randomInt());
portReply.setReceiveDropped(randomInt());
portReply.setreceiveErrors(randomInt());
portReply.setReceiveFrameErrors(randomInt());
portReply.setReceiveOverrunErrors(randomInt());
portReply.setreceivePackets(randomInt());
portReply.setTransmitBytes(randomInt());
portReply.setTransmitDropped(randomInt());
portReply.setTransmitErrors(randomInt());
portReply.setTransmitPackets(randomInt());
} else {
portReply.setCollisions(42);
portReply.setReceiveBytes(42);
portReply.setReceiveCRCErrors(42);
portReply.setReceiveDropped(42);
portReply.setreceiveErrors(42);
portReply.setReceiveFrameErrors(42);
portReply.setReceiveOverrunErrors(42);
portReply.setreceivePackets(42);
portReply.setTransmitBytes(42);
portReply.setTransmitDropped(42);
portReply.setTransmitErrors(42);
portReply.setTransmitPackets(42);
}
replyList.add(portReply);
} else {
for (int i = 1; i <= this.config.getSwitchConfig().getPortCountperSwitch(); i++) {
replyList.addAll(
portReplyBuilder(
(new OFPortStatisticsRequest()).setPortNumber((short) i)
)
);
}
}
return replyList;
}
/**
* Description Stats Reply Builder
*
* @param incomingStat incoming Stats Request
* @return the Stats Reply
*/
private OFDescriptionStatistics descStatBuilder(OFStatisticsRequest incomingStat) {
OFDescriptionStatistics descStat = new OFDescriptionStatistics();
descStat.setDatapathDescription("None");
descStat.setHardwareDescription("OFCProbe Vswitch");
descStat.setManufacturerDescription("Lehrstuhl fuer Informatik III, University of Wuerzburg");
descStat.setSerialNumber(String.valueOf(this.dpid));
descStat.setSoftwareDescription("1.0.3");
return descStat;
}
/**
* Aggregate Stats Reply Builder
*
* @param aggReq the incoming AggregateStatsRequest
* @return the Stats Reply
*/
private OFAggregateStatisticsReply aggregateReplyBuilder(OFAggregateStatisticsRequest aggReq) {
short port = aggReq.getOutPort();
OFAggregateStatisticsReply aggReply = new OFAggregateStatisticsReply();
Map<OFMatch, OFFlowTableEntry> matches = this.flow_table.getMatchingFlows(aggReq.getMatch(), false);
long byteCount = 42;
long packetCount = 42;
int flowCount = 42;
if (!matches.isEmpty()) {
Iterator<Entry<OFMatch, OFFlowTableEntry>> iter = matches.entrySet().iterator();
while (iter.hasNext()) {
Entry<OFMatch, OFFlowTableEntry> entry = iter.next();
if (port != OFPort.OFPP_NONE.getValue()) {
if (entry.getValue().actionsContainOutport(port)) {
if (!this.randomizeFlag) {
byteCount = entry.getValue().getByteCounter();
packetCount = entry.getValue().getPacketCounter();
flowCount = entry.getValue().getFlowCount();
} else {
long newByteCount = randomLong();
byteCount += newByteCount;
packetCount += newByteCount / 1024;
flowCount = entry.getValue().getFlowCount();
}
}
} else {
if (!this.randomizeFlag) {
byteCount = entry.getValue().getByteCounter();
packetCount = entry.getValue().getPacketCounter();
flowCount = entry.getValue().getFlowCount();
} else {
long newByteCount = randomLong();
byteCount += newByteCount;
packetCount += newByteCount / 1024;
flowCount += newByteCount / 1024 / 1024;
}
}
}
}
aggReply.setByteCount(byteCount);
aggReply.setPacketCount(packetCount);
aggReply.setFlowCount(flowCount);
return aggReply;
}
/**
* Get Random Long - Used for random Stats
*
* @return a random Long
*/
private long randomLong() {
long x = 0L;
long y = 12345678L;
Random r = new Random();
return x + (long) (r.nextDouble() * (y - x));
}
/**
* A Random Int - Used for random Stats
*
* @return a random Int
*/
private int randomInt() {
int x = 0;
int y = 12345678;
Random r = new Random();
return x + (int) (r.nextDouble() * (y - x));
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dpid == null) ? 0 : dpid.hashCode());
result = prime * result
+ ((flow_table == null) ? 0 : flow_table.hashCode());
result = prime * result + (randomizeFlag ? 1231 : 1237);
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;
}
OFStatsHandler other = (OFStatsHandler) obj;
if (dpid == null) {
if (other.dpid != null) {
return false;
}
} else if (!dpid.equals(other.dpid)) {
return false;
}
if (flow_table == null) {
if (other.flow_table != null) {
return false;
}
} else if (!flow_table.equals(other.flow_table)) {
return false;
}
if (randomizeFlag != other.randomizeFlag) {
return false;
}
return true;
}
}