/*******************************************************************************
* gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/
* Copyright (C) 2014 SVS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package userGeneratedContent.simulatorPlugIns.plugins.outputStrategy;
import java.util.Vector;
import staticContent.evaluation.simulator.Simulator;
import staticContent.evaluation.simulator.annotations.plugin.Plugin;
import staticContent.evaluation.simulator.annotations.property.DoubleSimulationProperty;
import staticContent.evaluation.simulator.annotations.property.IntSimulationProperty;
import staticContent.evaluation.simulator.core.event.Event;
import staticContent.evaluation.simulator.core.event.EventExecutor;
import staticContent.evaluation.simulator.core.message.MixMessage;
import staticContent.evaluation.simulator.core.networkComponent.AbstractClient;
import staticContent.evaluation.simulator.core.networkComponent.IdGenerator;
import staticContent.evaluation.simulator.core.networkComponent.Identifiable;
import staticContent.evaluation.simulator.core.networkComponent.Mix;
import staticContent.evaluation.simulator.core.networkComponent.NetworkNode;
import staticContent.evaluation.simulator.core.statistics.Statistics;
import userGeneratedContent.simulatorPlugIns.pluginRegistry.ClientSendStyle;
import userGeneratedContent.simulatorPlugIns.pluginRegistry.MixSendStyle;
import userGeneratedContent.simulatorPlugIns.pluginRegistry.StatisticsType;
import userGeneratedContent.simulatorPlugIns.plugins.clientSendStyle.ClientSendStyleImpl;
import userGeneratedContent.simulatorPlugIns.plugins.mixSendStyle.MixSendStyleImpl;
/**
* DLP Heuristic Algorithm (2008: Wei Wang, Mehul Motani, Vikram Srinivasan:
* Dependent Link Padding Algorithms for Low Latency Anonymity Systems) CCS'08
*
* "DLP Heuristic Algorithm
* Parameters: Packet arrival time tij for all flows fi element F
* Utility threshold U.
* Output: A sending schedule with utility of at least U
* 01: Put new packet Pij into a FIFO queue for the flow fi
* 02: Repeat step 01 until there is a packet P has been in the queue for Delta time units:
* 03: if more than U*|F| queues are non-empty
* 04: Add a new token and send one packet for each flow immediately
* 05: else
* 06: Drop the packet P.
* 07: endif
* 08: Go to step 01 until no more packet arrives."
*/
@Plugin(pluginKey = "DLPA_HEURISTIC", pluginName = "DLPA Heuristic I")
public class DLPAHeuristic extends OutputStrategyImpl implements Identifiable {
// Requirement
@IntSimulationProperty( name = "Maximum request delay (ms)",
key = "MAX_DLPAI_REQUEST_DELAY",
min = 0)
private int maxRequestDelay;
@IntSimulationProperty( name = "Maximum reply delay (ms)",
key = "MAX_DLPAI_REPLY_DELAY",
min = 0)
private int maxReplyDelay;
@DoubleSimulationProperty( name = "Request utility threshold (requests)",
key = "REQUEST_UTILITY_THRESHOLD_I",
min = 0)
double requestUtilityThreshold;
@DoubleSimulationProperty( name = "Reply utility threshold (requests)",
key = "REPLY_UTILITY_THRESHOLD_I",
min = 0)
double replyUtilityThreshold;
private Statistics statistics;
private int numericIdentifier;
private DLPAHeuristicSimplex requestHandler;
private DLPAHeuristicSimplex replyHandler;
private final static Event emptyEvent = new Event(null, 0, null);
public DLPAHeuristic(Mix mix, Simulator simulator) {
super(mix, simulator);
this.statistics = new Statistics(this);
this.numericIdentifier = IdGenerator.getId();
maxRequestDelay = Simulator.settings.getPropertyAsInt("MAX_DLPAI_REQUEST_DELAY");
maxReplyDelay = Simulator.settings.getPropertyAsInt("MAX_DLPAI_REPLY_DELAY");
requestUtilityThreshold = Simulator.settings.getPropertyAsDouble("REQUEST_UTILITY_THRESHOLD_I");
replyUtilityThreshold = Simulator.settings.getPropertyAsDouble("REPLY_UTILITY_THRESHOLD_I");
this.requestHandler = new DLPAHeuristicSimplex(true, maxRequestDelay, requestUtilityThreshold);
this.replyHandler = new DLPAHeuristicSimplex(false, maxReplyDelay, replyUtilityThreshold);
}
@Override
public void incomingRequest(MixMessage mixMessage) {
this.requestHandler.addMessage(mixMessage);
}
@Override
public void incomingReply(MixMessage mixMessage) {
this.replyHandler.addMessage(mixMessage);
}
private class DLPAHeuristicSimplex implements EventExecutor {
boolean isRequestHandler;
int MAX_DELAY;
double UTILITY_THRESHOLD;
Vector<Vector<MixMessage>> messageQueues;
Vector<Event> outputEvents;
public DLPAHeuristicSimplex(boolean isRequestHandler, int maxDelay, double utilityThreshold) {
this.isRequestHandler = isRequestHandler;
this.MAX_DELAY = maxDelay;
this.UTILITY_THRESHOLD = utilityThreshold;
int numberOfClients = Simulator.getSimulator().getNumberOfClients();
messageQueues = new Vector<Vector<MixMessage>>();
for (int i=0; i<numberOfClients; i++)
messageQueues.add(new Vector<MixMessage>());
outputEvents = new Vector<Event>();
for (int i=0; i<numberOfClients; i++)
outputEvents.add(emptyEvent);
}
public void addMessage(MixMessage mixMessage) {
int clientId = mixMessage.getOwner().getClientId();
mixMessage.timeOfArrival = Simulator.getNow();
// "01: Put new packet Pij into a FIFO queue for the flow fi":
messageQueues.get(clientId).add(mixMessage);
Event outputEvent = outputEvents.get(clientId);
if (outputEvent == emptyEvent) { // set timeout
long timeOfOutput = mixMessage.timeOfArrival + MAX_DELAY;
outputEvent = new Event(this, timeOfOutput, OutputStrategyEvent.DLPA_TIMEOUT);
outputEvent.setAttachment(mixMessage);
outputEvents.setElementAt(outputEvent, clientId);
simulator.scheduleEvent(outputEvent, this);
}
}
// called by scheduler on timeout ("a packet P has been in the queue for delta time units")
@Override
public void executeEvent(Event e) {
putOutSlot(e);
}
public void putOutSlot(Event e) {
MixMessage relatedMessage = (MixMessage)e.getAttachment();
int clientId = relatedMessage.getOwner().getClientId();
// 03: if more than U*|F| queues are non-empty
double notEmptyQueues = 0;
for (int i=0; i<messageQueues.size(); i++)
if (messageQueues.get(i).size() != 0)
notEmptyQueues++;
if (notEmptyQueues >= (UTILITY_THRESHOLD * (double)messageQueues.size())) { // dlpa-paper says "more than U*|F|" and that U must be <= 1. if we set u=1 and all queues are not empty, this would result in the algorithm dropping the token also all slots are in use. must be an error in the paper -> we use "at least U*|F|" instead of "more than U*|F|"
for (int i=0; i<messageQueues.size(); i++) { // "Add a new token and send one packet for each flow immediately"
MixMessage m;
if (messageQueues.get(i).size() == 0) {
m = createDummyMessage(simulator.getClientById(i), isRequestHandler);
} else {
m = messageQueues.get(i).remove(0);
if (isRequestHandler) {
statistics.addValue(true, StatisticsType.DLPA_REQUEST_MESSAGE_DROP_PERCENTAGE);
statistics.addValue(true, StatisticsType.DLPA_MESSAGE_DROP_PERCENTAGE);
} else {
statistics.addValue(true, StatisticsType.DLPA_REPLY_MESSAGE_DROP_PERCENTAGE);
statistics.addValue(true, StatisticsType.DLPA_MESSAGE_DROP_PERCENTAGE);
}
}
// TODO: sort messages
if (isRequestHandler) {
statistics.addValue(true, StatisticsType.DLPA_REQUEST_SENDING_RATE_PER_MIX);
statistics.addValue(true, StatisticsType.DLPA_REQUEST_SENDING_RATE_PER_MIX_AND_CLIENT);
mix.putOutRequest(m);
} else {
statistics.addValue(true, StatisticsType.DLPA_REPLY_SENDING_RATE_PER_MIX);
mix.putOutReply(m);
}
statistics.addValue(true, StatisticsType.DLPA_REQUEST_AND_REPLY_SENDING_RATE_PER_MIX);
}
} else { // "Drop the packet P."
messageQueues.get(clientId).remove(relatedMessage);
if (isRequestHandler) {
statistics.addValue(false, StatisticsType.DLPA_REQUEST_MESSAGE_DROP_PERCENTAGE);
statistics.addValue(false, StatisticsType.DLPA_MESSAGE_DROP_PERCENTAGE);
} else {
statistics.addValue(false, StatisticsType.DLPA_REPLY_MESSAGE_DROP_PERCENTAGE);
statistics.addValue(false, StatisticsType.DLPA_MESSAGE_DROP_PERCENTAGE);
}
}
// schedule next output if messages are still available
Vector<MixMessage> clientQueue = messageQueues.get(clientId);
if (clientQueue.size() > 0) {
System.out.println("queue empty");
long timeOfOutput = clientQueue.get(0).timeOfArrival + MAX_DELAY;
Event outputEvent = new Event(this, timeOfOutput, OutputStrategyEvent.DLPA_TIMEOUT);
outputEvent.setAttachment(clientQueue.get(0));
outputEvents.setElementAt(outputEvent, clientId);
simulator.scheduleEvent(outputEvent, this);
} else {
outputEvents.setElementAt(emptyEvent, clientId);
}
}
}
private MixMessage createDummyMessage(AbstractClient owner, boolean isRequest) {
NetworkNode source = isRequest ? owner : mix;
NetworkNode destination = isRequest ? simulator.getDistantProxy() : owner;
return MixMessage.getInstance(isRequest, source, destination, owner, Simulator.getNow(), true);
}
@Override
public int getGlobalId() {
return numericIdentifier;
}
@Override
public ClientSendStyleImpl getClientSendStyle(AbstractClient client) {
return ClientSendStyle.getInstance(client);
}
@Override
public MixSendStyleImpl getMixSendStyle() {
return MixSendStyle.getInstance(mix, mix);
}
}