Podręcznik do symulatora sieci ns-3. Rozdział 5

Podręcznik do symulatora sieci ns-3. Rozdział 5
rozdziały 1,2
rozdział 3
rozdział 4

5 Konfiguracja
5.1 Użycie modułu logowania
5.1.1 Przegląd logowania
5.1.2 Ustawienia logowania
5.1.3 Dodawanie logowania do twojego kodu
5.2 Użycie argumentów wiersza poleceń
5.2.1 Nadpisywanie domyślnych wartości atrybutów
5.2.2 Przechwytywanie własnych poleceń
5.3 Użycie systemu śledzenia
5.3.1 Śledzenie ASCII
Parsowanie śladów ASCII
5.3.2 Śledzenie PCAP

Rozdział 5

Konfiguracja

5.1 Użycie modułu logowania

Krótko omówiliśmy moduł logowania ns-3, przeglądając skrypt first.cc. W tym rozdziale bliżej przyjrzymy się możliwościom wykorzystania podsystemu logowania.

5.1.1 Przegląd logowania

Wiele dużych systemów wspiera jakieś narzędzie do rejestrowania komunikatów, a ns-3 nie jest wyjątkiem. W niektórych przypadkach, w "konsoli operatora" (która zazwyczaj jest stderr w systemach Unix) rejestrowane są tylko komunikaty o błędach. W innych systemach mogą być wyświetlane komunikaty ostrzegawcze i bardziej szczegółowe informacje. W niektórych przypadkach narzędzia logowania są używane do wyświetlania komunikatów debugowania, które mogą szybko "przeciążyć" wyjście.

Podejście stosowane w ns-3 zakłada, że wszystkie te poziomy informacji są użyteczne, a my zapewniamy wybór, wielopoziomowe podejście do rejestrowania komunikatów. Logowanie może być całkowicie wyłączone, włączone dla poszczególnych komponentów lub globalnie. Służą do tego konfigurowalne poziomy informacji. Moduł logowania ns-3 zapewnia stosunkowo prosty sposób uzyskania przydatnych informacji z twojej symulacji.

Musisz zrozumieć, że zapewniamy ogólny mechanizm – śledzenie – do pozyskiwania danych z twoich modeli, który powinien być preferowany do wyjścia podczas modelowania (więcej informacji o naszym systemie śledzenia znajdziesz w rozdziale podręcznika 5.3). Logowanie powinno być preferowaną metodą pozyskiwania informacji debugowych, ostrzeżeń, komunikatów o błędach lub szybkiej wyjścia komunikatów z twoich skryptów lub modeli w dowolnym momencie.

Aktualnie w systemie zdefiniowano siedem poziomów (typów) komunikatów dziennika w porządku rosnącym informacyjności.

  • LOG_ERROR — rejestracja komunikatów o błędach (powiązany makro: NS_LOG_ERROR);
  • LOG_WARN — rejestracja komunikatów ostrzegawczych (powiązany makro: NS_LOG_WARN);
  • LOG_DEBUG — rejestracja stosunkowo rzadkich specjalnych komunikatów debugowania (powiązany makro: NS_LOG_DEBUG);
  • LOG_INFO — rejestracja komunikatów informacyjnych o postępach programu (powiązany makro: NS_LOG_INFO);
  • LOG_FUNCTION — rejestracja komunikatów opisujących każdą wywołaną funkcję (dwa powiązane makra: NS_LOG_FUNCTION, używane dla funkcji członkowskich, oraz NS_LOG_FUNCTION_NOARGS, używane dla funkcji statycznych);
  • LOG_LOGIC — rejestracja komunikatów opisujących logiczny przebieg wewnątrz funkcji (powiązany makro: NS_LOG_LOGIC);
  • LOG_ALL — rejestracja wszystkich wymienionych powyżej (brak powiązanego makra).
    Dla każdego typu (LOG_TYPE) istnieje również typ LOG_LEVEL_TYPE, który, jeżeli jest używany, pozwala rejestrować oprócz jego własnego poziomu wszystkie poziomy nad nim. (W konsekwencji, LOG_ERROR i LOG_LEVEL_ERROR, a także LOG_ALL i LOG_LEVEL_ALL są funkcjonalnie równoważne.) Na przykład włączenie LOG_INFO pozwoli na rejestrację tylko komunikatów dostarczanych przez makro NS_LOG_INFO, natomiast włączenie LOG_LEVEL_INFO będzie również obejmować komunikaty dostarczane przez makra NS_LOG_DEBUG, NS_LOG_WARN oraz NS_LOG_ERROR.

Oferujemy również makro rejestrowania bezwarunkowego, które jest wyświetlane zawsze, niezależnie od poziomu rejestrowania lub wybranego komponentu.

  • NS_LOG_UNCOND — bezwarunkowa rejestracja powiązanego komunikatu (bez powiązanego poziomu rejestrowania).

Każdy poziom może być żądany oddzielnie lub kumulatywnie. Rejestrowanie można skonfigurować za pomocą zmiennej środowiskowej sh NS_LOG lub przez rejestrację wywołania funkcji systemowej. Jak pokazano wcześniej, system rejestrowania ma dokumentację Doxygen i teraz jest najlepszy czas, aby ją przejrzeć, jeśli jeszcze tego nie zrobiłeś.

Teraz, gdy przeczytałeś dokumentację bardzo szczegółowo, wykorzystajmy tę wiedzę, aby uzyskać ciekawą informację z przykładowego skryptu scratch/myfirst.cc, który już skompilowałeś.

5.1.2 Ustawienia logowania

Użyjmy zmiennej środowiskowej NS_LOG, aby uruchomić jeszcze kilka logów, ale najpierw, dla orientacji, uruchom ostatni skrypt, tak jak robiłeś wcześniej,

$ ./waf --run scratch/myfirst

Powinieneś zobaczyć już znajomy wynik pierwszego przykładowego programu ns-3.

$ Waf: Wchodzenie do katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Opuszczanie katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build` 'build'
ukończono pomyślnie (0.413s)
Wysłano 1024 bajtów do 10.1.1.2
Odebrano 1024 bajtów od 10.1.1.1
Odebrano 1024 bajtów od 10.1.1.2

Okazuje się, że "wysłane" i "odebrane" wiadomości, które widzisz powyżej, to tak naprawdę zarejestrowane komunikaty od UdpEchoClientApplication i UdpEchoServerApplication. Na przykład możemy poprosić aplikację kliencką, aby wydrukowała dodatkowe informacje, ustawiając poziom rejestrowania za pomocą zmiennej środowiskowej NS_LOG.

Od tego momentu założę, że używasz powłoki podobnej do sh, która stosuje składnię „VARIABLE = value”. Jeśli używasz powłok podobnych do csh, będziesz musiał przekształcić moje przykłady w składnię „ustawić wartość zmiennej setenv”, wymaganą przez te powłoki.

Obecnie aplikacja UDP Echo Client odpowiada na następującą linię kodu w scratch/myfirst.cc,

LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);

Aktywuje poziom rejestrowania LOG_LEVEL_INFO. Kiedy przekazujemy flagę poziomu rejestrowania, tak naprawdę włączamy ten poziom i wszystkie niższe poziomy. W tym przypadku włączyliśmy NS_LOG_INFO, NS_LOG_DEBUG, NS_LOG_WARN oraz NS_LOG_ERROR. Możemy zwiększyć poziom rejestrowania i uzyskać więcej informacji, bez zmiany skryptu i ponownej kompilacji, ustanawiając zmienną środowiskową NS_LOG w następujący sposób:

$ export NS_LOG=UdpEchoClientApplication=level_all

Tak ustalamy następującą wartość zmiennej NS_LOG w powłoce sh,

UdpEchoClientApplication=level_all

Lewą stroną przypisania jest nazwa komponentu rejestrowanego, który chcemy skonfigurować, a prawą stroną jest flaga, którą chcemy dla niego zastosować. W tym przypadku zamierzamy włączyć wszystkie poziomy debugowania w aplikacji. Jeśli uruchomisz skrypt z ustawioną w ten sposób NS_LOG, system rejestrowania ns-3 przyjmie zmiany i powinieneś zobaczyć następujący wynik:

Waf: Wchodzenie do katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Opuszczanie katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' ukończono pomyślnie (0.404s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
Wysłano 1024 bajtów do 10.1.1.2
Odebrano 1024 bajtów od 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
Odebrano 1024 bajtów od 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

Dodatkowe informacje debugowania dostarczane przez aplikację są teraz zgodne z poziomem NS_LOG_FUNCTION. Pokazuje to każdy przypadek wywołania funkcji w trakcie wykonywania skryptu. Zazwyczaj w funkcjach-metodach zaleca się użycie (przynajmniej)NS_LOG_FUNCTION (this). Używaj NS_LOG_FUNCTION_NOARGS ()
tylko w funkcjach statycznych. Warto jednak zauważyć, że w systemie ns-3 nie ma wymagań dotyczących wsparcia dla jakiejkolwiek funkcjonalności logowania. Decyzja o tym, ile informacji jest rejestrowanych, pozostaje w gestii projektanta modelu. W przypadku aplikacji echo dostępna jest znaczna ilość danych wyjściowych do logowania.

Teraz możesz przeglądać dziennik wywołań funkcji, które zostały wykonane przez aplikację. Jeśli się dobrze przyjrzysz, zauważysz dwukropek między linijką UdpEchoClientApplication a nazwą metody, w miejscu, gdzie mógłbyś oczekiwać zobaczyć operator przestrzeni nazw C++ (: :). Zrobiono to celowo.

W rzeczywistości nie jest to nazwa klasy, a nazwa komponentu logowania. Kiedy istnieje zgodność między plikiem źródłowym a klasą, zazwyczaj to nazwa klasy, ale musisz zrozumieć, że w rzeczywistości nie jest to nazwa klasy, a tam jest jeden dwukropek zamiast podwójnego dwukropka. To sposób, aby zasugerować Ci w sposób subtelny, aby rozdzielić nazwę komponentu logowania od nazwy klasy.

Jednak w niektórych przypadkach trudno jest określić, która metoda faktycznie generuje komunikat dziennika. Jeśli przyjrzysz się tekstowi powyżej, możesz zadać pytanie, skąd wzięła się linia „Otrzymano 1024 bajty od 10.1.1.2`. Możesz rozwiązać ten problem, ustawiając poziom prefix_func w zmiennej środowiskowej NS_LOG. Spróbuj zrobić to następująco,

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func'

Zauważ, że cudzysłowy są konieczne, ponieważ pionowa kreska, której używamy do oznaczenia operacji OR, jest również separatorem potoków w Unixie. Teraz, jeśli uruchomisz skrypt, zobaczysz, że system logowania zapewnia, że każde komunikat z danego dziennika ma prefiks z nazwą komponentu.

Waf: Wchodzenie do katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Opuszczenie katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'budowa' zakończona pomyślnie (0.417s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Wysłano 1024 bajty do 10.1.1.2
Otrzymano 1024 bajty od 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
UdpEchoClientApplication:HandleRead(): Otrzymano 1024 bajty od 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

Teraz możesz dostrzec, że wszystkie wiadomości z aplikacji UDP echo-klienta są identyfikowane jako takie. Wiadomość „Otrzymano 1024 bajty od 10.1.1.2” jest teraz wyraźnie zdefiniowana jako pochodząca z aplikacji echo-klienta. Pozostała wiadomość powinna pochodzić z aplikacji UDP echo-serwera. Możemy włączyć ten komponent, wprowadzając listę komponentów, oddzielonych dwukropkami, w zmiennej środowiskowej NS_LOG.

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func:
               UdpEchoServerApplication=level_all|prefix_func'

Ostrzeżenie: w podanym powyżej przykładzie tekstowym musisz usunąć znak nowej linii po dwukropku (:), został on użyty do formatowania dokumentu. Teraz, jeśli uruchomisz skrypt, zobaczysz wszystkie wiadomości dziennika z aplikacji echo-klienta i echo-serwera. Może to być bardzo przydatne podczas debugowania.

Waf: Wchodzenie do katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Opuszczenie katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'budowa' zakończona pomyślnie (0.406s)
UdpEchoServerApplication:UdpEchoServer()
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoServerApplication:StartApplication()
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Wysłano 1024 bajty do 10.1.1.2
UdpEchoServerApplication:HandleRead(): Otrzymano 1024 bajty od 10.1.1.1
UdpEchoServerApplication:HandleRead(): Odbieranie pakietu
UdpEchoClientApplication:HandleRead(0x624920, 0x625160)
UdpEchoClientApplication:HandleRead(): Otrzymano 1024 bajty od 10.1.1.2
UdpEchoServerApplication:StopApplication()
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Czasami pomocne jest również posiadanie możliwości zobaczenia czasu symulacji, w którym utworzono wiadomość dziennika. Możesz to zrobić, dodając po I lub bit prefix_time:

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func|prefix_time: UdpEchoServerApplication=level_all|prefix_func|prefix_time'

Ponownie, musisz usunąć powyższy znak nowej linii. Jeśli teraz uruchomisz skrypt, powinieneś zobaczyć następujący wynik:

Waf: Wchodzenie do katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Opuszczanie katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' zakończono pomyślnie (0.418s)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Wysłano 1024 bajty do 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Otrzymano 1024 bajty od 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Echoowanie pakietu
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Otrzymano 1024 bajty od 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Zwróć uwagę, że konstruktor dla UdpEchoServer został wywołany podczas symulacji 0 sekund. W rzeczywistości ma to miejsce przed rozpoczęciem symulacji, ale czas ten jest wyświetlany jako zero sekund. To samo dotyczy komunikatu konstruktor UdpEchoClient.

Waf: Wchodzenie do katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Opuszczanie katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' zakończono pomyślnie (0.418s)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Wysłano 1024 bajty do 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Otrzymano 1024 bajty od 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Echoowanie pakietu
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Otrzymano 1024 bajty od 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Przypomnijmy, że skrypt scratch/first.cc uruchomił aplikację serwera echo na jedną sekundę przed rozpoczęciem symulacji. Teraz możesz zobaczyć, że metoda StartApplication serwera jest faktycznie wywoływana w pierwszej sekundzie. Możesz również zauważyć, że klient echo uruchamia się w drugiej sekundzie symulacji, jak prosiliśmy w skrypcie.

Teraz możesz śledzić postęp symulacji wywołując ScheduleTransmit w kliencie, co wywołuje zwrotną funkcję HandleRead w aplikacji serwera echo. Zauważ, że czas potrzebny na wysłanie pakietu przez dwupunktowe połączenie wynosi 3,69 milisekundy. Widać, że serwer echo rejestruje komunikat informujący, że odpowiedział na pakiet, a następnie, po opóźnieniu kanału, widzisz, że klient echo otrzymuje pakiet echo w swojej metodzie HandleRead.

W tej symulacji wiele dzieje się niezauważalnie dla Ciebie. Ale możesz bardzo łatwo śledzić cały proces, włączając w systemie wszystkie komponenty rejestrowania. Spróbuj ustawić w zmiennej NS_LOG następującą wartość,

$ export 'NS_LOG=*=level_all|prefix_func|prefix_time'

Gwiazdka powyżej jest symbolem zastępczym komponentu rejestrowania. To włączy wszystkie wpisy we wszystkich komponentach używanych w symulacji. Nie będę powtarzać wyjścia tutaj (w momencie pisania artykułu generuje 1265 linii wyjścia dla jednego pakietu echo), ale możesz przekierować te informacje do pliku i przeglądać je w swoim ulubionym edytorze,

$ ./waf --run scratch/myfirst > log.out 2>&1

Osobiście korzystam z tej niezwykle szczegółowej wersji logowania, gdy mam problem i nie mam pojęcia, gdzie poszło nie tak. Mogę dość łatwo śledzić wykonywanie kodu, nie ustawiając punktów przerwania i nie wykonując przestępstwa w debuggerze. Mogę po prostu edytować wyjście w moim ulubionym edytorze i szukać tego, co oczekuję, i zobaczyć, co się dzieje, czego się nie spodziewałem. Kiedy mam ogólne pojęcie o tym, co poszło nie tak, przechodzę do debuggera, aby dokładniej zbadać problem. Tego rodzaju wyjście może być szczególnie przydatne, gdy twój skrypt robi coś całkowicie nieoczekiwanego. Jeśli korzystasz tylko z debuggera, możesz całkowicie przegapić niespodziewany zwrot akcji. Logowanie czyni te zwroty widocznymi.

5.1.3 Dodawanie logowania do twojego kodu

Możesz dodać nowe wpisy do swoich symulacji, wywołując komponent log z kilku makr. Zróbmy to w skrypcie myfirst.cc, który mamy w 'czystym' katalogu. Przypomnijmy, że w tym skrypcie zdefiniowaliśmy komponent logowania:

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

Wiesz, że możesz włączyć logowanie wszystkich wiadomości tego komponentu, ustawiając zmienną środowiskową NS_LOG na różnych poziomach. Kontynuujmy i dodajmy kilka wpisów do skryptu. Macros używany do dodawania wiadomości na poziomie informacyjnym to NS_LOG_INFO. Dodajmy wiadomość (bezpośrednio przed tym, jak zaczniemy tworzyć węzły), która informuje, że skrypt jest na etapie tworzenia topologii ("Creating Topology"). Robi się to w następującym fragmencie kodu,
Otwórz scratch/myfirst.cc w swoim ulubionym edytorze i dodaj linię,
NS_LOG_INFO ("Creating Topology");
bezpośrednio przed liniami,

NodeContainer nodes;
nodes.Create (2);

Teraz skompiluj skrypt, używając waf, i wyczyść zmienną NS_LOG, aby wyłączyć strumień logowania, który włączyliśmy wcześniej:

$ ./waf
$ export NS_LOG=
Teraz, jeśli uruchomisz skrypt,
$ ./waf --run scratch/myfirst

nie zobaczysz nowego komunikatu, ponieważ związany z nim komponent logowania (FirstScriptExample) nie został włączony. Aby zobaczyć swoją wiadomość, musisz włączyć komponent logowania FirstScriptExample z poziomem nie niższym niż NS_LOG_INFO. Jeśli chcesz tylko zobaczyć ten konkretny poziom logowania, możesz go włączyć w ten sposób,

$ eksport NS_LOG=FirstScriptExample=info

Jeśli uruchomisz skrypt teraz, zobaczysz nowe komunikat „Tworzenie topologii”

Waf: Wchodzenie do katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Opuszczenie katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' zakończono pomyślnie (0.404s)
Tworzenie topologii
Wysłano 1024 bajty do 10.1.1.2
Otrzymano 1024 bajty z 10.1.1.1
Otrzymano 1024 bajty z 10.1.1.2

5.2 Użycie argumentów wiersza poleceń

5.2.1 Nadpisywanie domyślnych wartości atrybutów

Innym sposobem zmiany zachowania skryptów ns-3 bez edytowania i kompilacji jest użycie argumentów wiersza poleceń. Oferujemy mechanizm do analizy argumentów wiersza poleceń oraz automatycznej konfiguracji zmiennych lokalnych i globalnych w oparciu o wyniki.

Pierwszym krokiem w używaniu systemu argumentów wiersza poleceń jest zadeklarowanie analizatora składniowego wiersza poleceń. To jest dość łatwe (w twoim głównym programie), jak w poniższym kodzie,

int
main (int argc, char *argv[])
{
...
CommandLine cmd;
cmd.Parse (argc, argv);
...
}

Ten prosty dwuwierszowy fragment jest bardzo przydatny sam w sobie. Otwiera drzwi do globalnej zmiennej ns-3 oraz systemu atrybutów. Dodajmy dwie linie kodu na początku głównej funkcji skryptu scratch/myfirst.cc. Następnie kompilujemy skrypt i uruchamiamy go, podczas uruchamiania prosząc o pomoc w następujący sposób,

$ ./waf --run "scratch/myfirst --PrintHelp"

To polecenie poprosi Waf o uruchomienie skryptu scratch/myfirst i przekazuje mu argument wiersza poleceń --PrintHelp. Cudzysłowy są potrzebne, aby pokazać, dla jakiego programu przeznaczony jest argument. Analizator wiersza poleceń wykryje argument --PrintHelp i wyświetli odpowiedź,

Waf: Wchodzenie do katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Opuszczenie katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' zakończono pomyślnie (0.413s)
TcpL4Protocol:TcpStateMachine()
CommandLine:HandleArgument(): Obsłuż nazwę argumentu=PrintHelp wartość=
--PrintHelp: Wyświetl ten komunikat pomocy.
--PrintGroups: Wyświetl listę grup.
--PrintTypeIds: Wyświetl wszystkie TypeIds.
--PrintGroup=[grupa]: Wyświetl wszystkie TypeIds grupy.
--PrintAttributes=[typeid]: Wyświetl wszystkie atrybuty typeid.
--PrintGlobals: Wyświetl listę globalnych.

Teraz rozważmy opcję --PrintAttributes. Już wspomnieliśmy o systemie atrybutów ns-3, podczas badania skryptu first.cc. Widzieliśmy następujące linie kodu,

PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));

i mówiliśmy, że DataRate jest atrybutem PointToPointNetDevice. Zastosujmy analizator argumentów wiersza poleceń, aby zobaczyć atrybuty PointToPointNetDevice. Lista pomocy mówi, że musimy dostarczyć TypeId. To jest nazwa klasy, do której należą interesujące nas atrybuty. W naszym przypadku będzie to ns3::PointToPointNetDevice. Kontynuujmy, wprowadź:

$ .\/waf --run "scratch\/myfirst --PrintAttributes=ns3::PointToPointNetDevice"

System wydrukuje wszystkie atrybuty tego typu urządzenia sieciowego. Zobaczysz, że wśród atrybutów na liście znajduje się

--ns3::PointToPointNetDevice::DataRate=[32768bps]:
Domyślny współczynnik danych dla łączy bezpośrednich

To jest wartość domyślna, która będzie używana w systemie podczas tworzenia obiektu PointToPointNetDevice. Nadpiszemy tę wartość domyślną za pomocą parametru Atrybut do PointToPointHelper wyżej. Użyjmy wartości domyślnych dla urządzeń point-to-point i kanałów. W tym celu usuńmy wywołania SetDeviceAttribute i SetChannelAttribute z myfirst.cc, które mamy w czystym katalogu.

Twój skrypt powinien teraz po prostu deklarować PointToPointHelper i nie wykonywać żadnych operacji instalacyjnych, jak pokazano w poniższym przykładzie,

...
NodeContainer nodes;
nodes.Create (2);
PointToPointHelper pointToPoint;
NetDeviceContainer devices;
devices = pointToPoint.Install (nodes);
...

Kontynuuj i stwórz nowy skrypt z Waf (.\/waf) i wróćmy, aby włączyć pewne logowanie z aplikacji serwera UDP echo oraz dodać prefix czasowy.

$ export 'NS_LOG=UdpEchoServerApplication=level_all|prefix_time'

Jeśli uruchomisz skrypt, powinieneś zobaczyć następujący wynik:

Waf: Wchodzenie do katalogu `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\nWaf: Zostawianie katalogu `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\n'build' zakończono pomyślnie (0.405s)\n0s UdpEchoServerApplication:UdpEchoServer()\n1s UdpEchoServerApplication:StartApplication()\nWysłano 1024 bajty do 10.1.1.2\n2.25732s Otrzymano 1024 bajty z 10.1.1.1\n2.25732s Powtarzanie pakietu\nOtrzymano 1024 bajty z 10.1.1.2\n10s UdpEchoServerApplication:StopApplication()\nUdpEchoServerApplication:DoDispose()\nUdpEchoServerApplication:~UdpEchoServer()

Przypomnijmy, że ostatnim razem, gdy patrzyliśmy na czas symulacji, moment otrzymania pakietu przez serwer echo, miał on miejsce w 2.00369 sekundy.

2.00369s UdpEchoServerApplication:HandleRead(): Otrzymano 1024 bajty z 10.1.1.1

Teraz odbiera pakiet w 2.25732 sekundy. Dzieje się tak, ponieważ po prostu zmniejszyliśmy szybkość transmisji danych PointToPointNetDevice z pięciu megabitów na sekundę do wartości domyślnej, która wynosi 32768 bitów na sekundę. Gdybyśmy podali nowy DataRate za pomocą wiersza poleceń, moglibyśmy ponownie przyspieszyć nasze modelowanie. Zrobimy to w następujący sposób, zgodnie z formułą sugerowaną przez element pomocy:

$ .\/waf --run "scratch\/myfirst --ns3::PointToPointNetDevice::DataRate=5Mbps"

W rezultacie wartość atrybutu DataRate domyślnie powróci do pięciu megabitów na sekundę. Jesteś zaskoczony wynikiem? Okazuje się, że aby przywrócić pierwotne działanie skryptu, musimy również ustawić opóźnienie kanału na odpowiadające prędkości światła. Możemy poprosić system wiersza poleceń o wydrukowanie atrybutów kanału, tak jak robiliśmy to dla urządzenia sieciowego:

$ .\/waf --run "scratch\/myfirst --PrintAttributes=ns3::PointToPointChannel"

Odkryjemy, że atrybut opóźnienia kanału jest ustawiony w następujący sposób:

--ns3::PointToPointChannel::Delay=[0ns]:
Opóźnienie transmisji przez kanał

Następnie możemy za pomocą systemu wiersza poleceń ustawić te dwa wartości domyślne:

$ .\/waf --run "scratch\/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms"

w tym przypadku przywracamy czas, który mieliśmy, gdy wyraźnie ustawiliśmy DataRate i Delay w skrypcie:

Waf: Wchodzenie do katalogu `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
Waf: Opuszczenie katalogu `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
'build' zakończono pomyślnie (0.417s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Wysłano 1024 bajty do 10.1.1.2
2.00369s Otrzymano 1024 bajty z 10.1.1.1
2.00369s Echoing packet
Otrzymano 1024 bajty z 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Zauważ, że pakiet został ponownie odebrany przez serwer po 2,00369 sekundach. Faktycznie moglibyśmy w ten sposób ustawić dowolne z używanych atrybutów w skrypcie. W szczególności moglibyśmy ustawić wartości różne od jedności dla atrybutów MaxPackets UdpEchoClient.

Jak byś to wykorzystał? Spróbuj. Pamiętaj, aby skomentować miejsce, w którym przedefiniowujemy wartość atrybutu domyślnego i wyraźnie go ustawić. MaxPackets w skrypcie. Następnie musisz ponownie zbudować skrypt. Możesz również w systemie wiersza poleceń uzyskać pomoc w zakresie składni dotyczącej ustawienia nowej wartości atrybutu domyślnego. Po zrozumieniu tego będziesz mógł zarządzać liczbą pakietów wyświetlanych w wierszu poleceń. Ponieważ jesteśmy pilnymi ludźmi, nasz wiersz poleceń powinien wyglądać mniej więcej tak:

$ .\/waf --run "scratch\/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms
--ns3::UdpEchoClient::MaxPackets=2"

Naturalnym pytaniem, które się pojawia w tym miejscu, jest to, jak dowiedzieć się o istnieniu wszystkich tych atrybutów. Ponownie, system wiersza poleceń ma funkcję pomocy w tej kwestii. Jeśli poprosimy o pomoc w wierszu poleceń, powinniśmy zobaczyć:

$ ./waf --run "scratch/myfirst --PrintHelp"
myfirst [Argumenty programu] [Ogólne argumenty]
Ogólne argumenty:
--PrintGlobals: Wydrukuj listę globalnych zmiennych.
--PrintGroups: Wydrukuj listę grup.
--PrintGroup=[grupa]: Wydrukuj wszystkie TypeId grupy.
--PrintTypeIds: Wydrukuj wszystkie TypeId.
--PrintAttributes=[typeid]: Wydrukuj wszystkie atrybuty typu typeid.
--PrintHelp: Wydrukuj tę wiadomość pomocy.

Jeśli wybierzesz argument „PrintGroups”, powinieneś zobaczyć listę wszystkich zarejestrowanych grup. TypeId. Nazwy grup są zgodne z nazwami modułów w katalogu źródłowym (choć z wielką literą). Wydrukowanie wszystkich informacji naraz będzie zbyt obszerne, dlatego dostępny jest dodatkowy filtr do drukowania informacji według grup. Tak więc, ponownie skupiając się na module „punkt-punkt”:

./waf --run "scratch/myfirst --PrintGroup=PointToPoint"
TypeIds w grupie PointToPoint:
ns3::PointToPointChannel
ns3::PointToPointNetDevice
ns3::PointToPointRemoteChannel
ns3::PppHeader

Możesz tutaj znaleźć dostępne nazwy TypeId do wyszukiwania atrybutów, na przykład w
--PrintAttributes = ns3::PointToPointChannel, jak pokazano powyżej.

Innym sposobem na poznanie atrybutów jest korzystanie z dokumentacji Doxygen ns‑3. Znajdziesz tam stronę, która wymienia wszystkie zarejestrowane atrybuty w symulatorze.

5.2.2 Przechwytywanie własnych poleceń

Możesz także dodać własne haki poprzez system wiersza poleceń. Robi się to dość łatwo, korzystając z metod parsera poleceń. AddValue.
Wykorzystajmy tę możliwość, aby określić liczbę pakietów, które powinny być wyświetlane, zupełnie innym sposobem. Dodajmy lokalną zmienną o nazwie nPackets do funkcji main. Ustawimy ją na jeden, aby odpowiadała naszemu poprzedniemu domyślnemu zachowaniu. Aby pozwolić parserowi poleceń na zmianę tej wartości, musimy przechwycić tę wartość w parserze. Robimy to, dodając wywołanie AddValue. Przejdź i zmień skrypt scratch/myfirst.cc tak, aby zaczynał się od następującego kodu,

int
main (int argc, char *argv[])
{
uint32_t nPackets = 1;
CommandLine cmd;
cmd.AddValue("nPackets", "Liczba pakietów do echa", nPackets);
cmd.Parse(argc, argv);
...

Przewiń w dół do miejsca w skrypcie, gdzie ustawiamy atrybut MaxPackets i zmień go tak, aby był ustawiony na zmienną nPackets zamiast stałej 1, jak pokazano poniżej.

echoClient.SetAttribute ("MaxPackets", UintegerValue (nPackets));

Teraz, jeśli uruchomisz skrypt i podasz argument --PrintHelp, powinieneś zobaczyć nowy argument użytkownika wymieniony w ekranie pomocy. Wprowadź,

$ ./waf --run "scratch/myfirst --PrintHelp"
Waf: Wchodzenie do katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Opuszczenie katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' zakończone pomyślnie (0.403s)
--PrintHelp: Wyświetl tę wiadomość pomocy.
--PrintGroups: Wyświetl listę grup.
--PrintTypeIds: Wyświetl wszystkie TypeIds.
--PrintGroup=[group]: Wyświetl wszystkie TypeIds danej grupy.
--PrintAttributes=[typeid]: Wyświetl wszystkie atrybuty typu typeid.
--PrintGlobals: Wyświetl listę globalnych.
Argumenty użytkownika:
--nPackets: Liczba pakietów do powtórzenia

Jeśli chcesz zmienić liczbę przesyłanych pakietów, możesz to zrobić, ustawiając w wierszu poleceń argument -nPackets,

$ ./waf --run "scratch/myfirst --nPackets=2"

Teraz powinieneś zobaczyć

Waf: Wchodzenie do katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Opuszczenie katalogu `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' zakończone pomyślnie (0.404s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Wysłano 1024 bajty do 10.1.1.2
2.25732s Otrzymano 1024 bajty od 10.1.1.1
2.25732s Echoing packet
Otrzymano 1024 bajty od 10.1.1.2
Wysłano 1024 bajty do 10.1.1.2
3.25732s Otrzymano 1024 bajty od 10.1.1.1
3.25732s Echoing packet
Otrzymano 1024 bajty od 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Wysłałeś teraz dwa pakiety. Dość proste, prawda?
Możesz zobaczyć, że jako użytkownik ns-3, możesz używać systemu argumentów wiersza poleceń do zarządzania globalnymi wartościami i atrybutami. Jeśli jesteś autorem modelu, możesz dodać nowe atrybuty do swoich obiektów, a one będą automatycznie dostępne do dostosowania przez twoich użytkowników za pośrednictwem systemu wiersza poleceń. Jeśli jesteś autorem skryptu, możesz dodawać nowe zmienne do swoich skryptów i bezproblemowo połączyć je z systemem wiersza poleceń.

5.3 Użycie systemu śledzenia

Cały sens modelowania polega na generowaniu wyników do dalszego badania, a system śledzenia ns-3 jest głównym mechanizmem do tego. Ponieważ ns-3 jest programem w C++, można używać standardowych metod generowania wyników programów w C++:

#include <iostream>
...
int main ()
{
...
std::cout << "The value of x is " << x << std::endl;
...
}

Możesz nawet użyć modułu logowania, aby dodać trochę struktury do swojego rozwiązania. Istnieje wiele problemów wynikających z tego podejścia, dlatego w celu rozwiązania tych problemów dostarczyliśmy ogólną podsystem monitorowania zdarzeń.

Główne cele systemu śledzenia ns-3:

  • Dla podstawowych zadań system śledzenia powinien umożliwiać użytkownikowi generowanie standardowego śledzenia dla popularnych źródeł i wybieranie obiektów generujących śledzenie;

  • Użytkownicy średniego poziomu powinni mieć możliwość rozszerzania systemu śledzenia w celu zmiany generowanego formatu wyjściowego lub dodawania nowych źródeł śledzenia, bez modyfikacji rdzenia symulatora;

  • Zaawansowani użytkownicy mogą modyfikować rdzeń symulatora, aby dodać nowe źródła i odbiorniki śledzenia. System śledzenia ns-3 oparty jest na zasadach niezależnych źródeł śledzenia oraz odbiorników, a także zunifikowanym mechanizmie łączenia źródeł z konsumentami.

System śledzenia ns-3 oparty jest na zasadach niezależnych źródeł i odbiorników śledzenia, a także zunifikowanym mechanizmie łączenia źródeł z odbiornikami. Źródłami śledzenia są obiekty, które mogą sygnalizować zdarzenia zachodzące w symulacji oraz zapewnić dostęp do interesujących danych podstawowych. Na przykład, źródło śledzenia może wskazywać, kiedy urządzenie sieciowe otrzymało pakiet i umożliwia dostęp do zawartości pakietu dla interesujących odbiorników śledzenia.

Źródła śledzenia są same w sobie bezużyteczne, jeśli nie są „połączone” z innymi częściami kodu, które rzeczywiście wykonują coś użytecznego z informacjami dostarczonymi przez odbiornik. Śledzący są konsumentami zdarzeń i danych dostarczanych przez źródła śledzenia. Na przykład, można stworzyć odbiornik śledzenia, który (po podłączeniu do źródła śledzenia z poprzedniego przykładu) wydrukuje interesujące części otrzymanego pakietu.

Rozsądne jest takie wyraźne rozdzielenie, aby pozwolić użytkownikom na podłączanie nowych typów odbiorników do istniejących źródeł śledzenia bez konieczności edytowania i rekompilowania rdzenia symulatora. W ten sposób, w powyższym przykładzie, użytkownik może zdefiniować nowego śledzącego w swoim skrypcie i podłączyć go do istniejącego źródła śledzenia zdefiniowanego w rdzeniu symulacji, edytując tylko skrypt użytkownika.

W tym przewodniku omówimy kilka wcześniej zdefiniowanych źródeł i odbiorników oraz pokażemy, jak można je skonfigurować przy minimalnym wysiłku użytkownika. Zobacz podręcznik ns-3 lub sekcje z instrukcjami, aby uzyskać informacje o zaawansowanej konfiguracji śledzenia, w tym rozszerzanie przestrzeni nazw śledzenia i tworzenie nowych źródeł śledzenia.

5.3.1 Śledzenie ASCII

ns-3 zapewnia funkcjonalność pomocniczą, która dostarcza niskopoziomowy system śledzenia, aby pomóc Ci w szczegółach podczas konfigurowania prostych śledzeń pakietów. Jeśli włączysz tę funkcję, zobaczysz dane wyjściowe w plikach ASCII. Dla tych, którzy znają wyjście ns-2, ten typ śledzenia jest analogiczny do out.tr, który jest generowany przez wiele skryptów.

Zabierzmy się do pracy i dodajmy kilka wyników śledzenia ASCII do naszego skryptu scratch/myfirst.cc. Tuż przed wywołaniem Symulator::Run(), dodaj następujące linie kodu:
AsciiTraceHelper ascii;

pointToPoint.EnableAsciiAll(ascii.CreateFileStream("myfirst.tr"));

Jak w wielu innych idiomach ns-3, ten kod wykorzystuje obiekt pomocniczy do tworzenia śledzeń ASCII. Druga linia zawiera dwa zagnieżdżone wywołania metody. Metoda „wewnętrzna” CreateFileStream() używa idiomu anonimowego obiektu do stworzenia obiektu strumienia plików na stosie (bez nazwy obiektu) i przekazuje go do wywoływanej metody. W przyszłości zgłębimy ten temat, ale wszystko, co musisz wiedzieć na tym etapie, to to, że tworzysz obiekt reprezentujący plik o nazwie myfirst.tr i przekazujesz go do ns-3. Powierzamy ns-3 opiekę nad utworzonym obiektem przez cały czas jego życia, podczas którego rozwiązywane są problemy związane z mało znanym (zamierzonym) ograniczeniem związanym z kopiującymi konstruktorami obiektów strumieniowych C++.

Zewnętrzne wywołanie EnableAsciiAll() informuje pomocnika, że chcesz włączyć w swojej symulacji śledzenie ASCII dla wszystkich połączeń urządzeń point-to-point oraz że chcesz, aby (określone) odbiorniki śledzenia zapisywały informacje o ruchu pakietów w formacie ASCII.

Dla tych, którzy znają ns-2, śledzone zdarzenia odpowiadają znanym punktom śledzenia, które rejestrują zdarzenia „+”, „-”, „d” i „r”.
Teraz możesz skompilować skrypt i uruchomić go z wiersza poleceń:

$ ./waf --run scratch/myfirst

Jak wiele razy wcześniej widziałeś kilka komunikatów od Waf, a potem „‘build’ finished successfully” (budowa zakończona pomyślnie) z pewną ilością komunikatów od działającego programu.

Podczas działania programu zostanie utworzony plik o nazwie myfirst.tr. Z powodu szczególnych właściwości działania Waf, domyślnie plik nie jest tworzony w lokalnym katalogu, lecz w katalogu głównym repozytorium. Jeśli chcesz zmienić ścieżkę, w której są zapisywane ślady, możesz użyć dla Waf parametru --cwd. Nie zrobiliśmy tego, więc aby spojrzeć na plik ASCII śledzenia myfirst.tr w swoim ulubionym edytorze, musimy przejść do katalogu głównego naszego repozytorium.

Parsowanie śladów ASCII

Jest tam bardzo dużo informacji w dość gęstej formie, ale pierwsze, na co należy zwrócić uwagę, to to, że plik składa się z oddzielnych linii. Będzie to wyraźnie widoczne, jeśli poszerzysz okno przeglądarki.

Każda linia w pliku odpowiada zdarzeniu śledzenia. W tym przypadku śledzimy zdarzenia w kolejce przesyłania, obecnej w każdym urządzeniu sieciowym punkt-punkt w symulacji. Kolejka przesyłania to kolejka, przez którą musi przejść każdy pakiet dla kanału punkt-punkt. Zauważ, że każda linia w pliku śledzenia zaczyna się od pojedynczego znaku (i ma po nim spację). Ten znak będzie miał następujące znaczenie:

+: w kolejce urządzenia miała miejsce operacja wstawienia;
-: w kolejce urządzenia miała miejsce operacja wyciągnięcia elementu;
d: pakiet został odrzucony, gdyż kolejka jest pełna;
r: pakiet został odebrany przez urządzenie sieciowe.

Zerknijmy bardziej szczegółowo na pierwszą linię w pliku śledzenia. Rozłożę ją na części (z wcięciami dla przejrzystości) i z numerem linii po lewej stronie:

0 +
1 2
2 /NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/TxQueue/Enqueue
3 ns3::PppHeader (
4   Protokół Point-to-Point: IP (0x0021))
6   ns3::Ipv4Header (
7     tos 0x0 ttl 64 id 0 protokół 17 offset 0 flaga [brak]
8     długość: 1052 10.1.1.1 > 10.1.1.2)
9     ns3::UdpHeader (
10      długość: 1032 49153 > 9)
11      Ładunek (rozmiar=1024)

Pierwsza sekcja tego zaawansowanego zdarzenia śledzenia (linia 0) to operacja. Mamy tutaj symbol +, co odpowiada operacji wstawienia do kolejki na przesył. Druga sekcja (linia 1) to czas symulacji, wyrażony w sekundach. Możesz przypomnieć sobie, że prosiliśmy UdpEchoClientApplication rozpocznij wysyłanie pakietów za dwie sekundy. Tutaj widzimy potwierdzenie, że rzeczywiście to się dzieje.

W następnym sekcji przykładu śledzenia (od linii 2) pokazuje, jakie źródło śledzenia spowodowało to zdarzenie (zawiera przestrzeń nazw śledzenia). Możesz wyobrazić sobie przestrzeń nazw śledzenia podobnie jak przestrzeń nazw systemu plików. Korzeniem przestrzeni nazw jest NodeList. Odpowiada to kontenerowi zarządzanemu głównie przez kod ns-3. Zawiera on wszystkie węzły, które zostały utworzone w skrypcie. Podobnie jak system plików może mieć w katalogu głównym katalogi, w NodeList możemy mieć wiele węzłów. W ten sposób linia /NodeList/0 odnosi się do węzła zerowego w NodeList, który zwykle rozumiemy jako „węzeł 0”. W każdym węźle znajduje się lista urządzeń, które zostały zainstalowane. Ta lista znajduje się w następnej przestrzeni nazw. Możesz zobaczyć, że to zdarzenie śledzenia pochodzi z DeviceList/0, które jest zerowym urządzeniem zainstalowanym w węźle.

Następny podciąg, $ ns3::PointToPointNetDevice, informuje, jakie urządzenie znajduje się na zerowej pozycji: listy urządzeń zerowego węzła. Przypomnijmy, że operacja +, znajdująca się w linii 0, oznaczała, że w kolejce przesyłania urządzenia dodano element. Odzwierciedla to ostatnie segmenty „ścieżki śledzenia”: TxQueue/Enqueue.

Pozostałe sekcje w śledzeniu powinny być dość intuicyjne. Linie 3-4 wskazują, że pakiet jest enkapsulowany w protokole punkt-punkt. Linie 5-7 pokazują, że pakiet ma nagłówek wersji IP4 i wystąpił w adresie IP 10.1.1.1 i jest przeznaczony dla 10.1.1.2. Linie 8-9 pokazują, że ten pakiet ma nagłówek UDP, a w końcu linia 10 pokazuje, że ładunek użyteczny wynosi oczekiwane 1024 bajty.

Następna linia w pliku śledzenia pokazuje, że ten sam pakiet został wyciągnięty z kolejki przesyłania na tym samym węźle.

Trzecia linia w pliku śledzenia pokazuje, że pakiet został przyjęty przez urządzenie sieciowe na węźle z serwerem echa. Powtórzyłem to zdarzenie poniżej.

0 r
1 2.25732
2 /NodeList/1/DeviceList/0/$ns3::PointToPointNetDevice/MacRx
3   ns3::Ipv4Header (
4     tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
5     length: 1052 10.1.1.1 > 10.1.1.2)
6     ns3::UdpHeader (
7       length: 1032 49153 > 9)
8       Payload (size=1024)

Zwróć uwagę, że operacja śledzenia jest teraz r, a czas symulacji zwiększył się do 2,25732 sekundy. Jeśli dokładnie podążałeś za instrukcjami podręcznika, oznacza to, że pozostawiłeś wartości domyślne dla DataRate urządzeń sieciowych oraz opóźnienia kanału. Ten czas powinien być znany, ponieważ już wcześniej go widziałeś w poprzedniej sekcji.

Zapis przestrzeni nazw źródła śledzenia (linia 2) został zmieniony, aby odzwierciedlić, że to zdarzenie pochodzi z węzła 1 (/NodeList/1) и пакет принят источником трассировки (/MacRx). Powinno być dość łatwo śledzić ruch pakietów przez topologię, przeglądając pozostałe w pliku ślady.

5.3.2 Śledzenie PCAP

Wsparcie dla urządzeń ns-3 może być również używane do tworzenia plików śledzenia w formacie .pcap. Akronim pcap zwykle pisane małymi literami, oznacza przechwytywanie pakietów i zasadniczo jest API, które definiuje format pliku .pcap. Najpopularniejszym programem, który może czytać i wyświetlać ten format, jest Wireshark (wcześniej nazywana Ethereal). Istnieje wiele analizatorów śledzenia ruchu, które korzystają z tego formatu pakietu. Zalecamy użytkownikom korzystanie z wielu dostępnych narzędzi do analizy śladów pcap. W tym przewodniku skupimy się na przeglądaniu śladów pcap za pomocą tcpdump.

Włączenie śledzenia pcap odbywa się za pomocą jednej linii kodu.

pointToPoint.EnablePcapAll ("myfirst");

Wstaw tę linię kodu po kodzie śledzenia ASCII, który właśnie dodaliśmy do scratch/myfirst.cc. Zauważ, że przekazaliśmy tylko ciąg „myfirst”, a nie „myfirst.pcap” ani nic podobnego. Wynika to z tego, że parametr to prefiks, a nie pełna nazwa pliku. Podczas symulacji asystent faktycznie utworzy plik śledzenia dla każdego urządzenia punkt-punkt. Nazwy plików będą budowane przy użyciu prefiksu, numeru węzła, numeru urządzenia i sufiksu „.pcap».

Dla naszego przykładu scenariusza ostatecznie zobaczymy pliki o nazwach „myfirst-0-0.pcap” i „myfirst-1-0.pcap”, które są śladami pcap dla węzła 0-urządzenie 0 oraz węzła 1-urządzenie 0. Po dodaniu linii kodu do włączenia śledzenia pcap możesz uruchomić skrypt w zwykły sposób:

$ ./waf --run scratch/myfirst

Jeśli spojrzysz w katalog główny swojego dystrybucji, powinieneś zobaczyć trzy pliki: plik ASCII śledzenia myfirst.tr, który wcześniej omówiliśmy, pliki myfirst-0-0.pcap i myfirst-1-0.pcap — nowe pliki pcap, które właśnie wygenerowaliśmy.

Odczyt danych za pomocą tcpdump

Obecnie najlepszym sposobem na przeglądanie plików pcap jest użycie tcpdump.

$ tcpdump -nn -tt -r myfirst-0-0.pcap
odczytywanie z pliku myfirst-0-0.pcap, typ połączenia PPP (PPP)
2.000000 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, długość 1024
2.514648 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, długość 1024
tcpdump -nn -tt -r myfirst-1-0.pcap
odczytywanie z pliku myfirst-1-0.pcap, typ połączenia PPP (PPP)
2.257324 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, długość 1024
2.257324 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, długość 1024

W zrzucie myfirst-0-0.pcap (urządzenie klienckie) można zobaczyć, że pakiet echa jest wysyłany po 2 sekundach symulacji. Jeśli spojrzysz na drugi zrzut (myfirst-1-0.pcap), zobaczysz, że pakiet jest odbierany w momencie 2,257324 sekundy. W drugim zrzucie zobaczysz, że pakiet wraca w momencie 2.257324 sekundy, a na koniec, że pakiet został odebrany przez klienta z powrotem w pierwszym zrzucie w momencie 2.514648 sekund.

Odczyt danych za pomocą Wireshark

Jeśli nie jesteś zaznajomiony z Wireshark, istnieje strona internetowa, z której możesz pobrać oprogramowanie i dokumentację: http://www.wireshark.org/. Wireshark — to graficzny interfejs użytkownika, który można wykorzystać do wyświetlania tych plików śledzenia. Jeśli masz Wireshark, możesz otworzyć dowolny z plików śledzenia i zobaczyć zawartość, tak jakbyś przechwycił pakiety za pomocą analizatora pakietów.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster