#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/csma-module.h"
#include "ns3/internet-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/applications-module.h"
#include "ns3/ipv4-global-routing-helper.h"
#include "ns3/netanim-module.h"
#include "ns3/node.h"
#include "ns3/names.h"
#include "ns3/gnuplot.h"
#include "_anim.h"
#include "_plot.h"
#include "ns3/packet.h"
#include "ns3/ipv4-header.h"
#include "_callback.h"

using namespace ns3;
using namespace std;

int 			clientPort = 9;
RTT_t 			rtt;
packetCount_t 		pc;
simulationTime_t 	st;
datasets_t  		ds;

NS_LOG_COMPONENT_DEFINE ("dos_log");

// attacker             server                   clients
//
//    n0 ---------------- n1 ----------------- n2   n3   n4
//                                             |    |    |
//           p2pAttack            p2pUser      |    |    |
//           30.0.0.0             30.0.1.0     ===========
//                                             LAN 30.0.2.0
//
//			    .1		  .2
//    .1	      .2       		      .1   .2   .3

int randomPort()
{
  int result = 1 + (std::rand() % (65535 - 1 + 1));
  return result == clientPort ? 10 : result;
}

int main (int args, char *argv[])
{
//  LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);
//  LogComponentEnable("UdpEchoServerApplication", LOG_LEVEL_INFO);

  ns3::Packet::EnablePrinting();

  NS_LOG_INFO ("Create nodes");
  Ptr<Node> n0 = CreateObject<Node> ();
  Ptr<Node> s = CreateObject<Node> ();
  Ptr<Node> n2 = CreateObject<Node> ();
  Ptr<Node> n3 = CreateObject<Node> ();
  Ptr<Node> n4 = CreateObject<Node> ();

  NS_LOG_INFO ("Group nodes");
  NodeContainer p2pAttackNodes (n0, s);
  NodeContainer p2pUserNodes (s, n2);
  NodeContainer csmaNodes (n2, n3, n4);
  NodeContainer allNodes (n0, s, n2, n3, n4);

  NS_LOG_INFO ("Create p2p attacker-server");
  PointToPointHelper p2pAttack;
  p2pAttack.SetDeviceAttribute ("DataRate", StringValue ("50Mbps"));
  p2pAttack.SetChannelAttribute ("Delay", StringValue("2ms"));

  NS_LOG_INFO ("Create p2p server-user");
  PointToPointHelper p2pUser;
  p2pUser.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
  p2pUser.SetChannelAttribute("Delay", StringValue("2ms"));

  NS_LOG_INFO ("Create user LAN");
  CsmaHelper csma;
  csma.SetChannelAttribute ("DataRate", StringValue ("100Mbps"));
  csma.SetChannelAttribute ("Delay", TimeValue (NanoSeconds (6560)));
  
  NS_LOG_INFO ("Put devices onto channels");
  NetDeviceContainer p2pAttackDevices; 
  p2pAttackDevices = p2pAttack.Install (p2pAttackNodes);

  NetDeviceContainer p2pUserDevices;
  p2pUserDevices = p2pUser.Install (p2pUserNodes);

  NetDeviceContainer csmaDevices;
  csmaDevices = csma.Install (csmaNodes);
  
  NS_LOG_INFO ("Install IP");
  InternetStackHelper stack;
  stack.Install (allNodes);

  NS_LOG_INFO ("Distribute IP adresses");
  Ipv4AddressHelper address;

  address.SetBase ("30.0.0.0", "255.255.255.252"); 
  Ipv4InterfaceContainer p2pAttackInterfaces;
  p2pAttackInterfaces = address.Assign (p2pAttackDevices);
  
  address.SetBase ("30.0.1.0", "255.255.255.252");
  Ipv4InterfaceContainer p2pUserInterfaces;
  p2pUserInterfaces = address.Assign (p2pUserDevices);
 
  address.SetBase ("30.0.2.0", "255.255.255.0");
  Ipv4InterfaceContainer csmaInterfaces;
  csmaInterfaces = address.Assign (csmaDevices);
  
  NS_LOG_INFO("Set IPv4 routing");
  Ipv4GlobalRoutingHelper::PopulateRoutingTables();

  NS_LOG_INFO ("Create a UDP server");
  UdpEchoServerHelper echoServer (clientPort);

  NS_LOG_INFO ("Start Apps on the server's side");
  ApplicationContainer serverApps = echoServer.Install (p2pAttackNodes.Get(1));
  serverApps.Start (Seconds (1.5));
  serverApps.Stop (Seconds (20.0));
  
  NS_LOG_INFO ("Configure client apps' parameters");
  UdpEchoClientHelper echoClient (s->GetObject<Ipv4>()->GetAddress(2, 0).GetLocal(), clientPort);
  echoClient.SetAttribute ("MaxPackets", UintegerValue (20));
  echoClient.SetAttribute ("Interval", TimeValue(Seconds(1.0)));
  echoClient.SetAttribute ("PacketSize", UintegerValue (1024));

  NS_LOG_INFO("UDP RANDOM PORT FLOOD");
  UdpEchoClientHelper echoAttack (s->GetObject<Ipv4>()->GetAddress(1, 0).GetLocal(), clientPort);
  echoAttack.SetAttribute ("MaxPackets", UintegerValue(1000000));
  echoAttack.SetAttribute ("Interval", TimeValue(MilliSeconds(1.0)));
  echoAttack.SetAttribute ("PacketSize", UintegerValue(1024));

  NS_LOG_INFO ("Start Apps on the client's side");
  ApplicationContainer clientApps = echoClient.Install (csmaNodes);
  clientApps.Start (Seconds (2.0));
  clientApps.Stop (Seconds (20.0));

  NS_LOG_INFO("Start Apps on the attacker's side");
  ApplicationContainer attackApps = echoAttack.Install (n0);
  attackApps.Start (Seconds (5.0));
  attackApps.Stop (Seconds (10.0));
 
  NS_LOG_INFO ("Save trace files");
  p2pAttack.EnablePcapAll ("dos:p2p_attacker");
  p2pUser.EnablePcapAll ("dos:p2p_user");
  csma.EnablePcapAll ("dos:csma");

  NS_LOG_INFO ("Set xml output");
  AnimationInterface anim = setAnimation(allNodes);

  NS_LOG_INFO("Sniff packets");
  Config::Connect("/NodeList/0/$ns3::Ipv4L3Protocol/Tx", MakeCallback(n0_tx));
  Config::Connect("/NodeList/0/$ns3::Ipv4L3Protocol/Rx", MakeCallback(n0_rx));
  Config::Connect("/NodeList/1/$ns3::Ipv4L3Protocol/Tx", MakeCallback(s_tx));
  Config::Connect("/NodeList/1/$ns3::Ipv4L3Protocol/Rx", MakeCallback(s_rx));
  Config::Connect("/NodeList/2/$ns3::Ipv4L3Protocol/Tx", MakeCallback(n2_tx));
  Config::Connect("/NodeList/2/$ns3::Ipv4L3Protocol/Rx", MakeCallback(n2_rx));
  Config::Connect("/NodeList/3/$ns3::Ipv4L3Protocol/Tx", MakeCallback(n3_tx));
  Config::Connect("/NodeList/3/$ns3::Ipv4L3Protocol/Rx", MakeCallback(n3_rx));
  Config::Connect("/NodeList/4/$ns3::Ipv4L3Protocol/Tx", MakeCallback(n4_tx));
  Config::Connect("/NodeList/4/$ns3::Ipv4L3Protocol/Rx", MakeCallback(n4_rx));

  NS_LOG_INFO ("Run simulation");
  Simulator::Stop (Seconds (20));
  Simulator::Run ();

  NS_LOG_INFO ("Stop simulation");
  Simulator::Destroy ();

  NS_LOG_INFO("Set the plot output");
  makePlots();

  return 0;
}

