Symulowanie sieci komputerowej w ns-3

Ostatnimi czasy intensywnie studiuję temat protokołów routingu w sieciach typu ad hoc. Zbudowanie odpowiedniej infrastruktury, posiadającej wiele komputerów, routerów oraz ich właściwe sparowanie znacznie wykracza poza moje możliwości logistyczno-finansowe, to też zdecydowałem się na ich symulacje. Użyłem w tym celu ns-3 Network Simulator, i właśnie o tym narzędziu oraz o pisaniu symulacji będzie dzisiejszy wpis.

Symulację piszemy w języku C++ (można też w Python), umieszczając pliki w folderze scratch programu. W przypadku języka C++ pliki muszą mieć rozszerzenie “cc“. Zalecane jest by w przypadku większej liczby plików pogrupować je w projekty poprzez utworzenie folderów. Należy wtedy jednak pamiętać o zdefiniowaniu funkcji main, która zostanie uruchomiona jako punkt startowy symulacji. Kompilacja plików robi się automatycznie podczas uruchomienia symulacji, sam program odpala się w następujący sposób:

./waf run --nazwa naszego pliku, ale bez rozszerzenia cc

Na czym polega filozofia pisania symulacji w ns-3?

Właściwie sprowadza się ona do odpowiedniego skonfigurowania topologi sieci, którą byśmy chcieli badać. Odpalenia zestawu aplikacji (symulowania ruchu), a następnie zebrania wyników i ich obrobienie. Ns-3 posiada gotowe komponenty, które znacznie przyspieszają całą pracę. Topologie sieci budujemy w oparciu o strukturę Node (węzeł). Może to być dowolne urządzenie w sieci (niekoniecznie tylko host). Do każdego węzła przypisujemy urządzenia, które ma posiadać (np. karta sieciowa i jej parametry), jak również zestaw aplikacji (można pisać własne, albo użyć już dostarczonych), które mają być na nim uruchomione. Dobrze to ilustruje obrazek poniżej (zapożyczony z prezentacji ns3 intro slides autorstwa Adil Alsuhaim).

Na szczęście do naszej dyspozycji jest masa helperów, które przyspieszą cały ten proces i skonfigurują ustaloną przez nas grupę węzłów. Poniżej przykład jak można utworzyć topologię sieci WIFI typu ad hoc, zaczerpnięty z http://www.lrc.ic.unicamp.br/ofswitch13/doc/html/wifi-hidden-terminal_8cc_source.html.

    // 1. Create 3 nodes 
    NodeContainer nodes;
    nodes.Create (3);

    // 2. Place nodes
    for (size_t i = 0; i < 4; ++i)
    {
        nodes.Get(i)-> AggregateObject (CreateObject<ConstantPositionMobilityModel> ());
    }

    // 3. Create propagation loss matrix
    Ptr<MatrixPropagationLossModel> lossModel = CreateObject<MatrixPropagationLossModel> ();
    lossModel->SetDefaultLoss (200); // set default loss to 200 dB (no link)
    for (size_t i = 0; i < 3; ++i)
    {
        lossModel->SetLoss (nodes.Get (i)-> GetObject<MobilityModel>(), nodes.Get (i+1)->GetObject<MobilityModel>(), 50); // set symmetric loss i <-> i+1 to 50 dB
    }

    // 4. Create & setup wifi channel
    Ptr<YansWifiChannel> wifiChannel = CreateObject <YansWifiChannel> ();
    wifiChannel->SetPropagationLossModel (lossModel);
    wifiChannel->SetPropagationDelayModel (CreateObject <ConstantSpeedPropagationDelayModel> ());

    // 5. Install wireless devices
    WifiHelper wifi;
    wifi.SetStandard (WIFI_PHY_STANDARD_80211b);
    Config::SetDefault ("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue ("2200"));
    wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager", 
                                  "DataMode",StringValue ("DsssRate11Mbps"));
    YansWifiPhyHelper wifiPhy =  YansWifiPhyHelper::Default ();
    wifiPhy.SetChannel (wifiChannel);
    WifiMacHelper wifiMac;
    wifiMac.SetType ("ns3::AdhocWifiMac");
    NetDeviceContainer devices = wifi.Install (wifiPhy, wifiMac, nodes);

    // 6. Install TCP/IP stack & assign IP addresses
    InternetStackHelper internet;
    internet.Install (nodes);
    Ipv4AddressHelper ipv4;
    ipv4.SetBase ("10.0.0.0", "255.0.0.0");
    ipv4.Assign (devices);

Jak widać powyżej tworzymy 3 węzły, na których konfigurujemy sieć WIFI typu ad hoc i ustawiamy adresy ip dla sieci 10.0.0.0. By uczynić symulację bardziej realną używamy tak zwanej tablicy utraty propagacji sygnału na wskutek np. działania warunków pogodowych. Ważna kwestią, o której jeszcze nie wspomniałem to kwestia kanałów. Powyżej używamy jednego z nich tj. YansWifiChannel. Kanały pozwalają określić relacje, połączyć poszczególne węzły. W przypadku sieci typu ad hoc, YansWifiChannel musi połączyć węzły na zasadzie “każdy z każdym“. Gdy już mamy skonfigurowaną topologie naszej symulowanej sieci, pozostaje nam zasymulować jakiś ruch przy użyciu aplikacji. Poniżej przykład żądania i odpowiedzi na ping, który zostanie wysłany w ściśle określonym przez nas przedziale czasowym.

  ApplicationContainer serverApps = echoServer.Install (nodes.Get (3));
  serverApps.Start (Seconds (1.0));
  serverApps.Stop (Seconds (10.0));
   
  UdpEchoClientHelper echoClient (csmaInterfaces.GetAddress (3), 9);
  echoClient.SetAttribute ("MaxPackets", UintegerValue (1));
  echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.0)));
  echoClient.SetAttribute ("PacketSize", UintegerValue (1024));
   
  ApplicationContainer clientApps = 
  echoClient.Install (wifiStaNodes.Get (nWifi - 1));
  clientApps.Start (Seconds (2.0));
  clientApps.Stop (Seconds (10.0));

Jeśli ktoś z Was złapał bakcyla, lub potrzebuje coś sobie potestować to polecam filmik poniżej, który bardziej szczegółowo to wszystko omawia.