package net.floodlightcontroller.hedera; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.annotations.LogMessageDoc; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.IFloodlightModule; import net.floodlightcontroller.forwarding.Forwarding; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.routing.IRoutingDecision; import org.openflow.protocol.OFPacketIn; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class Hedera extends Forwarding implements IFloodlightModule { class matrixElement { HashMap<byte[], Integer> flowamount = new HashMap<byte[], Integer>(); HashMap<byte[], Double> flowdemands = new HashMap<byte[], Double>(); HashMap<byte[], Boolean> convergeFlag = new HashMap<byte[], Boolean>(); } class TrafficEstimation implements Runnable { private boolean allflowconverged = false; private void estimateSource(Map.Entry<byte[], matrixElement> sourceRecord) { double convergedDemand = 0.0; int unconvergedFlowNumber = 0; for (Map.Entry<byte[], Boolean> destination : sourceRecord.getValue().convergeFlag.entrySet()) { if (destination.getValue()) { //it is converged convergedDemand += sourceRecord.getValue().flowdemands.get(destination.getKey()); } else { unconvergedFlowNumber += 1; } } double estFlow = (1 - convergedDemand) / unconvergedFlowNumber; for (Map.Entry<byte[], Boolean> destination : sourceRecord.getValue().convergeFlag.entrySet()) { if (!destination.getValue()) sourceRecord.getValue().flowdemands.put(destination.getKey(), estFlow); } } private void estimateDestination(Map.Entry<byte[], matrixElement> destinationRecord) { double totaldemand = 0.0; double senderlimitdemand = 0.0; int receiverlimitflownum = 0; HashMap<byte[], Boolean> flowreceiverLimited = new HashMap<byte[], Boolean>(); for (Map.Entry<byte[], Double> source : destinationRecord.getValue().flowdemands.entrySet()) { flowreceiverLimited.put(source.getKey(), true); totaldemand += source.getValue(); receiverlimitflownum += 1; } if (totaldemand < 1) return; double estimatedDemand = totaldemand / receiverlimitflownum; boolean findreceiverlimitedflow = false; do { receiverlimitflownum = 0; for (Map.Entry<byte[], Double> source : destinationRecord.getValue().flowdemands.entrySet()) { if (!flowreceiverLimited.containsKey(source.getKey())) continue; if (source.getValue() < estimatedDemand) { senderlimitdemand += source.getValue(); flowreceiverLimited.remove(source.getKey()); findreceiverlimitedflow = true; } else { receiverlimitflownum += 1; } } estimatedDemand = (totaldemand - senderlimitdemand) / receiverlimitflownum; } while (findreceiverlimitedflow); for (Map.Entry<byte[], Double> source : destinationRecord.getValue().flowdemands.entrySet()) { sourceFlowMap.get(source.getKey()).convergeFlag.put( destinationRecord.getKey(), true); if (estimatedDemand != sourceFlowMap.get(source.getKey()).flowdemands.get(destinationRecord.getKey())) { allflowconverged = false; } sourceFlowMap.get(source.getKey()).flowdemands.put( destinationRecord.getKey(), estimatedDemand); } } @Override public void run() { while (!allflowconverged) { try { Thread.sleep(1000 * 10); } catch (InterruptedException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } allflowconverged = true; for (Map.Entry<byte[], matrixElement> sourceRecord : sourceFlowMap.entrySet()) { estimateSource(sourceRecord); //foreach source } for (Map.Entry<byte[], matrixElement> destinationRecord : destinationFlowMap.entrySet()) { estimateDestination(destinationRecord); //foreach source } } } } private ConcurrentHashMap<byte[], matrixElement> sourceFlowMap = new ConcurrentHashMap<byte[], matrixElement>(); private ConcurrentHashMap<byte[], matrixElement> destinationFlowMap = new ConcurrentHashMap<byte[], matrixElement>(); private void fillTrafficMatrix(Ethernet eth) { //1. check if sourceFlowMap exists if (!sourceFlowMap.contains(eth)) { sourceFlowMap.put(eth.getSourceMACAddress(), new matrixElement()); } //2. check if destinationFlowMap exists if (!destinationFlowMap.contains(eth)) { destinationFlowMap.put(eth.getSourceMACAddress(), new matrixElement()); } } @Override @LogMessageDoc(level="ERROR", message="Unexpected decision made for this packet-in={}", explanation="An unsupported PacketIn decision has been " + "passed to the flow programming component", recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) { Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); // If a decision has been made we obey it // otherwise we just forward if (decision != null) { if (log.isTraceEnabled()) { log.trace("Forwaring decision={} was made for PacketIn={}", decision.getRoutingAction().toString(), pi); } log.debug("routing action has been made"); switch(decision.getRoutingAction()) { case NONE: // don't do anything return Command.CONTINUE; case FORWARD_OR_FLOOD: case FORWARD: fillTrafficMatrix(eth); doForwardFlow(sw, pi, cntx, false); return Command.CONTINUE; case MULTICAST: // treat as broadcast fillTrafficMatrix(eth); doFlood(sw, pi, cntx); return Command.CONTINUE; case DROP: doDropFlow(sw, pi, decision, cntx); return Command.CONTINUE; default: log.error("Unexpected decision made for this packet-in={}", pi, decision.getRoutingAction()); return Command.CONTINUE; } } else { fillTrafficMatrix(eth); if (log.isTraceEnabled()) { log.trace("No decision was made for PacketIn={}, forwarding", pi); } log.debug("receive a PACKET_IN message, pi = " + pi); if (eth.isBroadcast() || eth.isMulticast()) { // For now we treat multicast as broadcast log.debug("no target host information, then flood"); doFlood(sw, pi, cntx); } else { doForwardFlow(sw, pi, cntx, false); } } return Command.CONTINUE; } @Override public void startUp(FloodlightModuleContext context) { super.startUp(); //start estimation thread Thread t = new Thread(new TrafficEstimation()); t.start(); } }