diff --git a/sim/scenarios/asymmetric-p2p/asymmetric-p2p.cc b/sim/scenarios/asymmetric-p2p/asymmetric-p2p.cc index 19e9cd6d4f920560706ff4cd0e73a371b4d27d12..6ce1a6166db74c64f167132e12f58ae24938480f 100644 --- a/sim/scenarios/asymmetric-p2p/asymmetric-p2p.cc +++ b/sim/scenarios/asymmetric-p2p/asymmetric-p2p.cc @@ -8,12 +8,51 @@ #include "../helper/quic-network-simulator-helper.h" #include "../helper/quic-point-to-point-helper.h" #include "drop-rate-error-model.h" +#include "gilbert-elliott-drop-model.h" +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <sys/time.h> + using namespace ns3; using namespace std; NS_LOG_COMPONENT_DEFINE("ns3 simulator"); +static Ptr<Queue<Packet>> returnQueue; +static Ptr<Queue<Packet>> forwardQueue; +static constexpr unsigned int QUEUE_LOG_INTERVAL_USEC = 200000; + +void timer_handler(int signum){ + cout << + "# return\t" << + returnQueue->GetNPackets() << "pkgs \t" << + returnQueue->GetNBytes() << "bytes \t" << + returnQueue->GetTotalReceivedPackets() << " pkgs\t" << + returnQueue->GetTotalReceivedBytes() << " bytes\t" << + returnQueue->GetTotalDroppedPackets() << " pkgs\t" << + returnQueue->GetTotalDroppedBytes() << " bytes\t" << + returnQueue->GetTotalDroppedPacketsBeforeEnqueue() << " pkgs\t" << + returnQueue->GetTotalDroppedBytesBeforeEnqueue() << " bytes\t" << + returnQueue->GetTotalDroppedPacketsAfterDequeue() << " pkgs\t" << + returnQueue->GetTotalDroppedBytesAfterDequeue() << " bytes" << + endl; + cout << + "# forward\t" << + forwardQueue->GetNPackets() << "pkgs \t" << + forwardQueue->GetNBytes() << "bytes \t" << + forwardQueue->GetTotalReceivedPackets() << " pkgs\t" << + forwardQueue->GetTotalReceivedBytes() << " bytes\t" << + forwardQueue->GetTotalDroppedPackets() << " pkgs\t" << + forwardQueue->GetTotalDroppedBytes() << " bytes\t" << + forwardQueue->GetTotalDroppedPacketsBeforeEnqueue() << " pkgs\t" << + forwardQueue->GetTotalDroppedBytesBeforeEnqueue() << " bytes\t" << + forwardQueue->GetTotalDroppedPacketsAfterDequeue() << " pkgs\t" << + forwardQueue->GetTotalDroppedBytesAfterDequeue() << " bytes" << + endl; +} + int main(int argc, char *argv[]) { string channelDelay; @@ -21,14 +60,17 @@ int main(int argc, char *argv[]) { string forwardQueueSize; string returnDataRate; string returnQueueSize; - string drop_rate_to_client; - string drop_rate_to_server; + string goodBadProbToClient; + string badGoodProbToClient; + string goodBadProbToServer; + string badGoodProbToServer; + string dropRateToClient; + string dropRateToServer; + bool linkErrorModels = false; // for dropping packets: random_device rand_dev; mt19937 generator(rand_dev()); // Seed random number generator first - Ptr<DropRateErrorModel> drops_to_client = CreateObject<DropRateErrorModel>(); - Ptr<DropRateErrorModel> drops_to_server = CreateObject<DropRateErrorModel>(); CommandLine cmd; cmd.AddValue("delay", "delay of the channel in both directions (RTT = 2 * delay)", channelDelay); @@ -36,8 +78,13 @@ int main(int argc, char *argv[]) { cmd.AddValue("forward-queue", "queue size of the forward link (right -> left) (in packets)", forwardQueueSize); cmd.AddValue("return-data-rate", "data rate of the return link (left -> right)", returnDataRate); cmd.AddValue("return-queue", "queue size of the return link (left -> right) (in packets)", returnQueueSize); - cmd.AddValue("drop-rate-to-client", "packet drop rate (towards client)", drop_rate_to_client); - cmd.AddValue("drop-rate-to-server", "packet drop rate (towards server)", drop_rate_to_server); + cmd.AddValue("good-bad-prob-to-client", "Gilber-Elliott Probability for Transition from Good to Bad (towards client)", goodBadProbToClient); + cmd.AddValue("bad-good-prob-to-client", "Gilber-Elliott Probability for Transition from Bad to Good (towards client)", badGoodProbToClient); + cmd.AddValue("good-bad-prob-to-server", "Gilber-Elliott Probability for Transition from Good to Bad (towards server)", goodBadProbToServer); + cmd.AddValue("bad-good-prob-to-server", "Gilber-Elliott Probability for Transition from Bad to Good (towards server)", badGoodProbToServer); + cmd.AddValue("link-error-models", "Use the same error model instance in both directions (-> produce errors during the same time)", linkErrorModels); + cmd.AddValue("drop-rate-to-client", "random packet drop rate (towards client)", dropRateToClient); + cmd.AddValue("drop-rate-to-server", "random packet drop rate (towards server)", dropRateToServer); cmd.Parse(argc, argv); NS_ABORT_MSG_IF(channelDelay.length() == 0, "Missing parameter: delay"); @@ -59,27 +106,87 @@ int main(int argc, char *argv[]) { leftP2PNetDev->SetDataRate(DataRate(returnDataRate)); rightP2PNetDev->SetDataRate(DataRate(forwardDataRate)); - auto returnQueue = leftP2PNetDev->GetQueue(); - auto forwardQueue = rightP2PNetDev->GetQueue(); + returnQueue = leftP2PNetDev->GetQueue(); + forwardQueue = rightP2PNetDev->GetQueue(); returnQueue->SetMaxSize(QueueSize(returnQueueSize + "p")); forwardQueue->SetMaxSize(QueueSize(forwardQueueSize + "p")); + struct sigaction sa; + struct itimerval timer; + + /* Install timer_handler as the signal handler for SIGVTALRM. */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = &timer_handler; + sigaction(SIGVTALRM, &sa, NULL); + cout << "# queue\t" << + "size pkgs\tsize bytes\t" << + "rx pkgs\trx bytes\t" << + "dropped pkgs\tdropped bytes\t" << + "dr b enq pkgs\tdr b enq bytes\t" << + "dr a deq pkgs\tdr a deq pkgs bytes" << + endl; + + /* Configure the timer to expire after x usec... */ + timer.it_value.tv_sec = 0; + timer.it_value.tv_usec = QUEUE_LOG_INTERVAL_USEC; + /* ... and every x usec after that. */ + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = QUEUE_LOG_INTERVAL_USEC; + /* Start a virtual timer. It counts down whenever this process is executing. */ + setitimer(ITIMER_VIRTUAL, &timer, NULL); + // Set client and server drop rates. - if (drop_rate_to_client.length() == 0) { - drops_to_client->SetDropRate(0); - cout << "Using drop rate to client: 0 %. (Use --drop-rate-to-client to change)" << endl; + if (linkErrorModels && (goodBadProbToClient.length() == 0 || badGoodProbToClient.length() == 0 || dropRateToClient.length() != 0 || goodBadProbToServer.length() != 0 || badGoodProbToServer.length() != 0 || dropRateToServer.length() != 0)) { + NS_ABORT_MSG_IF(true, "--link-error-models must be used with --good-bad-prob-to-client and --bad-good-prob-to-client only."); + } else if (linkErrorModels && goodBadProbToClient.length() != 0 && badGoodProbToClient.length() != 0) { + // link error models + auto probGB = stof(goodBadProbToClient); + auto probBG = stof(badGoodProbToClient); + cout << "Using Gilbert-Elliot-Loss-Model for both directions with P(b|g) = " << probGB << " \% and P(g|b) = " << probBG << " \%" << endl; + Ptr<GilbertElliottDropModel> drops = CreateObject<GilbertElliottDropModel>(); + drops->SetProbabilities(probGB, probBG); + devices.Get(0)->SetAttribute("ReceiveErrorModel", PointerValue(drops)); + devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(drops)); } else { - drops_to_client->SetDropRate(stoi(drop_rate_to_client)); - } - if (drop_rate_to_server.length() == 0) { - drops_to_server->SetDropRate(0); - cout << "Using drop rate to server: 0 %. (Use --drop-rate-to-server to change)" << endl; - } else { - drops_to_server->SetDropRate(stoi(drop_rate_to_server)); - } - devices.Get(0)->SetAttribute("ReceiveErrorModel", PointerValue(drops_to_client)); - devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(drops_to_server)); + if (goodBadProbToClient.length() > 0 && badGoodProbToClient.length() > 0 && dropRateToClient.length() == 0) { + auto probGB = stof(goodBadProbToClient); + auto probBG = stof(badGoodProbToClient); + cout << "Using Gilbert-Elliot-Loss-Model for forward path with P(b|g) = " << probGB << " \% and P(g|b) = " << probBG << " \%" << endl; + Ptr<GilbertElliottDropModel> dropsToClient = CreateObject<GilbertElliottDropModel>(); + dropsToClient->SetProbabilities(probGB, probBG); + devices.Get(0)->SetAttribute("ReceiveErrorModel", PointerValue(dropsToClient)); + } else if (goodBadProbToClient.length() == 0 && badGoodProbToClient.length() == 0 && dropRateToClient.length() > 0) { + cout << "Using uniform random loss model in forward path with P('err') = " << dropRateToClient << " \%" << endl; + Ptr<DropRateErrorModel> dropsToClient = CreateObject<DropRateErrorModel>(); + dropsToClient->SetDropRate(stoi(dropRateToClient)); + devices.Get(0)->SetAttribute("ReceiveErrorModel", PointerValue(dropsToClient)); + } else if (goodBadProbToClient.length() == 0 && badGoodProbToClient.length() == 0 && dropRateToClient.length() == 0) { + cout << "Using drop rate to client: 0 %. (Use --drop-rate-to-client or --good-bad-prob-to-client and --bad-good-prob-to-client to change)" << endl; + } else { + cerr << "Cannot mix random loss model (--drop-rate-to-client) and Gilbert-Elliott-Loss-Model (--good-bad-prob-to-client and --bad-good-prob-to-client)" << endl; + NS_ABORT_MSG_IF(true, "Fix arguments!"); + } + + if (goodBadProbToServer.length() > 0 && badGoodProbToServer.length() > 0 && dropRateToServer.length() == 0) { + auto probGB = stof(goodBadProbToServer); + auto probBG = stof(badGoodProbToServer); + cout << "Using Gilbert-Elliot-Loss-Model for return path with P(b|g) = " << probGB << " \% and P(g|b) = " << probBG << " \%" << endl; + Ptr<GilbertElliottDropModel> dropsToServer = CreateObject<GilbertElliottDropModel>(); + dropsToServer->SetProbabilities(probGB, probBG); + devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(dropsToServer)); + } else if (goodBadProbToServer.length() == 0 && badGoodProbToServer.length() == 0 && dropRateToServer.length() > 0) { + cout << "Using uniform random loss model in return path with P('err') = " << dropRateToServer << " \%" << endl; + Ptr<DropRateErrorModel> dropsToServer = CreateObject<DropRateErrorModel>(); + dropsToServer->SetDropRate(stoi(dropRateToServer)); + devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(dropsToServer)); + } else if (goodBadProbToServer.length() == 0 && badGoodProbToServer.length() == 0 && dropRateToServer.length() == 0) { + cout << "Using drop rate to server: 0 %. (Use --drop-rate-to-server or --good-bad-prob-to-server and --bad-good-prob-to-server to change)" << endl; + } else { + cerr << "Cannot mix random loss model (--drop-rate-to-server) and Gilbert-Elliott-Loss-Model (--good-bad-prob-to-server and --bad-good-prob-to-server)" << endl; + NS_ABORT_MSG_IF(true, "Fix arguments!"); + } + } sim.Run(Seconds(36000)); } diff --git a/sim/scenarios/asymmetric-p2p/gilbert-elliott-drop-model.cc b/sim/scenarios/asymmetric-p2p/gilbert-elliott-drop-model.cc new file mode 100644 index 0000000000000000000000000000000000000000..23e2fe9dd8309d262e8aea1022b74886f09ca33b --- /dev/null +++ b/sim/scenarios/asymmetric-p2p/gilbert-elliott-drop-model.cc @@ -0,0 +1,86 @@ +#include "../helper/quic-packet.h" +#include "gilbert-elliott-drop-model.h" + +using namespace std; + +NS_OBJECT_ENSURE_REGISTERED(GilbertElliottDropModel); + +TypeId GilbertElliottDropModel::GetTypeId(void) { + static TypeId tid = TypeId("GilbertElliottDropModel") + .SetParent<ErrorModel>() + .AddConstructor<GilbertElliottDropModel>() + ; + return tid; +} + +GilbertElliottDropModel::GilbertElliottDropModel() + : + goodBadPerc(0), + badGoodPerc(0), + state(GilbertElliottState::GOOD), + distr(0.0, 1.0), + drop_counter(0), + pass_counter(0), + first_packet_logged(false) +{ + std::random_device rd; + rng = new std::mt19937(rd()); +} + +void GilbertElliottDropModel::DoReset(void) { } + +void GilbertElliottDropModel::log(void) { + if (!first_packet_logged) { + if (pass_counter > 0) { + cout << "First packet forwarded." << endl; + } else if (drop_counter > 0) { + cout << "First packet dropped." << endl; + } else { + cout << "WTF!" << endl; + } + first_packet_logged = true; + cout << "Will log stats after every " << LOG_AFTER_PACKETS << "th packet." << endl; + } else if (drop_counter + pass_counter >= LOG_AFTER_PACKETS) { + cout << "Among the last " << + LOG_AFTER_PACKETS << " packets, " << + drop_counter << " packets were dropped and " << + pass_counter << " were forwarded -> p_err = " << + drop_counter * 100.0 / LOG_AFTER_PACKETS << " %" << + endl; + drop_counter = 0; + pass_counter = 0; + } +} + +bool GilbertElliottDropModel::DoCorrupt(Ptr<Packet> p) { + if (!IsUDPPacket(p)) { + return false; + } + + QuicPacket qp = QuicPacket(p); + + // check if we switch the state: + float rnd = distr(*rng); + if (this->state == GilbertElliottState::GOOD && rnd < this->goodBadPerc) { + state = GilbertElliottState::BAD; + } else if (this->state == GilbertElliottState::BAD && rnd < this->badGoodPerc) { + state = GilbertElliottState::GOOD; + } + + if (this->state == GilbertElliottState::GOOD) { + pass_counter += 1; + log(); + qp.ReassemblePacket(); + return false; + } else { + drop_counter += 1; + log(); + return true; + } + +} + +void GilbertElliottDropModel::SetProbabilities(float goodBadPerc, float badGoodPerc) { + this->goodBadPerc = goodBadPerc; + this->badGoodPerc = badGoodPerc; +} diff --git a/sim/scenarios/asymmetric-p2p/gilbert-elliott-drop-model.h b/sim/scenarios/asymmetric-p2p/gilbert-elliott-drop-model.h new file mode 100644 index 0000000000000000000000000000000000000000..b991d141c1af91cef5c2180648fec02c1e7f3b24 --- /dev/null +++ b/sim/scenarios/asymmetric-p2p/gilbert-elliott-drop-model.h @@ -0,0 +1,42 @@ +#ifndef GILBERT_ELLIOTT_DROP_MODEL_H +#define GILBERT_ELLIOTT_DROP_MODEL_H + +#include <set> +#include <random> +#include "ns3/error-model.h" + +using namespace ns3; + +enum GilbertElliottState { + GOOD, + BAD, +}; + +// The model drops packets in the error state and passes packets in the normal state. +// The transition between both states is described by the transition probabilities. +class GilbertElliottDropModel : public ErrorModel { + public: + static TypeId GetTypeId(void); + GilbertElliottDropModel(); + void SetProbabilities(float goodBadPerc, float badGoodPerc); + + private: + float goodBadPerc; + float badGoodPerc; + GilbertElliottState state; + + std::mt19937 *rng; + std::uniform_real_distribution<> distr; + + unsigned int drop_counter; + unsigned int pass_counter; + bool first_packet_logged; + static constexpr unsigned int LOG_AFTER_PACKETS = 1000; + + bool DoCorrupt(Ptr<Packet> p); + void DoReset(void); + + void log(void); +}; + +#endif /* GILBERT_ELLIOTT_DROP_MODEL_H */ diff --git a/sim/scenarios/drop-rate/drop-rate-error-model.cc b/sim/scenarios/drop-rate/drop-rate-error-model.cc index 4234e4d15ab7d4b76c0be5bf83a06c92e5f268b3..45982efe3a9c1d9108fd93c640799965dfe81e7d 100644 --- a/sim/scenarios/drop-rate/drop-rate-error-model.cc +++ b/sim/scenarios/drop-rate/drop-rate-error-model.cc @@ -39,7 +39,12 @@ void DropRateErrorModel::log(void) { first_packet_logged = true; cout << "Will log stats after every " << LOG_AFTER_PACKETS << "th packet." << endl; } else if (drop_counter + pass_counter >= LOG_AFTER_PACKETS) { - cout << "Among the last " << LOG_AFTER_PACKETS << " packets, " << drop_counter << " packets were dropped and " << pass_counter << " were forwarded." << endl; + cout << "Among the last " << + LOG_AFTER_PACKETS << " packets, " << + drop_counter << " packets were dropped and " << + pass_counter << " were forwarded -> p_err = " << + drop_counter * 100.0 / LOG_AFTER_PACKETS << " %" << + endl; drop_counter = 0; pass_counter = 0; } diff --git a/sim/scenarios/drop-rate/drop-rate-error-model.h b/sim/scenarios/drop-rate/drop-rate-error-model.h index 74a8fbf8bb0e5fbcde80dd66155bf948dba01a42..07208245e596462cee9ce12a163f97da7392ec22 100644 --- a/sim/scenarios/drop-rate/drop-rate-error-model.h +++ b/sim/scenarios/drop-rate/drop-rate-error-model.h @@ -24,7 +24,7 @@ class DropRateErrorModel : public ErrorModel { bool first_packet_logged; static constexpr unsigned int LOG_AFTER_PACKETS = 1000; - bool DoCorrupt (Ptr<Packet> p); + bool DoCorrupt(Ptr<Packet> p); void DoReset(void); void log(void);