/*******************************************************************************
* 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_2", pluginName = "DLPA Heuristic II")
public class DLPAHeuristic2 extends OutputStrategyImpl implements Identifiable {
// Requirement
@IntSimulationProperty( name = "Maximum request delay (ms)",
key = "MAX_DLPAII_REQUEST_DELAY",
min = 0)
private int maxRequestDelay;
@IntSimulationProperty( name = "Maximum reply delay (ms)",
key = "MAX_DLPAII_REPLY_DELAY",
min = 0)
private int maxReplyDelay;
@DoubleSimulationProperty( name = "Request utility threshold (requests)",
key = "REQUEST_UTILITY_THRESHOLD_II",
min = 0)
double requestUtilityThreshold;
@DoubleSimulationProperty( name = "Reply utility threshold (requests)",
key = "REPLY_UTILITY_THRESHOLD_II",
min = 0)
double replyUtilityThreshold;
private Statistics statistics;
private int numericIdentifier;
private DLPAHeuristicSimplex requestHandler;
private DLPAHeuristicSimplex replyHandler;
public DLPAHeuristic2(Mix mix, Simulator simulator) {
super(mix, simulator);
this.statistics = new Statistics(this);
this.numericIdentifier = IdGenerator.getId();
maxRequestDelay = Simulator.settings.getPropertyAsInt("MAX_DLPAII_REQUEST_DELAY");
maxReplyDelay = Simulator.settings.getPropertyAsInt("MAX_DLPAII_REPLY_DELAY");
requestUtilityThreshold = Simulator.settings.getPropertyAsDouble("REQUEST_UTILITY_THRESHOLD_II");
replyUtilityThreshold = Simulator.settings.getPropertyAsDouble("REPLY_UTILITY_THRESHOLD_II");
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;
Event outputEvent = null;
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>());
}
public void addMessage(MixMessage mixMessage) {
mixMessage.timeOfArrival = Simulator.getNow();
// "01: Put new packet Pij into a FIFO queue for the flow fi":
messageQueues.get(mixMessage.getOwner().getClientId()).add(mixMessage);
if (outputEvent == null) { // set timeout
long timeOfOutput = mixMessage.timeOfArrival + MAX_DELAY;
outputEvent = new Event(this, timeOfOutput, OutputStrategyEvent.DLPA_TIMEOUT);
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) {
// 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."
for (int i=0; i<messageQueues.size(); i++) { // drop all packets that are outdated (only check longest waiting packet per user)
if (messageQueues.get(i).size() != 0) {
if ((Simulator.getNow() - messageQueues.get(i).get(0).timeOfArrival) >= MAX_DELAY) { // message is outdated
messageQueues.get(i).remove(0);
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
MixMessage longestWaitingMessage = null; // determine the longest waiting message
for (int i=0; i<messageQueues.size(); i++) {
if (messageQueues.get(i).size() != 0) {
if (longestWaitingMessage == null)
longestWaitingMessage = messageQueues.get(i).get(0);
else if (longestWaitingMessage.timeOfArrival > messageQueues.get(i).get(0).timeOfArrival)
longestWaitingMessage = messageQueues.get(i).get(0);
}
}
if (longestWaitingMessage == null) { // no messages available
outputEvent = null; // schedule will happen with the next arriving message (see above)
} else {
//int timeOfOutput = longestWaitingMessage.timeOfArrival + MAX_DELAY;
long timeOfOutput = Simulator.getNow() + MAX_DELAY;
outputEvent = new Event(this, timeOfOutput, OutputStrategyEvent.DLPA_TIMEOUT);
simulator.scheduleEvent(outputEvent, this);
}
}
}
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);
}
}