diff --git a/doc/review_3/chapters/1-einleitung.tex b/doc/review_3/chapters/1-einleitung.tex new file mode 100644 index 0000000..fdb425d --- /dev/null +++ b/doc/review_3/chapters/1-einleitung.tex @@ -0,0 +1,45 @@ +\documentclass[../review_3.tex]{subfiles} +\graphicspath{{\subfix{../img/}}} +\begin{document} + +\chapter{Einleitung}\thispagestyle{fancy} + +\section{Problemstellung} +Denial-of-Service-Angriffe stellen eine ernstzunehmende und stetig wachsende Bedrohung dar. +Im digitalen Zeitalter sind viele Systeme über das Internet oder private Netzwerke miteinander verbunden. Viele Unternehmen, Krankenhäuser und Behörden sind durch unzureichende Schutzmaßnahmen und große Wirkung zu beliebten Angriffszielen geworden\cite{infopoint_security_cyber_angriffe}. Bei solchen Angriffe werden in der Regel finanzielle oder auch politische Gründe verfolgt, selten aber auch die bloße Störung oder Destruktion des Ziels. + +Bei DoS\footnote{Denial of Service, dt.: Verweigerung des Dienstes, Nichtverfügbarkeit des Dienstes}- und DDoS\footnote{Distributed Denial of Service}-Attacken werden Server und Infrastrukturen mit einer Flut sinnloser Anfragen so stark überlastet, dass sie von ihrem normalen Betrieb abgebracht werden. Daraus kann resultieren, dass Nutzer die angebotenen Dienste des Betreibers nicht mehr erreichen und Daten bei dem Angriff verloren gehen können. +Hierbei können schon schwache Rechner große Schaden bei deutlich leistungsfähigeren Empfängern auslösen. In Botnetzen können die Angriffe zusätzlich von mehreren Computern gleichzeitig koordiniert werden, aus verschiedensten Netzwerken stammen \cite{tecchannel_gefahr_botnet} und damit gleichzeitig die Angriffskraft verstärken und die Erkennung erschweren. + +Das Ungleichgewicht zwischen der Einfachheit bei der Erzeugung von Angriffen gegenüber komplexer und ressourcenintensiver DoS-Abwehr verschärft das Problem zusätzlich. Obwohl gelegentlich Erfolge im Kampf gegen DoS-Angriffe erzielt werden (z.B. Stilllegung einiger großer ,,DoS-for-Hire'' Webseiten), vergrößert sich das Datenvolumen der DoS-Angriffe stetig weiter. Allein zwischen 2014 und 2017 hat sich die Frequenz von DoS-Angriffen um den Faktor 2,5 vergrößert und das Angriffsvolumen verdoppelt sich fast jährlich \cite{neustar_ddos_report}. Die Schäden werden weltweit zwischen 20.000 und 40.000 US-Dollar pro Stunde geschätzt \cite{datacenterknowledge_study}. + +Im Bereich kommerzieller DoS-Abwehr haben sich einige Ansätze hervorgetan (z.B. Project Shield\cite{projectshield}, Cloudflare\cite{cloudflare} oder AWS Shield\cite{aws_shield}). Der Einsatz kommerzieller Lösungen birgt jedoch einige Probleme, etwa mitunter erhebliche Kosten oder das Problem des notwendigen Vertrauens, welches dem Betreiber einer DoS-Abwehr entgegengebracht werden muss. Folglich ist eine effiziente Abwehr von DoS-Angriffen mit eigenen Mitteln ein oft gewünschtes Ziel - insbesondere wenn sich dadurch mehrere Systeme zugleich schützen lassen. + +Ziel dieses Softwareprojekts ist es, ein System zwischen der Internet-Anbindung und dem internem Netzwerk zu schaffen, das bei einer hohen Bandbreite und im Dauerbetrieb effektiv (D)DoS Angriffe abwehren kann, während Nutzer weiterhin ohne Einschränkungen auf ihre Dienste zugreifen können. Die entstehende Anwendung implementiert eine (D)DoS-Verkehrs-Inspektion und einen intelligenten Regelgenerator, wodurch interne Netzwerke vor externen Bedrohungen, die zu einer Überlastung des Systems führen würden, geschützt sind. Es enthält Algorithmen zur Verkehrsanalyse, die bösartigen Verkehr erkennen und ausfiltern können, ohne die Benutzererfahrung zu beeinträchtigen und ohne zu Ausfallzeiten zu führen. + +\section{Überblick} + +\begin{figure}[H] + \centering + \includegraphics[width=0.8\linewidth]{img/projektstrukturplan.pdf} + \caption{Projektstrukturplan} + \label{projektstrukturplan} +\end{figure} + +Das Softwareprojekt wurde vom zuständigen Fachgebiet in drei Teile aufgeteilt. Die Planungs- und Entwicklungsphase, die Implementierungsphase und die Validierungsphase dauern jeweils einen Monat und werden durch ein Review abgeschlossen. Zu diesen Reviews werden die Ergebnisse der vergangenen Phase vorgestellt und die erforderlichen Review-Dokumente abgegeben. + +Zu Beginn des Projekts wurde sich auf den Unified Process als Vorgehensmodell geeinigt, damit sowohl ein gewisser Grad an Flexibilität als auch die Planbarkeit der Ergebnisse gewährleistet werden kann. Prinzipiell besteht dieses Vorgehensmodell aus vier Phasen, von denen die Konzeption und die Ausarbeitung beide in der Planungs- und Entwurfsphase lagen. Die Konstruktionsphase und die Inbetriebnahme decken sich zeitlich mit der Implementierungs- und der Validierungsphase. + +Dieses dritte Review-Dokument bezieht sich auf die Validierungssphase. Das heißt, dass es auf den Ergebnissen der vorhergehenden Phase und dem zweiten Review-Dokument vom 23. Juni 2021 aufbaut. + +Das erste Review-Dokument enthält die gängigen Inhalte eines Pflichtenhefts wie die funktionalen und nicht-funktionalen Anforderungen, eine Aufwands- und Risikoanalyse und Überlegungen zum Vorgehen und der internen Organisation. Außerdem umfasste es eine Entwurfsdokumentation für den Grobentwurf, die Anforderungsanalyse, ein Kapitel zu den Technologien und Entwicklungswerkzeugen, Ergebnisse zu den Machbarkeitsanalysen und Beispielrealisierungen und ein Testdrehbuch. + +Im zweiten Review-Dokument wurden im Kapitel zum Grobentwurf zusätzlich zur erneuten Erläuterung der grundlegenden Architektur die für den Unified Process üblichen Überarbeitungen des Grobentwurfs dargestellt und begründet. Dabei wurde, genauso wie beim darauffolgenden Feinentwurf, Paket für Paket vorgegangen. Schließlich wurden in einem Bug-Review die offenen Anforderungen und Fehler beschrieben und die mittels des Tools Kimai erfassten Arbeitszeiten ausgewertet. + +In diesem dritten Review-Dokument kommt nun ein ausführliches Kapitel zu sämtlichen Tests dazu. Außerdem behandelt es verschiedene Softwaremetriken und Statistiken, wie Konventionen und den Umfang der Software. Das Kapitel zur Auswertung der erfassten Arbeitszeiten enthält nun auch diese letzte Phase des Softwareprojekts. Am Ende des Dokuments kommt es zur umfangreichen Auswertung des Projekts. + +Es bleibt also anzumerken, dass einige Teile dieses Dokuments dem ersten und zweiten Review-Dokument entnommen sind, weil dies vom Fachgebiet empfohlen wurde und dadurch die Veränderungen besonders gut dargestellt werden können. + +Die Erstellung dieses Review-Dokuments stellt allerdings nur einen Teil der in dieser Phase erledigten Aufgaben dar. Hauptsächlich ging es um das Testen, aber auch um weitere Themen, was sich im Projektstrukturplan in Abb. \ref{projektstrukturplan} gut erkennen lässt. + +\end{document} diff --git a/doc/review_3/chapters/10-abkuerzungsverzeichnis.tex b/doc/review_3/chapters/10-abkuerzungsverzeichnis.tex new file mode 100644 index 0000000..54208c0 --- /dev/null +++ b/doc/review_3/chapters/10-abkuerzungsverzeichnis.tex @@ -0,0 +1,17 @@ +\documentclass[../review_3.tex]{subfiles} +\graphicspath{{\subfix{../img/}}} +\begin{document} + +\chapter{Abkürzungsverzeichnis}\thispagestyle{fancy} +\begin{description} + \item[DDoS] Distributed Denial-of-Service + \item[DoS] Denial-of-Service + \item[DRoS] Distributed Reflected Denial-of-Service + \item[Gbps] Giga bit pro sekunde + \item[KW] Kalenderwoche + \item[LoC] Lines of Code + \item[Mpps] Million packets per second + \item[UML] Unified Modelling Language +\end{description} + +\end{document} diff --git a/doc/review_3/chapters/11-glossar.tex b/doc/review_3/chapters/11-glossar.tex new file mode 100644 index 0000000..b1545a1 --- /dev/null +++ b/doc/review_3/chapters/11-glossar.tex @@ -0,0 +1,22 @@ +\documentclass[../review_3.tex]{subfiles} +\graphicspath{{\subfix{../img/}}} +\begin{document} + +\chapter{Glossar}\thispagestyle{fancy} + +\begin{description} + %Botnets + %Bottleneck-Problem + \item \textbf{Middlebox} auch Mitigation Box. Ein System zwischen zwei Systemen das in die Kommunikation der beiden Systeme eingreift. + % Network Address Translation + \item \textbf{NIC} engl. Network Interface Card; dt. Netzwerkkarte zum Empfang und Senden von Paketen über ein kabelgebundenes oder kabelloses Netzwerk + \item \textbf{Non-Worker-Thread} Ein einzelner Thread neben den Worker-Threads der eine spezielle und einzigartige Aufgabe im System übernimmt. + \item \textbf{Polling} das Holen von Paketen von der Netzwerkkarte in den Kernel oder Userspace + \item \textbf{RX/TX Queues} Empfangs (engl. Receive) und Sende (engl. Transceive) Warteschlangen (engl. Queues) für Netzwerkkarten innerhalb eines Systems + \item \textbf{Thread} leichtgewichtiger Prozess, auch Ausführungsstrang für die Abarbeitung eines Programms + %\item \textbf{Vtable} + \item \textbf{Worker-Thread} Einer von mehreren parallelen Threads, die unabhängig voneinander die Pipeline bearbeiten. + +\end{description} + +\end{document} diff --git a/doc/review_3/chapters/2-grobentwurf.pdf b/doc/review_3/chapters/2-grobentwurf.pdf new file mode 100644 index 0000000..685bea2 Binary files /dev/null and b/doc/review_3/chapters/2-grobentwurf.pdf differ diff --git a/doc/review_3/chapters/2-grobentwurf.tex b/doc/review_3/chapters/2-grobentwurf.tex new file mode 100644 index 0000000..b93d0c7 --- /dev/null +++ b/doc/review_3/chapters/2-grobentwurf.tex @@ -0,0 +1,246 @@ +\documentclass[../review_3.tex]{subfiles} +\graphicspath{{\subfix{../img/}}} +\begin{document} + +\chapter{Grobentwurf}\thispagestyle{fancy} +Dieses Kapitel behandelt zunächst den Grobentwurf, wie er in der Planungs- und Entwurfsphase des Projekts erarbeitet wurde. Schließlich wird auf dessen Überarbeitung und dazugehörige Diagramme eingegangen. + +\section{Grundlegende Architektur} +In folgendem Unterkapitel werden die grundlegenden Entscheidungen des Entwurfs erklärt und durch die Rahmenbedingungen begründet. Ein intuitiver Einstieg soll schrittweise an das System heranführen über Erklärung des Netzwerkaufbaus, dem grundlegenden Aufbau der Software, dem Kontrollfluss eines Pakets und verwendeter Verfahren. + +\subsection{Netzwerkaufbau} +\begin{figure}[h] + \centering + \includegraphics[width=1.0\linewidth]{img/Netzwerkplan-Real} + \caption{Realaufbau unter Verwendung eines Angreifers} + \label{fig:netzwerkplan-real} +\end{figure} +Die Abbildung \ref{fig:netzwerkplan-real} zeigen den typischen, zu erwartenden Netzwerkaufbau, welcher in dieser Form im Internet und in der Produktivumgebung vorkommt. Das System untergliedert sich grob in drei Teile. Links in der Abbildung ist jeweils das Internet zu erkennen. In diesem sind unterschiedliche Netzwerke mit jeweils verschiedenen Computern miteinander verbunden. Unter den vielen Computern im Internet, welche für Serversysteme teilweise harmlos sind, befinden sich allerdings auch einige Angreifer. Hier ist ganz klar eine Unterscheidung zwischen dem Angriff eines einzelnen Angreifers, oder einer Menge von einem Angreifer gekaperten und gesteuerten Computer, also eines Botnets, vorzunehmen. + +Wird das Internet, hin zum zu schützenden Netzwerk, verlassen, so wird zuerst ein Router vorgefunden, welcher Aufgaben wie die Network Address Translation vornimmt. Hinter diesem Router befände sich im Produktiveinsatz nun das zu entwickelnde System. Router und zu entwickelndes System sind ebenfalls über eine Verbindung mit ausreichend, in diesem Fall 25Gbit/s, Bandbreite verbunden. Das System selbst agiert als Mittelsmann zwischen Router, also im Allgemeinen dem Internet, und dem internen Netz. Um mehrere Systeme gleichzeitig schützen zu können, aber dennoch die Kosten gering zu halten, ist dem zu entwickelnden System ein Switch nachgeschaltet, mit welchem wiederum alle Endsysteme verbunden sind. + +Leider ist durch Begrenzungen im Budget, der Ausstattung der Universität sowie der Unmöglichkeit das Internet in seiner Gesamtheit nachzustellen ein exakter Nachbau des Systems für dieses Projekt nicht möglich. Deswegen musste ein alternativer Aufbau gefunden werden, der allerdings vergleichbare Charakteristika aufweisen muss. + +\begin{figure}[h] + \centering + \includegraphics[width=0.7\linewidth]{img/Netwerkplan-Versuch} + \caption{Versuchsaufbau} + \label{fig:Versuchsaufbau} +\end{figure} + +Der für das Projekt verwendete Versuchsaufbau untergliedert sich ebenfalls in drei Teile. Auch hier beginnt die Darstellung \ref{fig:Versuchsaufbau} ganz links mit dem System, welches Angreifer und legitimen Nutzer in sich vereint. Um die Funktionalität von Angreifer und Nutzer gleichzeitig bereitstellen zu können, setzt der Projektstab, in diesem Fall auf das Installieren zweier Netzwerkkarten, in einem Computer. Eine 10 Gbit/s Netzwerkkarte ist mit der Aufgabe betraut, legitimen Verkehr zu erzeugen. Da aufgrund der Hardwarerestriktionen keine direkte Verbindung zur Middlebox aufgebaut werden kann, wird der ausgehende Verkehr dieser Netzwerkkarte in einen Eingang einer zweiten, in demselben System verbauten Netzwerkkarte mit einer maximalen Datenrate von 25 Gbit/s eingeführt. Von dieser führt ein 25 Gbit/s Link direkt zur Middlebox. Intern wird nun im System, das sich in der Abbildung \ref{fig:Versuchsaufbau} auf der rechten Seite befindet, sowohl legitimer Verkehr erzeugt als auch Angriffsverkehr kreiert, wobei diese beiden Paketströme intern zusammengeführt werden, und über den einzigen Link an die Middlebox gemeinsam übertragen werden. Die Middlebox selbst ist nicht nur mit dem externen Netz verbunden, sondern hat über die selbe Netzwerkkarte auch noch eine Verbindung ins interne Netz. Das gesamte interne Netz wird im Versuchsaufbau durch einen einzelnen, mit nur 10 Gbit/s angebundenen Computer realisiert. + +Die Entscheidung zur Realisierung in dieser Art fiel, da insbesondere der Fokus darauf liegen soll, ein System zu erschaffen, welches in der Lage ist, mit bis zu 25 Gbit/s an Angriffsverkehr und legitimen eingehenden Verkehr zurechtzukommen. Aus diesem Grund ist es ausreichend, eine Verbindung zum internen Netz mit nur 10 Gbit/s aufzubauen, da dieses System bei erfolgreicher Abwehr und Abschwächung der Angriffe mit eben diesen maximalen 10 Gbit/s an legitimen Verkehr zurecht kommen muss. Ursächlich für die Verwendung der 10 Gbit/s Netzwerkkarte im externen Rechner, welcher hierüber den legitimen Verkehr bereitstellen soll, ist, dass der Fokus bei einem solchen Schutzmechanismus natürlich darauf beruht, die Datenrate des Angreifers zu maximieren, um das zu entwickelnde System in ausreichendem Maße belasten und somit Stresstests unterwerfen zu können. + +\subsection{Grundlegender Aufbau der Software} \label{section:basic_structure} +Das Grundprinzip der zu entwickelten Software soll sein, Pakete auf einem Port der Netzwerkkarte zu empfangen und diese zu einem anderen Port weiterzuleiten. Zwischen diesen beiden Schritten werden die Pakete untersucht, Daten aus diesen extrahiert und ausgewertet. Im weiteren Verlauf des Programms werden Pakete, welche einem Angriff zugeordnet werden, verworfen, und legitime Pakete zwischen dem internen und externen Netz ausgetauscht. Es bietet sich an, hier ein Pipelinemodell zu verwenden, wobei die einzelnen Softwarekomponenten in Pakete aufgeteilt werden. Im \texttt{ConfigurationManagement} werden die initialen Konfigurationen vorgenommen. Das \texttt{NicManagement} ist eine Abstraktion der Netzwerkkarte und sorgt für das Empfangen und Senden von Paketen. Die \texttt{PacketDissection} extrahiert Daten von eingehenden Paketen. Die \texttt{Inspection} analysiert diese Daten und bestimmt, welche Pakete verworfen werden sollen. Das \texttt{Treatment} behandelt die Pakete nach entsprechenden Protokollen. Um die Abarbeitung dieser Pipeline möglichst effizient zu gestalten, soll diese jeweils von mehreren Threads parallel und möglichst unabhängig voneinander durchschritten werden. + +In den folgenden Sektionen wird auf den Kontrollfluss innerhalb des Programms, auf den Einsatz von parallelen Threads und auf die einzelnen Komponenten näher eingegangen. + +\subsubsection{Einsatz von parallelen Threads} +Zunächst ist jedoch ein wichtiger Aspekt der Architektur hervorzuheben. Von der Mitigation-Box wird gefordert, eine hohe Paket- und Datenlast verarbeiten zu können. Das Hardwaresystem, auf welchem das zu entwickelnde Programm laufen wird, besitzt eine Multicore-CPU, d.h. das System ist in der Lage, Aufgaben aus unterschiedlichen Threads parallel zu bearbeiten. Dies hat das Potenzial, die Rechengeschwindigkeit zu vervielfachen und so die Bearbeitungszeit insgesamt zu verringern. + +Dabei stellt sich die Frage, wozu die Threads im Programm genau zuständig sind. Es wäre zum Beispiel möglich, dass jeder Thread eine Aufgabe übernimmt, d.h. es gäbe einen Thread, der nur Daten analysiert oder aber einen Thread, der nur Paketinformationen extrahiert. Eine solche Aufteilung würde allerdings zu einem hohen Grad an Inter-Thread-Kommunikation führen. Diese ist nicht trivial und kann einen Großteil der verfügbaren Ressourcen benötigen, was den durch die Parallelisierung erzielten Gewinn wieder zunichte machen könnte. Um dieses Risiko zu vermeiden, soll stattdessen jeder Thread die gesamte Pipeline durchlaufen. So ist kaum Inter-Thread-Kommunikation notwendig. Außerdem ist es dann verhältnismäßig einfach, den Entwurf skalierbar zu gestalten: Wenn ein Prozessor mit größerer Anzahl an Kernen verwendet werden würde, könnten mehr Pakete parallel bearbeitet werden, ohne dass die Architektur geändert werden muss. + +\subsubsection{Kontrollfluss eines Paketes} +In diesem Abschnitt soll veranschaulicht werden, wie genau die Behandlung eines Paketes vom \texttt{NicManagement} bis zum \texttt{Treatment} erfolgt. Dabei werden die Pakete selbst als Akteure angesehen und nicht deren Klassen. Hinweis: Ein Thread soll später mehrere Pakete auf einmal durch die Pipeline führen. In diesem Diagramm wird zur Übersichtlichkeit jedoch nur der Fluss eines Paketes gezeigt. Dieser lässt sich dann einfach auf eine größere Menge von Paketen anwenden. Ein Aktivitätsdiagramm ist unter Abbildung \ref{fig:control_flow} am Ende der Sektion \ref{section:basic_structure} zu finden. + +\begin{figure}[h] + \centering + \includegraphics[angle=90, width=0.8\linewidth]{img/activity_control_flow.pdf} + \caption{Schematische Darstellung des Kontrollflusses} + \label{fig:control_flow} +\end{figure} + +\subsubsection{Verwendung von Receive-Side-Scaling} +Ein weiterer grundlegender Vorteil ergibt sich durch das von der Netzwerkkarte und von DPDK unterstützte Receive Side Scaling (RSS), siehe Abbildung \ref{fig:Receive-Side-Scaling}: Ein auf einem Port eingehendes Paket wird einer von mehreren sogenannten RX-Queues zugeordnet. Eine RX-Queue gehört immer zu genau einem Netzwerkkartenport, ein Port kann mehrere RX-Queues besitzen. Kommen mehrere Pakete bei der Netzwerkkarte an, so ist die Zuordnung von Paketen eines Ports zu seinen RX-Queues gleich verteilt~-- alle RX-Queues sind gleich stark ausgelastet. Diese Zuordnung wird durch eine Hashfunktion umgesetzt, in die Quell- und Ziel-Port-Nummer und IP-Adresse einfließen. Das führt dazu, dass Pakete, die auf einem Port ankommen und einer bestimmten Verbindung zugehören, immer wieder zu der selben RX-Queue dieses Ports zugeordnet werden. Mit ,,Port'' im Folgenden entweder der physische Steckplatz einer Netzwerkkarte gemeint oder jener Teil der Netzwerkadresse, die eine Zuordnung zu einem bestimmten Prozess bewirkt. Die Bedeutung erschließt sich aus dem Kontext. + +\begin{figure}[H] + \centering + \includegraphics[width=0.95\linewidth]{img/Receive-Side-Scaling.png} + \caption{Beispielhafte Paketverarbeitung mit Receive Side Scaling} + \label{fig:Receive-Side-Scaling} +\end{figure} + +Ferner besteht die Möglichkeit, Symmetric RSS einzusetzen. Dieser Mechanismus sorgt dafür, dass die Pakete, die auf dem einen Port der Netzwerkkarte ankommen, nach genau der selben Zuordnung auf dessen RX-Queues aufgeteilt werden, wie die auf dem anderen Port ankommenden Pakete auf dessen RX-Queues. Dabei ist die Zuordnung auf dem einen Port ,,symmetrisch'' zu der auf dem anderen Port. Das heißt, wenn bei Port 0 ein Paket mit \texttt{Src-IP: a, Dst-IP: b, Src-Port: x, Dst-Port: y} ankommt, wird es genauso dessen RX-Queues zugeteilt, wie ein Paket mit \texttt{Src-IP: b, Dst-IP: a, Src-Port: y, Dst-Port: x} auf RX-Queues von Port 1. So ergeben sich Paare von RX-Queues, die jeweils immer Pakete von den gleichen Verbindungen beinhalten. Angenommen, die RX-Queues sind mit natürlichen Zahlen benannt und RX-Queue 3 auf Port 0 und RX-Queue 5 auf Port 1 sind ein korrespondierendes RX-Queue-Paar. Wenn nun ein Paket P, zugehörig einer Verbindung V auf RX-Queue 3, Port 0 ankommt, dann weiß man, dass Pakete, die auf Port 1 ankommen und der Verbindung V angehören immer auf RX-Queue 5, Port 1 landen. + +Neben RX-Queues existieren auch TX-Queues (Transmit-Queues), die ebenfalls zu einem bestimmten Port gehören. Darin befindliche Pakete werden von der Netzwerkkarte auf den entsprechenden Port geleitet und gesendet. Auf Basis dieses Mechanismus sollen die Threads wie folgt organisiert werden: Einem Thread gehört ein Paar von korrespondierenden RX-Queues (auf verschiedenen Ports) und daneben eine TX-Queue auf dem einen und eine TX-Queue auf dem anderen Port. Das bringt einige Vorteile mit sich: Es müssen zwei Arten von Informationen entlang der Pipeline gespeichert, verarbeitet und gelesen werden: Informationen zu einer Verbindung und Analyseinformationen/Statistiken. Daher ist kaum Inter-Thread-Kommunikation nötig, weil alle Informationen zu einer Verbindung in Datenstrukturen gespeichert werden können, auf die nur genau der bearbeitende Thread Zugriff haben muss. An dieser Stelle soll auch kurz auf eine Besonderheit von DPDK eingegangen werden: Im Linux-Kernel empfängt ein Programm Pakete durch Interrupt-Handling. Gegensätzlich dazu werden bei DPDK alle empfangenen Pakete, die sich derzeit in den RX-Queues der Netzwerkkarte befinden, auf einmal von der Anwendung geholt. In der zu entwickelnden Software geschieht dieses Paket-holen (engl. ,,Polling'') durch den einzelnen Thread stets zu Beginn eines Pipeline-Durchlaufes. + +Im Falle eines Angriffes ist die Seite des Angreifers (entsprechender Port z.B. ,,Port 0'') viel stärker belastet, als die Seite des Servers (z.B. ,,Port 1''). Wegen der gleich verteilten Zuordnung des eingehenden Verkehrs auf die RX-Queues und weil ein Thread von RX-Queues von beiden Ports regelmäßig Pakete pollt, sind alle Threads gleichmäßig ausgelastet und können die Pakete bearbeiten. Ein günstiger Nebeneffekt bei DDoS-Angriffen ist, dass die Absenderadressen von Angriffspaketen oft sehr unterschiedlich sind. Das begünstigt die gleichmäßige Verteilung von Paketen auf RX-Queues, weil das Tupel aus besagten Adressen der Schlüssel der RSS-Hash-Funktion sind. + +\section{Überarbeiteter Grobentwurf} +Die in diesem Abschnitt erläuterten Änderungen wurden im Laufe der Implementierungsphase vorgenommen. Für das bei diesem Softwareprojekt genutzte Vorgehensmodell des Unified Process ist es typisch, dass sich auch während der Implementierung Änderungen am Entwurf ergeben. Für die Teammitglieder ist es besonders aufgrund der geringen Erfahrung bezüglich der Thematik des Projekts unerlässlich, wichtige Verbesserungen direkt vornehmen zu können. + +\subsection{Paketdiagramm} + +\begin{figure}[H] + \centering + \includegraphics[width=0.82\linewidth]{img/packet_diagram.pdf} + \caption{Paketdiagramm} + \label{fig:dospaketdiagramm} +\end{figure} +Grundsätzlich ist es angedacht, wie im Paketdiagramm \ref{fig:dospaketdiagramm} ersichtlich, die zu entwickelnde Software in insgesamt 5 Teile zu untergliedern. + +Das \texttt{NicManagement} wird eingesetzt, um die Kommunikation und Verwaltung der Netzwerkkarten und Ports zu ermöglichen, hier finden Operationen wie der Versand und Empfang von Paketen statt. Verwendet wird das \texttt{NicManagement} von der \texttt{PacketDissection}. Diese Komponente beinhaltet Klassen zur Paketrepräsentation für das \texttt{Treatment} und die \texttt{Inspection}. Sie liefert Operationen zum Löschen, Senden, Empfangen und Bearbeiten von Paketen. In der \texttt{PacketDissection} werden auch Informationen aus den einzelnen Headern eines Netzwerkpakets extrahiert. + +Die extrahierten Informationen werden von der \texttt{Inspection} verwendet, um sowohl Angriffe erkennen zu können, als auch über den allgemeinen Zustand des Systems in Form von Statistiken Auskunft zu geben. Das \texttt{Treatment}, welches für die Abwehrwehrmaßnahmen der verschiedenen Angriffe zuständig ist, verwendet hierzu die von der \texttt{Inspection} bereitgestellten Ergebnisse und Informationen. Für das Versenden und Verwerfen von Pakten, sowie den Aufbau und das Terminieren von Verbindungen, verwendet das \texttt{Treatment} die \texttt{PacketDissection}, welche die Anweisung an das \texttt{NicManagement} weitergibt. + +Sowohl \texttt{Treatment}, als auch \texttt{Inspection} und \texttt{PacketDissection} verwenden das \\ \texttt{ConfigurationManagement}, welches Parameter für die Programmbestandteile in Form von Konfigurationsdateien vorhält. Das \texttt{ConfigurationManagement} bietet die Möglichkeit für den Nutzer, aktiv Einstellungen am System vorzunehmen. + +\subsection{NicManagement} +Das \texttt{NicManagement} übernimmt, wie im letzten Review-Dokument erwähnt, das Senden, das Pollen und das Löschen von Paketen. Dieses Paket wurde eingeführt, um bestimmte Funktionen und Initialisierungsschritte vom DPDK zu kapseln. Dabei handelt es sich vor allem um folgende Operationen: \texttt{rte\_eth\_rx\_burst()} und \texttt{rte\_eth\_tx\_burst()} Es hat sich allerdings herausgestellt, dass die Operationen ,,Senden'', ,,Empfangen'' und ,,Löschen'' in der Implementierung sehr wenig Aufwand bereiten. Das Zusammenbauen von Paketen wird von der Komponente \texttt{PacketDissection} übernommen. Der aufwändigere Teil ist die Initialisierung des DPDK, insbesondere die Ermöglichung von Multithreading und die Konfigurierung von symmetric Receive-Side-Scaling. Die dazu notwendigen Schritte werden jedoch von \texttt{Initializer} bzw in der main.cpp-Datei vor dem Starten der einzelnen Threads durchgeführt und sind nicht mehr Teil des \texttt{NicManagements}. + +Aus diesem Grund und weil jeder nicht notwendige Funktionsaufruf Rechenzeit kostet, könnte das \texttt{NicManagement} aufgelöst und die bereitgestellten Funktionen an anderer Stelle implementiert werden. Die einzige Klasse, die das \texttt{NicManagement} zum jetzigen Zeitpunkt verwendet, ist die \texttt{PacketContainer}-Klasse in der Komponente \texttt{PacketDissection}. Es wäre möglich, den Inhalt der \texttt{NicManagement}-Aufgaben in diese Klasse zu verschieben. + +\begin{figure}[h] + \centering + \includegraphics[width=\linewidth]{img/NetworkPacketHandler.pdf} + \caption{Klassendiagramm: \texttt{NetworkPacketHandler}} + \label{nph} +\end{figure} + +Um ein neues Objekt der Klasse \texttt{NetworkPacketHandler} zu erzeugen, muss der Konstruktor aufgerufen werden. Diesem müssen zwei Parameter übergeben werden (vgl. Abb. \ref{nph}): Zum einen die ID der RX-Queues (\texttt{rx\_queue\_number}), zum anderen die der TX-Queues(\texttt{tx\_queue\_number}). + +Für das Verwerfen von Paketen ist die Methode \texttt{drop\_packet()} zuständig. Hierbei muss ein Pointer auf das Paket übergeben werden, das verworfen werden soll. + +Die Methode \texttt{poll\_packets\_from\_port()} holt Pakete von einem spezifischen Port. Dazu werden die ID des Ports, von denen die Pakete geholt werden sollen, ein Array, auf welches die Pointer der geholten mbufs geschrieben werden sollen und die Anzahl der Pakete, die in das Array geholt werden sollen, benötigt. +Ähnliche Parameter werden der Methode \texttt{send\_packets\_to\_port()} übergeben. + +\textbf{Folgende Änderungen ergaben sich während der Validierungsphase}: + +Es wurde festgestellt, dass, wie oben beschrieben, das Paket \textbf{NicManagement} nicht weiter benötigt wird. Es exisitiert zum jetzigen Zeitpunkt nicht mehr. + +\subsection{ConfigurationManagement} +Das Paket \texttt{ConfigurationManagement} kümmert sich um die Initialisierung der Software. Des weiteren werden hier die ablaufenden Threads konfiguriert und verwaltet. Grundlegend ist das Paket in drei Klassen eingeteilt: \texttt{Configurator}, \texttt{Initializer} und \texttt{Thread}. + +\begin{figure}[h] + \centering + \includegraphics[width=0.4\linewidth]{img/configurator.pdf} + \caption{Klassendiagramm: \texttt{Configurator}} + \label{config} +\end{figure} +Die Klasse \texttt{Configurator} bietet eine Schnittstelle zu der Konfigurationsdatei, welche im Projekt liegt und die grundlegenden Einstellungen der Software enthält. An anderer Stelle kann dann über verschiedene Methoden auf Konfigurationsinformationen zugegriffen werden. Abbildung \ref{config} zeigt das Klassendiagramm vom \texttt{Configurator}. + +\begin{figure}[h] + \centering + \includegraphics[width=0.7\linewidth]{img/Initializer.pdf} + \caption{Klassendiagramm: \texttt{Initializer}} + \label{init} +\end{figure} +Die Klasse \texttt{Initializer} dient dazu die für die Bibliothek DPDK notwendigen Voraussetzungen zu schaffen. Das Klassendiagramm befindet sich in Abb. \ref{init}. + +\begin{figure}[h] + \centering + \includegraphics[width=0.85\linewidth]{img/Thread.pdf} + \caption{Klassendiagramm: \texttt{Thread}} + \label{thread} +\end{figure} +Die Klasse \texttt{Thread} enthält den Ablaufplan für die Worker-Threads des Systems (vgl. Abb. \ref{thread}). + +\subsection{PacketDissection} +Der Zweck dieses Pakets ist, sämtliche Daten, die \texttt{Inspection} und \texttt{Treatment} für ihre Arbeit brauchen, aus den Paketen zu extrahieren. + +Dafür war geplant, in dieser Komponente die Repräsentation eines Paketes - die Klasse \texttt{PacketInfo} - unterzubringen. Jedes Paket sollte einzeln repräsentiert durch die Pipeline des Programms gereicht werden. Es hat sich herausgestellt, dass dieses Vorgehen ineffizient ist. Näheres dazu ist im Feinentwurfskapitel beschrieben. + +Aus diesem Grund wurde eine neue Klasse namens \texttt{PacketContainer} eingeführt. Diese dient als Repräsentation einer Folge von Paketen, die empfangen wurden. Enthalten sind sowohl die Pointer auf die tatsächlichen Pakete, als auch Metadaten in Form mehrerer Objekte der \texttt{PacketInfo}-Klasse. Im \texttt{PacketContainer} ist es möglich, Pakete zu entnehmen, hinzuzufügen und zu löschen. Weiterhin gibt es jeweils eine Methode zum Pollen neuer Pakete und zum Senden aller vorhandener Pakete. + +Die \texttt{PaketInfo}-Klasse stellt immer noch alle relevanten Header-Informationen eines Paketes zur Verfügung. Allerdings werden Informationen nur noch auf Abruf extrahiert. Hierbei werden für die IP Versionen 4 und 6, sowie die Layer 4 Protokolle TCP, UDP und ICMP unterstützt. Darüber hinaus soll sie auch das verändern einzelner Informationen im Header ermöglichen. + +Die letzte Klasse in der \texttt{PacketDissection} ist der namensgebende \texttt{HeaderExtractor}. Seine Aufgabe wandelte sich vom Extrahieren der Informationen zum Vorbereiten des Extrahierens auf Bedarf. + +\subsection{Inspection} +Die zuvor globale Auswertung von Angriffen aller Threads durch eine einzige Instanz wurde ersetzt durch eine lokale threadeigene Auswertung. Berechnete Zahlen und Statistiken wie Paketrate und Angriffsrate werden per Interthreadkommunikation nur noch an eine globale Statistikinstanz gesendet. Dadurch können die Threads unabhängig voneinander agieren und reagieren, die Implementation der Methoden ist deutlich einfacher ausgefallen und die Interthreadkommunikation konnte auf ein Minimum begrenzt werden, was der Auswertungsgeschwindigkeit jedes Inspection-Threads zugute kommt und ein Bottleneck-Problem an der Inspection vorbeugt. + +\begin{figure}[h] + \centering + \includegraphics[width=\linewidth]{img/Inspection_old.png} + \caption{Altes Klassendiagramm der Inspection aus der Implementierungsphase} + \label{InspectionOld} +\end{figure} + +Durch den Einsatz von symetric Receive Side Scaling ist sowohl die Auslastung jeder Inspektion ausgeglichen und zusätzlich werden gleiche Paketströme (selbe Paketquelle und -Empfänger) durch denselben Thread verarbeitet. Dies erleichtert die Erkennung legitimer Pakete, da diese über eine eigene Patchmap für bestimmte Fälle von großteils illegitimen Verkehr unterscheidbar ist und die Variationen geringer sind. + +Die Statistik wird statt durch eine eigene Klasse direkt in der \texttt{Inspection} erstellt und das Ergebnis an eine globale Statistik Instanz gesendet, um diese an den Nutzer auszugeben. Die \texttt{Inspection}-Klasse ist dadurch schlanker und folgt einem linearen Pipelinemodell für Paketverarbeitung. + +\begin{figure}[h] + \centering + \includegraphics[width=.3\linewidth]{img/Inspection.pdf} + \caption{Aktuelles Klassendiagramm der \texttt{Inspection} aus der Planungs- und Entwurfsphase} + \label{InspectionNew} +\end{figure} + +In Abb. \ref{InspectionOld} und Abb. \ref{InspectionNew} lässt sich gut erkennen, wie sich die \texttt{Inspection} während der Überarbeitung verändert und vereinfacht hat. + +\subsection{Treatment} + +\begin{figure}[h] + \centering + \includegraphics[width=\linewidth]{img/classdia_treatment.pdf} + \caption{Klassendiagramm des Treatments aus der Implementierungsphase} + \label{Treatmentclassdia} +\end{figure} + +\begin{figure}[h] + \centering + \includegraphics[width=0.7\linewidth]{img/package_Treatment.png} + \caption{Altes Paket Treatments mit verschiedenen Klassen aus der Planungs- und Entwurfsphase} + \label{Treatmentclassdia2} +\end{figure} + +Abbildung \ref{Treatmentclassdia} zeigt das während der Implementierungsphase überarbeitete Klassendiagramm. Auf den ersten Blick unterscheidet sich dieses stark vom Grobentwurf des \texttt{Treatments} aus der Planungs- und Entwurfsphase (vgl. Abb. \ref{Treatmentclassdia2}). + +Das \texttt{Treatment} hat fortan die Aufgabe, die Implementierung von TCP-SYN-Cookies sowie die Realisierung eines TCP-Proxies zu übernehmen. Zur Realisierung des TCP-Proxies gehört insbesondere die Sequenznummernanpassung zwischen internen und externen Verbindungen. + +Es fällt auf, dass keine Vererbung mehr verwendet wird. Das heißt, dass nicht mehr zwischen \texttt{TcpTreatment} und \texttt{UdpTreatment} unterschieden wird. Der Grund hierfür ist die Auslagerung des UDP-Treatments in den \texttt{Inspection} (Paket \texttt{Inspection}). Es wird allerdings nicht nur das UDP-Treatment ausgelagert, sondern auch die Behandlung der SYN-FIN-Attacke sowie des TCP-Small- und Zero-Window-Angriffs. Dies ist darin begründet, dass bereits in der \texttt{Inspection} alle hierzu benötigten Informationen und Funktionalitäten bereitstehen. Dies führt letztlich dazu, dass Funktionsaufrufe oder function calls reduziert werden, welches es dem Programm ermöglicht, insgesamt eine bessere Performanz aufzuweisen. +Durch den Wegfall der Klasse \texttt{UdpTreatment} entfällt die Notwendigkeit der Vererbung und die gesamte Implementierung des Treatments kann in einer einzigen Klasse erfolgen. + +Das ursprüngliche Attribut \texttt{tcp\_connection\_table} wurde umbenannt in \texttt{\_densemap}. Der Unterstrich vor dem Variablennamen zeigt, dass es sich um eine Member-Variable handelt. Durch die Umbenennung wird deutlich, dass es sich um eine Google-Densemap handelt und nicht um eine beliebige Map. Die Densemap untergliedert sich in drei Teile: \texttt{Data} ist der Key (vgl. Abb. \ref{data}), die Info ist die Nutzlast (vgl. Abb. \ref{info}) und der Hashwert aus \texttt{Data} ergibt die Position in der Map (vgl. Abb. \ref{MyHashFunction}). +Hinzu kommt zusätzlich die \texttt{\_ackmap}, bei der es sich ebenfalls um eine Densemap handelt. Die ACK-Map hat zur Aufgabe, diejenigen Pakete zwischenzuspeichern, welche im letzten ACK des externen Verbindungsaufbaus am System ankommen und nach erfolgreichem Verbindungsaufbau mit dem internen System an ebendieses weitergeleitet werden müssen. +Der Wegfall von \texttt{load\_rate\_limit} und \texttt{timeout\_value} ist ähnlich wie beim \texttt{UdpTreatment} durch die Auslagerung in der \texttt{Inspection} zu begründen. +Die Variable \texttt{\_timestamp}, die in der Implementierungsphase hinzugekommen ist, wird benötigt, um das Alter der ACKs, welche als Reaktion auf ein SYN-ACK erhalten werden, zu bestimmen. +Das \texttt{\_cookie\_secret} wird im SYN-Cookie verwendet, um es einem potentiellen Angreifer schwieriger zu machen, eine illegitime Verbindung zum System aufzubauen, indem den Cookies ein weiterer schwieriger zu erratender Wert hinzugefügt wird. +Bei den Variablen \texttt{\_packet\_to\_inside} und \texttt{\_packet\_to\_outside} handelt es sich um Pointer zu \texttt{PacketContainern}. Diese speichern die dem \texttt{Treatment} im Konstruktor übergebenen \texttt{PacketContainer}-Pointer für den weiteren internen Gebrauch. + +Um ein Objekt der Klasse \texttt{Treatment} zu erzeugen, muss der Konstruktor aufgerufen werden und die beiden Parameter \texttt{pkt\_to\_inside} und \texttt{pkt\_to\_outside} vom Typ \texttt{PacketContainer*} übergeben werden. + +Die Sequenznummernzuordnung, die ursprünglich in der Methode \texttt{do\_seq\_num\_mapping()} vorgenommen werden sollte, ist nun Teil der Methode \texttt{treat\_packets()}, welche allumfassend für das gesamte Verbindungsmanagement des TCP-Verkehrs zuständig ist. +Der Inhalt der Methode \texttt{manage\_syn\_cookies()} wurde mit der Überarbeitung auf verschiedene Methoden aufgeteilt: Der Hash-Wert des TCP-SYN-Cookies wird in der Methode \texttt{calc\_cookie\_hash()} berechnet. Das dazu benötigte Cookie-Secret ist der globale Wert \texttt{\_cookie\_secret}, der durch den Rückgabewert der Methode \texttt{create\_cookie\_secret()} initialisiert wird. Dieser Wert ändert sich während des Ablaufs des Programms nicht. \texttt{Check\_syn\_cookie()} vergleicht den Cookie eines ankommenden, zum Verbindungsaufbau gehörenden ACKs mit dem für diese Verbindung erwarteten Wert. Dazu wird der Methode unter anderem ein Pointer auf ein \texttt{Data}-Objekt übergeben. Der Aufbau der Klasse \texttt{Data} ist in Abb. \ref{data} genauer dargestellt. +Die Methode \texttt{manage\_timeout()} wurde aus oben genannten Effizienzgründen und der Zugehörigkeit zur Behandlung der Sockstress-Attacken (TCP-Small- bzw. TCP-Zero-Window) ebenfalls in der \texttt{Inspection} verschoben. +Die Methode \texttt{manage\_server\_connection()} wurde mit der Methode \texttt{treat\_packets()} konsolidiert, um auch hier Funktionsaufrufe einzusparen. +\begin{figure}[h] + \centering + \includegraphics[width=0.7\linewidth]{img/Data.pdf} + \caption{Klassendiagramm: \texttt{Data}} + \label{data} +\end{figure} + +\begin{figure}[h] + \centering + \includegraphics[width=0.7\linewidth]{img/Info.pdf} + \caption{Klassendiagramm: \texttt{Info}} + \label{info} +\end{figure} + +\begin{figure}[h] + \centering + \includegraphics[width=0.3\linewidth]{img/MyHashFunction.pdf} + \caption{Klassendiagramm: \texttt{MyHashFunction}} + \label{MyHashFunction} +\end{figure} + +\textbf{Folgende Änderungen ergaben sich während der Validierungsphase}: + +Gemäß der Bennenungskonventionen wurde das Attribut \texttt{\_timestamp} in \texttt{\_s\_timestamp} und die Methode \texttt{increment\_timestamp()} in \texttt{s\_increment\_timestamp()} umbenannt. + +Nach intensivem Testen der Realisierung mittels zwei Maps im Treatment stellte sich heraus, dass diese Realisierung aus performancetechnischer Sicht unvorteilhaft war. In Folge dessen wurden die Maps \texttt{\_ackmap} und \texttt{\_densemap} konsolidiert, sodass nunmehr insgesamt pro Verbindung ein Einfüge-, Such- und Löschvorgang gespart werden kann. + +Die Methode \texttt{treat\_packets()} wurde aufgeteilt: Die Funktion \texttt{treat\_packets\_to\_inside()} bearbeitet diejenige Pakete, die als Ziel die internen Server haben. Die Funktion \\ \texttt{treat\_packets\_to\_outside()} bearbeitet diejenige Pakete, die als Ziel die externen Server haben. + +Die Methode \texttt{create\_cookie\_secret()} wurde aufgrund der Kapselung in die Klasse \texttt{Rand} ausgelagert. Die dortige Methode \texttt{get\_random\_64bit\_value()} ist statisch und verfügt über die gleiche Funktionalität. + +Um die Kapselung der Klasse \texttt{Treatment} zu erhöhen, wurden außerdem alle Methoden bis auf \texttt{treat\_packets\_to\_inside()}, \texttt{treat\_packets\_to\_outside()} und \texttt{s\_increment\_timestamp()} auf \texttt{private} gesetzt. Diese drei sind weiterhin \texttt{public}, weil diese in der \texttt{main.cpp} und im \texttt{Thread} benötigt werden. + +Die Klasse \texttt{info} wurde um vier Attribute ergänzt: \texttt{\_finseen\_to\_inside}, \texttt{\_finseen\_to\_outside}, \texttt{\_ack\_to\_inside} und \texttt{\_ack\_to\_outside}. Das Attribut \texttt{\_finseen} wurde durch diese spezialisiertere Attribute nicht mehr benötigt. +Die zusätzlichen Attribute werden benötigt, um den Verbindungsabbau vornehmen zu können. So wird nicht zu früh in den Zustand der Termination der Verbindung eingegangen. + +\end{document} diff --git a/doc/review_3/chapters/3-feinentwurf.tex b/doc/review_3/chapters/3-feinentwurf.tex new file mode 100644 index 0000000..1e24188 --- /dev/null +++ b/doc/review_3/chapters/3-feinentwurf.tex @@ -0,0 +1,189 @@ +\documentclass[../review_3.tex]{subfiles} +\graphicspath{{\subfix{../img/}}} +\begin{document} + +\chapter{Feinentwurf}\thispagestyle{fancy} +In diesem Kapitel zum Feinentwurf wird der im vorherigen Kapitel beschriebene Grobentwurf verfeinert. Dabei wird für jedes Paket präzise erklärt, auf welche Art und Weise die entsprechende Komponente realisiert werden soll, sodass diese dann direkt implementiert werden kann. Zudem werden mithilfe von Aktivitätsdiagrammen die Kernfunktionen des jeweiligen Pakets übersichtlich und grafisch dargestellt. + +\section{ConfigurationManagement} +Die folgenden Klassen sind im Paket \texttt{ConfigurationManagement} enthalten. + +\subsection{Configurator} +Für die Software gibt es die Konfigurationsdatei \texttt{config.JSON}, welche innerhalb dieser Klasse eingelesen wird und diese Informationen global zur Verfügung stellt. Von dieser Klasse, dem \texttt{Configurator}, soll es im ganzen Programmablauf nur ein Objekt geben, damit keine Inkonsistenzen entstehen. +\begin{figure}[H] + \centering + \includegraphics[width=0.4\linewidth]{img/Configurator.pdf} + \caption{Klassendiagramm: \texttt{Configurator}} + \label{Class_Configurator} +\end{figure} +Aufgrund dieser Anforderungen kann ein spezielles Entwurfsmuster verwendet werden: Das Singleton. Das Singleton ist ein Erzeugungsmuster, welches automatisch dafür sorgt, dass nur eine Instanz des \texttt{Configurator} existieren kann, und es stellt ähnlich globalen Variablen Informationen global dar. Der Vorteil des Singleton besteht darin, dass das Singleton nur dann verwendet wird, wenn es wirklich benötigt wird. Die Klasse \texttt{Configurator} hat nur einen privaten Konstruktor, welcher in der zum Singleton gehörigen Methode der Instanziierung verwendet wird. In der ersten Verwendung des \texttt{Configurators} wird die Methode \texttt{read\_config()} zur Einlesung der Daten ausgeführt. Falls die Konfigurationsdatei nicht auffindbar ist, so wird eine Exception geworfen, da die Software ohne diese nicht ablaufen kann. Die ausgelesenen Daten werden dann in einem privaten JSON-Objekt hinterlegt. Hierzu wird \texttt{nlohmann::JSON} verwendet. Die Informationen der JSON-Datei werden über eine Schnittstelle \texttt{get\_config(Datentyp)} anderen Klassen zur Verfügung gestellt, wobei es unterschiedliche Methoden je nach Datentyp gibt. Der explizite Aufruf des Auslesens erfolgt über die Methode \texttt{instance()}, mithilfe jener ein Zeiger auf das \texttt{Configurator}-Objekt zurückgegeben wird. + +\subsection{Initializer} +Die Klasse \texttt{Initializer} dient dazu, dass grundlegende Initialisierungen für DPDK vorgenommen werden. Hierzu existiert die Methode \texttt{init\_dpdk(int argc, char** argv)}. + +\subsection{Thread} +Die Klasse \texttt{Thread} dient dazu, parallele Threads erzeugen zu können, welche dann die gesamte Paketbehandlung des Systems durchlaufen. Hierzu werden jedem Thread zwei \texttt{PacketContainer} übergeben. Ein \texttt{PacketContainer} dient zum Annehmen von Paketen, welche von außerhalb des Netzwerkes in das Netzwerk kommen. Der andere \texttt{PacketContainer} dient analog zum Annehmen von Paketen, welche von innerhalb des Netzwerkes nach außen sollen. Die \texttt{run()}-Methode der \texttt{Thread}-Klasse hat die Aufgabe, dass eine bestimmte Anzahl an Paketen geholt wird, und zwar mittels der Methode \texttt{poll\_packets(int number)}. Dies gilt für beide \texttt{PacketContainer}. Nachdem die Pakete behandelt wurden, was innerhalb dieses Pollings vorgenommen wird, werden die restlichen Pakete mittels \texttt{send\_packets()} in die jeweilige Richtung weitergeschickt. + +\section{PacketDissection} +Die Aufgabe der \texttt{PacketDissection} ist es, Informationen über die zu untersuchenden Pakete bereitzustellen. Zudem wird die Kommunikation des \texttt{NicManagements} über die \texttt{PacketDissection} geleitet. + +In Diagramm \ref{Sequenzdiagramm_PacketDissection} wird das Polling von Paketen unter Benutzung des \texttt{PacketContainers} dargestellt. Der \texttt{PacketContainer} fungiert hierbei als zentrales Element zur Ablaufsteuerung. + +\begin{figure} + \centering + \includegraphics[width=\linewidth]{img/SequenceDiagramPacketDissection2.pdf} + \caption{Sequenzdiagramm zum Polling von Paketen über den \texttt{PacketContainer}} + \label{Sequenzdiagramm_PacketDissection} +\end{figure} + +\subsection{PacketContainer} +DPDK liefert beim Pollen von Paketen ein Array von Pointern auf sogenannte \texttt{mbuf}-Strukturen. Auch beim Senden muss dem Framework ein solches Array übergeben werden, denn die \texttt{mbuf}-Strukturen repräsentieren Pakete innerhalb von DPDK. Um nur die \texttt{PacketInfo}-Objekte durch das Programm zu reichen, wäre das Array von \texttt{mbuf}-Strukturen zu durchlaufen und die Pointer jeweils in die \texttt{PacketInfo}-Objekte zu schreiben. Ein \texttt{mbuf}(-Paket) gehört dabei genau einer \texttt{PacketInfo}. Wenn am Ende der Pipeline Pakete gesendet werden, müssten die Pointer der \texttt{mbuf}-Strukturen den \texttt{PacketInfo}-Objekten wieder entnommen und in ein Array geschrieben werden. Dies ist überflüssiger Aufwand, da es möglich ist, das empfangene \texttt{mbuf}-Array beizubehalten. Dies setzt der \texttt{PacketContainer} um. + +\begin{figure} + \centering + \includegraphics[width=\linewidth]{img/PacketContainerClass.pdf} + \caption{Klassendiagramm PacketContainer} + \label{Klassendiagramm_PacketContainer} +\end{figure} + +Der \texttt{PacketContainer} ist keine aktive Klasse und wird aufgerufen, um spezielle, im Abb. \ref{Klassendiagramm_PacketContainer} angegebene, Aufgaben umzusetzen. Eine dieser Aufgaben ist das Polling von Paketen, dessen Ablauf im Sequenzdiagramm \ref{Sequenzdiagramm_PacketDissection} dargestellt wird. Eine weitere Aufgabe ist das Verwerfen von Paketen, welches durch \texttt{drop\_packet(int index)} umgesetzt wird. Hierbei wird dem \texttt{NetworkPacketHandler} mitgeteilt, welcher \texttt{mbuf} verworfen werden soll und die Referenzen im \texttt{PacketContainer} selbst gelöscht. + +Es ist aber auch möglich, mittels der Methode \texttt{take\_packet(int Index):PacketInfo*} Pakete aus dem \texttt{PacketContainer} zu entfernen, ohne sie zu löschen. Dafür werden nur die\\ \texttt{PacketContainer}-internen Referenzen auf den \texttt{mbuf} und seine \texttt{PacketInfo} zu Nullreferenzen gesetzt und die \texttt{PacketInfo} zurückgegeben. Diese entnommenen Pakete können später wieder mit \texttt{add\_packet(PacketInfo* pkt\_info):int} eingefügt werden. Dafür wird dieses Paket hinter den bereits existenten Pakete im \texttt{mbuf}-Array gespeichert. Selbiges wird für die zugehörige \texttt{PacketInfo} gemacht. Zurückgegeben wird der Index, unter dem das neue Paket zukünftig erreichbar sein wird. Es können nicht nur zuvor entnommene Pakete einem \texttt{PacketContainer} hinzugefügt werden, sondern auch komplett neue. Dieses Erstellen von Paketen ist mit dem Befehl \texttt{get\_empty\_packet(PacketType pkt\_type):PacketInfo*} möglich. Hierbei wird für einen neuen \texttt{mbuf} Speicher aus einem \texttt{mempool} alloziert und eine zugehörige \texttt{PacketInfo} vom gewünschten \texttt{PacketType} erstellt. Mithilfe dieser \texttt{PacketInfo}, kann der Paketkopf im Anschluss befüllt werden. Zuletzt müssen all diese Pakete auch wieder mit \texttt{send\_packets()} versendet werden. Dafür wird das \texttt{mbuf}-Array, falls notwendig, umsortiert, da durch \texttt{drop\_packet(int index)} Lücken entstehen können und DPDK nicht mit Nullreferenzen umgehen kann. Zuletzt wird das Array über den NetworkPacketHandler an DPDK zur Versendung übergeben. + +Auch wenn bisher immer nur von je einem Array für \texttt{mbuf}s und \texttt{PacketInfos} gesprochen wurde, können es mehrere werden. Es gibt bei DPDK eine sogenannte BurstSize, welche angibt, wie viel Pakete maximal auf einmal entgegengenommen und wieder versendet werden können. Daran sind auch die Größen der Arrays angepasst. Da es aber durch Maßnahmen des \texttt{Treatments} und \texttt{Inspection} zur Erzeugung von neuen Paketen kommen kann, gibt es zusätzliche Arrays, falls die ersten bereits voll sind. Die Verwaltung dieser Arrays ist in allen Funktionen enthalten und hat nach außen keinen sichtbaren Effekt. + +\subsection{PacketInfo} +Die genaue Umsetzung, sowie die daraus resultierende Befüllung hat sich im Laufe der Entwicklungsphase sehr stark verändert. Dies hatte vor allem Performance-Gründe. In der aktuellen Variante ist die \texttt{PacketInfo} selbst nur für die Verwaltung des \texttt{mbuf}s sowie das Speichern seines Layer 3 und 4-Protokolls verantwortlich. + +Um ausschließlich notwendige Informationen zu speichern, wird diese \texttt{PacketInfo} in eine protokollspezifische Variante gecastet. Diese spezialisierten Varianten erben von der eigentlichen \texttt{PacketInfo} und erweitern sie um Getter- und Setterfunktionen für die relevanten Header-Informationen ihrer jeweiligen Protokolle. + +Auch wenn in Diagramm \ref{Klassendiagramm_PacketInfo} \texttt{PacketInfos} mit IPv6 aufgeführt werden, sind diese noch nicht funktionsfähig. Es wurde sich entsprechend der Anforderungen zuerst auf IPv4 konzentriert. + +\begin{figure} + \centering + \includegraphics[width=\linewidth]{img/PacketInfoInheritance.pdf} + \caption{Klassendiagramm aller PacketInfo Varianten} + \label{Klassendiagramm_PacketInfo} +\end{figure} + +\subsection{HeaderExtractor} +Wie bereits erwähnt, wurde die Extraktion der Headerinformationen dezentralisiert und wird nur bei Abruf entsprechender Informationen durchgeführt. Dies führte zu einer Verringerung von Code für den \texttt{HeaderExtractor} im Laufe der Entwicklung, weshalb er in den \texttt{PacketContainer} integriert wurde. In obigen Sequenzdiagramm stellt er die Funktionen \texttt{extract\_header\_info()} und \texttt{fill\_info(rte\_mbuf* mbuf, PacketInfo* pkt\_inf)}. + +Dabei wird in \texttt{extract\_header\_info()} über die einzelnen Elemente des \texttt{PacketContainers} iteriert und für jeden \texttt{mbuf} die Funktion \texttt{fill\_info(rte\_mbuf* mbuf, PacketInfo* pkt\_inf)} aufgerufen, welche wiederum den \texttt{PacketInfoCreator} ausführt und den \texttt{mbuf} mit der zugehörigen \texttt{PacketInfo} verknüpft. + +\subsection{PacketInfoCreator} +Diese Klasse ist ein Hilfsmittel, um Vererbungsketten zu vermeiden. Ihre Aufgabe ist es, die zum Paket passende \texttt{PacketInfo}-Version zu erzeugen. Dabei liest der \texttt{PacketInfoCreator} die Layer 3 und Layer 4-Protokoll-IDs aus, legt die entsprechenden \texttt{structs} auf den Speicher und speichert sie in der frisch erzeugten \texttt{PacketInfo}. + +\section{Inspection} +Die \texttt{Inspection} ist für die Erkennung böswilliger IP Pakete zuständig und untersucht diese daher auf verdächtige Strukturen und Muster. Dazu wird auch eine eigene lokale Statistik erstellt, zur Auswertung genutzt und zur Informationsweitergabe mit einer globalen Statistik geteilt. + +Der \texttt{Initializer} erstellt für jeden genutzten Thread eine eigene Inspektion, welche alle Pakete dieses Threads analysiert und DDoS-Attacken erkennt. Dazu wird der \texttt{Inspection} jeweils ein \texttt{PacketContainer} übergeben, der eine Menge von Paketen enthält, die über das \texttt{NicManagement} eingegangen sind. + +Die Erkennung basiert auf einer Mustererkennung von zeitlich aufeinanderfolgenden Paketen nach einer Auftrennung in die Protokolle UDP, TCP und ICMP. UDP und ICMP Pakete werden ausschließlich mit einem vorher festgelegten Threshold geprüft, der sich an eine selbst berechnete Angriffsrate anpasst. TCP Pakete werden zusätzlich auf Zero und Small Window sowie auf SYN-FIN und SYN-FIN-ACK Muster überprüft. + +Der Ablauf und Reihenfolge der Prüfungen der \texttt{Inspection} ist aus einer Versuchsdurchführung mit einem Decision Tree für DDoS-Abwehr entstanden, um einen möglichst schnellen und effizienten Ablauf zu finden. Implementiert wurde eine statische, nicht veränderliche Pipeline, die nach den größten auszuschließenden Faktoren jedes Pakets vorgeht. + +Der Ablauf kann grob in drei Filterstufen, auch Security Layers genannt, unterteilt werden: +\begin{enumerate} + \item RFC Compliance + \item Static Rules + \item Dynamic Rules +\end{enumerate} +Ob ein Paket dem RFC Standard entspricht, wird bereits bei der \texttt{PacketInfo} klar. Die Klasse \texttt{Inspection} bietet die Möglichkeit, bestimmte Fehler zuzulassen oder Pakete mit bestimmten Fehlern zu blockieren und zu löschen. + +Die zweite Stufe der Filter setzt sich aus fest definierten Angriffen und Angriffsmustern zusammen. So sind zum Beispiel bei SYN-FIN und SYN-FIN-ACK Angriffen immer die Flags SYN und FIN oder SYN, FIN und ACK gesetzt. So können diese sofort erkannt und das Paket verworfen werden. Weitere Angriffe, die in der statischen Abwehr erkannt werden, sind Zero- und Small-Window Angriffe. + +\begin{figure}[H] + \centering + \includegraphics[width=\linewidth]{img/Security_Layers.png} + \caption{Stufen der Sicherheit} + \label{security_layers} +\end{figure} + +In der dynamischen Filterstufe werden die Filterregeln entsprechend dem aktuellen Netzwerkverkehr und vorher eingegangenen Paketen angepasst. So dient ein Limit der Paketrate (engl. Threshold) dazu, UDP und TCP Floods abzuwehren. Eigene Verbindungstabellen der ausgehenden Netzwerkverbindungen lassen jedoch legitime Pakete, die als Antwort auf eine bestehende Verbindung dienen, weiterhin zu, um den legitimen Netzwerkverkehr nicht einzuschränken. + +Die Verknüpfung und Ablauf der Filterung wird in der Abbildung. \ref{security_layers} vereinfacht dargestellt. + +Im Diagramm \ref{security_layers} muss Folgendes unterschieden werden: Die Computer 1 bis 3 sind Angreifer mit unterschiedlichen Angriffen, die ebenso in unterschiedlichen Filterstufen als Angriff erkannt werden und Computer 4 als Nutzer mit legitimen Anfragen an den Server, die den Filterregeln entsprechen. +Ausgehender Verkehr aus dem internen System wird grundsätzlich vertraut und nicht zusätzlich gefiltert. Jedoch wird ausgehender Verkehr analysiert, um die dynamischen Regeln anzupassen. + +Nach jedem Durchlauf eines \texttt{PacketContainer}s werden die lokalen und globalen Statistiken aktualisiert. Die Weitergabe der Informationen an die Statistik erfolgt über einen eigenen Interthread- Kommunikationskanal zum globalen Statistik-Thread. Die globale Statistik führt alle einzelnen Informationen zusammen und macht sie dem Nutzer in einfacher Weise abrufbar. +\begin{figure}[h] + \centering + \includegraphics[angle=90, width=\linewidth]{img/inspection_ablauf.png} + \caption{Aktivitätsdiagramm der Methode \texttt{analyzeContainer()} der Inspection} + \label{inspection_activity} +\end{figure} + +\section{Treatment} +Das \texttt{Treatment}, welches für die Behandlung der SYN-Flut zuständig ist, erhält vom \texttt{Thread} zwei Pointer auf \texttt{PacketContainer}. Für jede Senderichtung, sowohl von Intern nach Extern als auch umgekehrt, existiert einer dieser Container. Der Ablauf in der Behandlung von Paketen unterscheidet sich basierend auf deren Senderichtung. Jedes Paket wird im \texttt{Treatment} zwar einzeln, allerdings im Kontext der gesamten Verbindung betrachtet. Die Behandlung im \texttt{Treatment} beginnt mit dem Iterieren über die Einträge im jeweiligen \texttt{PacketContainer}. Hierbei wird zugleich geprüft, ob das gerade betrachtete Paket bereits gelöscht wurde, oder von einem Typ ist, welcher nicht im \texttt{Treatment} behandelt wird. Dies ist auch im globalen Ablauf der Funktion \texttt{treat\_packets()} in Abbildung \ref{Aktivität_treat_packet_0} sowie \ref{Aktivität_treat_packet_1} zu erkennen. Sollte dies der Fall sein, wird ebendieser Eintrag übersprungen. Sollte es sich bei dem gerade betrachteten Paket beispielsweise um ein UDP-Paket handeln, so wird dieses im Treatment nicht weiter betrachtet, da dies bereits in der \texttt{Inspection} geschah. + +Nach diesen ersten Tests findet jeweils eine Fallunterscheidung statt. Für Pakete, welche von extern nach intern geschickt werden sollen, gilt: + +Falls es sich bei dem Paket um ein TCP-SYN-Paket handelt, so wird als Antwort hierauf ein SYN-ACK generiert, dessen Sequenznummer durch einen, vom Programm berechneten, SYN-Cookie ersetzt wird. Hierzu existiert die Methode \texttt{calc\_cookie\_hash()}, welche 24 der 32 Bit langen Sequenznummer generiert, welche später mit einem 8 Bit Zeitpunkt (engl. \glqq Timestamp\grqq) in der Methode \texttt{treat\_packets()} aufgefüllt werden. Dieser SYN-Cookie enthält Informationen über die Verbindungsentitäten, sowie zur Verbesserung der Effektivität einen Zeitstempel und ein Secret. Dieser SYN-Cookie ermöglicht es, im Verlauf des Verbindungsaufbaus auf das Speichern von Informationen über die Verbindung zu verzichten. Somit wird die Angriffsfläche von SYN-Floods effektiv minimiert. + +Sollte ein ACK als Reaktion auf dieses SYN-ACK erhalten werden, so ist durch die Funktion \texttt{check\_syn\_cookie()} zu überprüfen, ob der empfangene Cookie in Form der Sequenznummer plausibel ist (Siehe Abb. \ref{check_syn_cookie}). Das bedeutet, dass der Zeitstempel maximal eine Zeiteinheit alt ist, welche in diesem Programm 64 Sekunden dauert, und der restliche Cookie mit dem erwarteten Cookie übereinstimmt. Der Cookie setzt sich insgesamt zusammen aus 8 Bit Timestamp, sowie 24 Bit Hashwert über externe und interne IP-Adresse, externe und interne Portnummer sowie dem Timestamp und dem Cookie\_Secret. +Des weiteren ist eine Verbindung mit dem internen Server, spezifiziert in der DestIP des ACK-Paketes, aufzubauen. Zudem muss die dem ACK hinzugefügten Payload gespeichert werden, auch dies geschieht in einer separaten Map, der ACKmap. Dieses ACK-Paket muss nach erfolgreichem Verbindungsaufbau mit dem internen Server an ebendiesen verschickt werden. + +Wird ein SYN-ACK von extern empfangen, so ist dies ohne Veränderung an das interne Netz zuzustellen. Allerdings muss hier ein Eintrag in der Offsetmap erzeugt werden, wobei der Offset realisierungsbedingt null ist. + +Werden Pakete ohne gesetzte Flags, beziehungsweise nur mit gesetztem ACK-Flag verschickt, so findet eine Sequenznummernzuordnung und eine Anpassung von Sequenznummern statt. Hierzu wird eine Densemap mit individueller Hashfunktion, in diesem Fall XXH3, verwendet. Bei der Densemap handelt es sich um eine besonders effiziente Hashmap, welche ein Einfügen, Suchen und Löschen in bis zu vier mal weniger Zeit als eine unordered\_map ermöglicht. Die Auswahl der Hashfunktion XXH3 ist dadurch motiviert, dass sie extrem schnell ist und dennoch kaum Kollisionen erzeugt. Insbesondere werden durch sie bereits auf handelsüblichen Computersystemen Hashraten von bis zu 31.5 Gbit/s erzielt. + +Der Ablauf bei Empfang eines solchen Paketes ist wie folgt: Bei eingehenden Paketen wird ein zuvor berechneter Offset, welcher in der Offsetmap für jede Verbindung gespeichert ist, von der ACK-Nummer subtrahiert. + +Wird ein ACK empfangen, welches zu einer Verbindung gehört, in deren Info finseen auf true gesetzt ist, so muss die ACK-Nummer angepasst, das Paket an den internen Server geschickt und der Eintrag in der Densemap verworfen werden. + +Falls ein Paket mit gesetztem RST-Flag von extern empfangen wird, wird der Eintrag in der Densemap gelöscht und das empfangene Paket an den internen Server weitergeleitet. Hierbei muss keine Anpassung der ACK-Nummer vorgenommen werden. + +Sollte ein FIN empfangen werden, so muss im Info-Struct, welches Teil der Offsetmap ist, der Wert finseen auf true gesetzt werden. In diesem Fall ist das Paket nach Anpassung der ACK-Nummer weiterzuleiten. + +Im zweiten Fall der übergeordneten Fallunterscheidung erhält das Programm anschließend den \texttt{PacketContainer} der Pakete, welche das Netz von intern nach extern verlassen wollen. Auch hier wird, bevor ein Paket der Behandlung unterzogen wird, geprüft, ob das Paket nicht bereits gelöscht wurde, oder es sich um ein Paket falschen Typs handelt. + +Erhält das System ein SYN-Paket von einem internen Server, so wird dieses an das im Paket spezifizierte Ziel weitergeleitet. Eine Anpassung der Sequenznummer findet in diesem Fall nicht statt. + +Erhält das System ein SYN-ACK aus dem internen Netz, so muss das System die Differenz aus der ACK-Nummer dieses Pakets, und der des in der ACKmap gespeicherten Paketes berechnen, und den Wert als Offset in der Offsetmap eintragen. Das von intern empfangene SYN-ACK Paket muss verworfen werden. +Das zuvor in der ACKmap zwischengespeicherte Paket muss nun mit angepasster ACK-Nummer intACK = extACK-offset an den internen Host geschickt werden. + +Wird ein Paket ohne gesetzte Flags oder mit gesetztem ACK-Flag von Intern nach Extern verschickt, so findet eine weitere Fallunterscheidung statt. +Im Fall, dass finseen bereits auf true gesetzt ist, muss der Offset in der Offsetmap nachgeschlagen werden, der Eintrag daraufhin gelöscht werden und das empfangene Paket mit extSeq = intSeq + offset verschickt werden. +Gesetzt den Fall, dass noch kein Eintrag in der Offsetmap existiert, muss ein neuer Eintrag in dieser erstellt werden. Der Offsetwert muss auf null, und finseen auf false gesetzt werden. Das empfangene Paket muss hiernach nach Intern weitergeleitet werden. +Trifft keiner der beiden obigen Fälle ein, so muss der Offset in der Offsetmap nachgeschlagen werden und das empfangene Paket nach Intern weitergeschickt werden. Vor dem Versenden muss hierbei die Sequenznummer wie folgt angepasst werden: extSeq = intSeq + offset. + +Sollte ein Paket mit gesetztem FIN-Flag erkannt werden, so ist diese Information in der Info an Stelle Key = Hash(extip, intip, extport, intport) mit dem Vermerk finseen = true, zu speichern. Das empfangene Paket ist durch das System nach extern mit extSeq = intSeq + offset weiterzuschicken. + +Wird ein RST erhalten, so ist eine Anpassung der Sequenznummer vorzunehmen, das Paket entsprechend weiterzuleiten und der Eintrag in der Offsetmap an entsprechender Stelle zu entfernen. + +Des weiteren könnte es unter Umständen erforderlich werden, die Einträge mit einem Timestamp zu versehen, welcher speichert, wann dieser Eintrag zuletzt verwendet wurde, sollte es zu Situationen kommen, in denen sowohl Sender als auch Empfänger die Verbindung nicht korrekt terminieren können. Dies ist bisweilen nicht implementiert, die Idee wird allerdings basierend auf den Ausgängen der Tests auf dem Testbed weiter verfolgt oder verworfen. + +\begin{figure}[h] + \centering + \includegraphics[width=0.98\linewidth]{img/treat_packets_0.pdf} + \caption{Aktivitätsdiagramm der Methode \texttt{treat\_packets()}, Teil: Pakete nach Intern} + \label{Aktivität_treat_packet_0} +\end{figure} +\begin{figure}[h] + \centering + \includegraphics[width=0.96\linewidth]{img/treat_packets_1.pdf} + \caption{Aktivitätsdiagramm der Methode \texttt{treat\_packets()}, Teil: Pakete nach Extern} + \label{Aktivität_treat_packet_1} +\end{figure} + +\begin{figure}[t] + \centering + \includegraphics[width=\linewidth]{img/check_typ_syn_cookie_neu.pdf} + \caption{Aktivitätsdiagramm der Methode \texttt{check\_syn\_cookie()}} + \label{check_syn_cookie} +\end{figure} +Nachdem ein ACK als Reaktion auf ein SYN-ACK bei dem zu entwerfenden System angekommen ist, wird die Methode \texttt{check\_typ\_syn\_cookie()} aufgerufen. +Grundsätzlich wird hier überprüft, ob der Hash-Wert aus dem empfangenen Paket mit dem eigens berechneten Hash-Wert übereinstimmt. Falls dies nicht der Fall ist oder die Differenz der Zeitstempel zu groß ist, wird ein Paket mit gesetzten Reset-Flag (RST) an den Sender geschickt. Dieses Flag zeigt an, dass die Verbindung beendet werden soll. Andernfalls wird die Verbindung als legitim erkannt und das Paket in der ACKmap zwischengespeichert, bis die Verbindung mit dem internen System erfolgreich war. + +Abbildung \ref{createcookiesecret} zeigt die parameterlose Methode \texttt{create\_cookie\_secret()}. Zu Beginn werden drei 16 Bit lange Zufallszahlen generiert, wobei auf die Funktion \texttt{rand()} aus der C Standardbibliothek zugegriffen wird. Der erste mit \texttt{rand()} generierte Wert wird um 48 Bit nach links verschoben, der zweite um 32 Bit. Diese beiden Werte werden danach bitweise ODER miteinander verknüpft. Dieser verknüpfte Wert wird dann wiederum mit der dritten zufälligen 16 Bit Zahl bitweise ODER verknüpft. Das Ergebnis dieser Verknüpfung ist eine 64 Bit lange Zufallszahl, die von der Methode zurückgegeben wird. + +\begin{figure}[H] + \centering + \includegraphics[width=\linewidth]{img/create_cookie_secret_neu.pdf} + \caption{Aktivitätsdiagramm der Methode \texttt{create\_cookie\_secret()}} + \label{createcookiesecret} +\end{figure} + +\end{document} diff --git a/doc/review_3/chapters/4-tests.pdf b/doc/review_3/chapters/4-tests.pdf new file mode 100644 index 0000000..e349129 Binary files /dev/null and b/doc/review_3/chapters/4-tests.pdf differ diff --git a/doc/review_3/chapters/4-tests.tex b/doc/review_3/chapters/4-tests.tex new file mode 100644 index 0000000..4d9c85c --- /dev/null +++ b/doc/review_3/chapters/4-tests.tex @@ -0,0 +1,839 @@ +\documentclass[../review_3.tex]{subfiles} +\graphicspath{{\subfix{../img/}}} +\begin{document} + +\chapter{Testdokumentation}\thispagestyle{fancy} +Im vorliegenden Softwareprojekt wurden verschiedene Arten von Tests durchgeführt. Zum einen wurden die einzelnen Komponenten mit Hilfe von Unit-Tests auf ihre korrekte Funktionalität geprüft, zum anderen wurde direkt auf dem Testbed getestet. Der Aufbau des Testbeds ist in Abbildung \ref{fig:Versuchsaufbau} (siehe Seite \pageref{fig:Versuchsaufbau}) dargestellt. + +\section{Unit-Tests} + +In sogenannten Unit-Tests, die auch als Modultest oder Komponententest bezeichnet werden, geht es um den Test einzelner Teile der Software. Unter Testen wird das Überprüfen, ob das Modell dem System entspricht, verstanden. Dies kann jedoch nur die Anwesenheit von Fehlern, nicht aber deren Abwesenheit nachweisen. Fast jede Komponente und die jeweils darin enthaltenen Klassen haben eine eigene Testdatei, in der die Unit-Tests ausgeführt werden konnten. + +Da die Unit-Tests im vorliegenden Softwareprojekt bereits in der Implementierungsphase durchgeführt werden konnten, noch vor der eigentlichen Validierungsphase, war es möglich, Fehler bereits frühzeitig zu erkennen. Ein weiterer Vorteil des Unit-Testens besteht darin, dass beim Auftreten eines Fehlers dieser sehr genau eingegrenzt werden kann. Somit kann dieser Fehler schneller gefunden und dann auch behoben werden. Außerdem ermöglichen Unit-Tests eine parallele Bearbeitung, denn das Testbed existiert schließlich nur einmal. + +\subsection{Mocking: \texttt{libdpdk\_dummy}} +Mocking (dt.: Nachbildung oder Imitation) findet innerhalb der Unit-Tests Verwendung, um so isolierte Tests durchführen zu können. Da in diesem Projekt Tests bereits frühzeitig stattfinden sollten, sich DPDK jedoch nicht mit Unit Tests kompilieren ließ, wurde die Mocking-Bibliothek \texttt{libdpdk\_dummy} erstellt. Diese kleine Bibliothek weist zwar eine geringere Funktionalität als das komplette DPDK-Framework auf, setzt aber genau das um, was bei den Unit-Tests gebraucht wird. So wurden genau die Header-Dateien nachgebildet, deren Funktionalitäten beim Testen benötigt wurden. Diese Nachbildung entstand durch Kopieren aus den \glqq Original-DPDK-Headern\grqq{} und individuelle Anpassung an die Anforderungen des Projekts. + +\begin{lstlisting} [caption= {Unit-Test zu \texttt{lipdpdk\_dummy}}, label={libdpdk}] +TEST_CASE("rte_mbuf", "[]"){ + struct rte_mbuf* mbuf; + struct rte_mempool* mempool = nullptr; + + mbuf = rte_pktmbuf_alloc(mempool); + CHECK(mbuf != nullptr); +} +\end{lstlisting} + +Auch zu \texttt{lipdpdk\_dummy} existiert ein Unit-Test, um zu überprüfen, ob sie so wie beabsichtigt arbeitet (vgl. Codeausschnitt \ref{libdpdk}). Hier werden zuerst Pointer auf einen \texttt{rte\_mbuf} und einen \texttt{rte\_mempool} angelegt. Danach wird überprüft, ob die Methode \texttt{rte\_pktmbuf\_alloc()} richtig arbeitet, indem gecheckt wird, ob nach der Allokation in \texttt{mbuf} kein Nullpointer liegt. Auf das triviale Löschen des \texttt{mbuf}s wird an dieser Stelle verzichtet, weil es bei diesem Test lediglich auf die grundlegende Funktionalität ankommt. Da das Löschen sehr einfach ist, ist das es in diesem Fall nicht unbedingt notwendig. + +\subsection{ConfigurationManagement} +... %\todo +\textcolor{red}{toDo Leon} + +\subsubsection{Beispiel: Einlesen einer JSON-Datei} +\begin{lstlisting} [caption= {Unit-Test zum Einlesen einer JSON-Datei}, label={config1}] +TEST_CASE("Json Datei einlesen", "[]") { + + REQUIRE_NOTHROW(Configurator::instance()->read_config( + "../test/ConfigurationManagement/config_test.json")); + + REQUIRE(Configurator::instance()->get_config_as_bool("BOOLEAN") == true); REQUIRE(Configurator::instance()->get_config_as_unsigned_int( + "UNSIGNED_INT") == 42); + REQUIRE(Configurator::instance()->get_config_as_string("STRING") == + "Hello World."); + REQUIRE(Configurator::instance()->get_config_as_float("FLOAT") == 1.337f); + REQUIRE(Configurator::instance()->get_config_as_double("DOUBLE") == -3.001); +} \end{lstlisting} +\subsubsection{Beispiel: Nicht exisitierende JSON-Datei} +\begin{lstlisting}[caption= {Unit Test: Nicht existierende JSON-Datei}, label={config2}] +TEST_CASE("nicht existierende Json-Datei", "[]") { + REQUIRE_THROWS(Configurator::instance()->read_config("non-existent.json")); + REQUIRE_THROWS(Configurator::instance()->read_config("non-existent.json", + "typo.json")); +}\end{lstlisting} + +\subsection{PacketDissection} +%\todo Ausführlicher das Beschreiben +Das Paket der \texttt{PacketDissection} wird in zwei verschiedenen Testdateien getestet: \\ %\\weil sonst das von texttt zu weit rausragt +\texttt{PacketContainer\_test.cpp} und \texttt{PacketInfo\_test.cpp}. + +Diese Aufteilung ermöglich einen gewissen Grad an Unabhängigkeit beim Testen. Außerdem findet man die Testfälle zur gewünschten Klasse auf diese Art wesentlich schneller. +\subsubsection{Beispiele aus dem PacketContainer} +\begin{lstlisting} [caption = {Testfall PacketContainer}, label={pc1}] +TEST_CASE("PacketContainer", "[]") { + uint16_t inside_port = 0; + uint16_t outside_port = 1; + struct rte_mempool mbuf_pool_struct; + struct rte_mempool* mbuf_pool = &mbuf_pool_struct; + CHECK(mbuf_pool != nullptr); + + NetworkPacketHandler pkt_handler(0,0); + + PacketContainer pkt_container(pkt_handler, mbuf_pool, inside_port, outside_port); + + ... + +} \end{lstlisting} +Innerhalb des Testfalls \texttt{PacketContainer} sind die folgend dargestellten Sektionen eingebettet (vgl. Codeausschnitt \ref{pc1}). Sektionen dienen im Allgemeinen zur weiteren Unterteilung bzw. Strukturierung der Testcases. Im Folgenden Abschnitt werden einige dieser Sektionen in\\ \texttt{PacketContainer\_test.cpp} näher erläutert. + +Im obigen Testfall werden zuerst alle für die folgenden Unit-Tests benötigten Initialisierungen vorgenommen, wie zum Beispiel die des \texttt{rte\_mempool} (siehe Z. 5) und des \texttt{NetworkPacketHandler} sowie des \texttt{PacketContainer} (siehe Z. 8 und 10). + +\begin{lstlisting} [caption= {Sektion \texttt{get\_empty\_packet} mit den zwei Untersektionen \texttt{default} und \texttt{IPv4TCP}}, label={pc2}] +SECTION("get_empty_packet", "[]") { + + SECTION("default", "[]") { + CHECK(pkt_container->get_number_of_polled_packets() == 0); + CHECK(pkt_container->get_total_number_of_packets() == 0); + + // by default it returns a TCP SYN packet + PacketInfo* pkt_info = pkt_container->get_empty_packet(); + CHECK(pkt_info != nullptr); + CHECK(pkt_info->get_mbuf() != nullptr); + CHECK(pkt_info->get_type() == IPv4TCP); + + CHECK(pkt_container->get_total_number_of_packets() >= + pkt_container->get_number_of_polled_packets()); + CHECK(pkt_container->get_total_number_of_packets() == 1); + CHECK(pkt_container->get_number_of_polled_packets() == 0); + } + + SECTION("IPv4TCP", "[]") { + CHECK(pkt_container->get_number_of_polled_packets() == 0); + CHECK(pkt_container->get_total_number_of_packets() == 0); + + PacketInfo* pkt_info = pkt_container->get_empty_packet(IPv4TCP); + CHECK(pkt_info != nullptr); + CHECK(pkt_info->get_mbuf() != nullptr); + CHECK(pkt_info->get_type() == IPv4TCP); + + CHECK(pkt_container->get_total_number_of_packets() >=pkt_container-> + get_number_of_polled_packets()); + CHECK(pkt_container->get_total_number_of_packets() == 1); + CHECK(pkt_container->get_number_of_polled_packets() == 0); + } +}\end{lstlisting} + +Im Codeausschnitt \ref{pc2} zur Methode \texttt{get\_empty\_packet()} wird überprüft, ob die Getter-Methode zum Erhalten eines leeren Paketes wie gewünscht funktioniert. Zunächst wird dazu in Z. 4 f. sichergestellt, dass die bisherige Zahl der (gepollten) Pakete null ist. Anschließend wird für eine \texttt{PacketInfo}-Referenz die entsprechende Methode aufgerufen, wie in Z. 7 zu erkennen ist. In den anschließenden Code-Zeilen wird gecheckt, ob es keine Null-Pointer gibt und ob es sich um \texttt{IPv4TCP} handelt. Außerdem ist es wichtig, dass die Anzahl der Pakete insgesamt größer als die der abgefragten Pakete ist, genauer gesagt eins und null. Die entsprechenden Assertions sind in Z. 27 - 30 zu finden. + +Die darauf folgende Section \texttt{IPv4TCP} unterscheidet sich nur insofern von der default-Variante, dass hier in Z. 22 beim Aufruf der \texttt{get\_empty\_packet()}-Methode zusätzlich \texttt{IPv4TCP} übergeben wird. Die Beschreibung der weiteren Bestandteile der Sektion findet man demzufolge im vorhergehenden Absatz. + +Im Codeausschnitt \ref{pc3} wird \texttt{get\_empty\_packet()} so oft aufgerufen, bis die \texttt{BURST\_SIZE} erreicht ist. Direkt danach wird sichergestellt, dass die gesamte Anzahl an Paketen auch wirklich der \texttt{BURST\_SIZE} entspricht. + +\begin{lstlisting} [caption= {Sektion ,,Create more packets than burst size'' mit den zwei Untersektionen ,,fill till BURST\_SIZE'' und ,,fill till BURST\_SIZE + 1''}, label={pc3}] +SECTION("create more packets than burst size", "[]") { + + SECTION("fill till BURST_SIZE", "[]") { + for (int i = 0; i < BURST_SIZE; ++i) { + PacketInfo* pkt_info = pkt_container->get_empty_packet(); + CHECK(pkt_info != nullptr); + } + + CHECK(pkt_container->get_total_number_of_packets() == BURST_SIZE); + } + + SECTION("fill till BURST_SIZE + 1", "[]") { + for (int i = 0; i < BURST_SIZE + 1; ++i) { + PacketInfo* pkt_info = pkt_container->get_empty_packet(); + CHECK(pkt_info != nullptr); + } + + CHECK(pkt_container->get_total_number_of_packets() == + BURST_SIZE + 1); + } + + CHECK(pkt_container->get_total_number_of_packets() >= + pkt_container->get_number_of_polled_packets()); +}\end{lstlisting} + +In der anschließenden Sektion wird eine ähnliche Befüllung des \texttt{pkt\_container} vorgenommen. Der Unterschied besteht darin, dass die Methode zum Erhalten der leeren Pakete einmal öfter aufgerufen wird. + +Die Test-Section, die in Codeabschnitt \ref{pc4} zu sehen ist, dient zum Test des Zugriffes auf einzelne Pakete an einem bestimmten Index. Dafür wurden zwei Unter-Sections geschrieben, eine für allgemeine Tests und eine, die sich auf Out-of-Bounds-Fehler bezieht. + +In ersterer wird zunächst in Z. 5 ein leeres Paket hinzugefügt und wieder die Richtigkeit der Anzahl der Pakete getestet. Darauf hin kommt es für \texttt{pkt\_info\_1} zum Aufruf der\\ \texttt{get\_packet\_at\_index()}-Methode. Dabei wird der mit Hilfe von\\ \texttt{get\_total\_number\_of\_packets()} ermittelte Index des zu Beginn erstellten leeren Pakets übergeben. Anschließend wird wieder einmal gecheckt, ob es sich auch wirklich um ein valide Paket handelt und die Anzahl aller Pakete und der abgerufenen Pakete korrekt ist. In Z. 18 f. wird zunächst getestet, ob es bei der bereits in Z. 9 aufgerufenen Methode auch wirklich zu keinen Fehlern kommt. Bei der Übergabe eines Indexes, unter dem kein Paket zu finden ist, muss es allerdings zum Wurf einer Exception kommen, was in Z. 20 kontrolliert wird. + +\begin{lstlisting} [caption= {Sektion ,,get\_packet\_at\_index'' mit den zwei Untersektionen ,,general'' und ,,test out of bounds error''}, label={pc4}] +SECTION("get_packet_at_index", "[]") { + + SECTION("general", "[]") { + // add empty packet + PacketInfo* pkt_info_0 = pkt_container->get_empty_packet(); + CHECK(pkt_container->get_number_of_polled_packets() == 0); + CHECK(pkt_container->get_total_number_of_packets() == 1); + + PacketInfo* pkt_info_1 = pkt_container->get_packet_at_index( + pkt_container->get_total_number_of_packets() - 1); + CHECK(pkt_info_0 == pkt_info_1); + CHECK(pkt_info_1 != nullptr); + CHECK(pkt_info_1->get_mbuf() != nullptr); + CHECK(pkt_info_1->get_type() == IPv4TCP); + + CHECK(pkt_container->get_total_number_of_packets() >= + pkt_container->get_number_of_polled_packets()); + CHECK_NOTHROW(pkt_container->get_packet_at_index( + pkt_container->get_total_number_of_packets() - 1)); + CHECK_THROWS(pkt_container->get_packet_at_index( + pkt_container->get_total_number_of_packets())); + } + + SECTION("test out of bounds error", "[]") { + for (int i = 0; i < int(BURST_SIZE / 2); ++i) { + pkt_container->get_empty_packet(); + } + + CHECK(pkt_container->get_total_number_of_packets() == + int(BURST_SIZE / 2)); + + for (int i = 0; i < int(BURST_SIZE / 2); ++i) { + CHECK_NOTHROW(pkt_container->get_packet_at_index(i)); + } + + for (int i = int(BURST_SIZE / 2); i < BURST_SIZE; ++i) { + CHECK_THROWS(pkt_container->get_packet_at_index(i)); + } + + CHECK_THROWS(pkt_container->get_packet_at_index( + pkt_container->get_total_number_of_packets())); + CHECK_NOTHROW(pkt_container->get_packet_at_index( + pkt_container->get_total_number_of_packets() - 1)); + } +}\end{lstlisting} + +In der zweiten Section des Codeabschnitts \ref{pc4} wird wieder ähnlich wie oben vorgegangen. Zu Beginn wird für jedes \texttt{i} von 0 bis \texttt{BURST\_SIZE / 2} ein leeres Paket abgerufen und geprüft, ob die Anzahl der Pakete stimmt. Somit darf es auch beim Aufruf von \texttt{get\_packet\_at\_index} für die erste Hälfte des Intervalls von null bis \texttt{BURST\_SIZE} zu keinem Fehler kommen, was in Z. 32 ff. getestet wird. In der darauf folgenden for-Schleife muss es hingegen zu einer Exception kommen. Die letzten beiden Checks in Z. 40 - 43 sind äquivalent zu denen in Z. 18 - 21. + +\begin{lstlisting} [caption= {Sektion ,,take\_packet and add\_packet''}, label={pc5}] +SECTION("take_packet and add_packet", "[]") { + + PacketInfo* pkt_info_0 = pkt_container->get_empty_packet(); + CHECK(pkt_container->get_number_of_polled_packets() == 0); + CHECK(pkt_container->get_total_number_of_packets() == 1); + + PacketInfo* pkt_info_1 = pkt_container->take_packet(0); + CHECK(pkt_info_0 == pkt_info_1); + CHECK(pkt_info_1 != nullptr); + CHECK(pkt_info_1->get_mbuf() != nullptr); + CHECK(pkt_container->get_packet_at_index(0) == nullptr); + CHECK(pkt_container->get_total_number_of_packets() == 1); + CHECK(pkt_container->get_total_number_of_packets() >= + pkt_container->get_number_of_polled_packets()); + + pkt_container->add_packet(pkt_info_1); + CHECK(pkt_container->get_total_number_of_packets() >= + pkt_container->get_number_of_polled_packets()); + CHECK(pkt_container->get_number_of_polled_packets() == 0); + CHECK(pkt_container->get_total_number_of_packets() == 2); + CHECK(pkt_container->get_packet_at_index(1) == pkt_info_1); + CHECK(pkt_container->get_packet_at_index(0) == nullptr); + +}\end{lstlisting} + +In Codeabschnitt \ref{pc5} wird getestet, ob das Herausnehmen und das Hinzufügen von Paketen funktioniert. Dazu wird zu Beginn wieder ein leeres Paket mittels \texttt{get\_empty\_packet()} erstellt. In den Zeilen 7 - 14 wird zunächst das leere Paket durch die Methode \texttt{take\_paket()} dem \texttt{pkt\_container} entnommen. Dieses wird damit auch aus dem Container entfernt. Danach kommt es zu verschiedenen Checks, wie zum Beispiel, ob das entnommene Paket das selbe ist, welches hinzugefügt wird. In Z. 16 ff. wird dann die Methode \texttt{add\_packet()} getestet. Dafür wird \texttt{pkt\_info\_1} hinzugefügt und die Anzahl der Pakete überprüft. So muss die Anzahl aller Pakete nun zwei betragen. Die Methode \texttt{get\_packet\_at\_index()} muss bei Übergabe des Wertes eins \texttt{pkt\_info\_1} zurückgeben, bei null muss \texttt{nullptr} zurückgegeben werden. + +Die Methode \texttt{drop\_packet()} wird durch die im Codeabschnitt \ref{pc6} dargestellte Sektion getestet. Auch hierfür werden in den Zeilen 4 - 7 die gleichen Initialisierungen wie in der vorherigen Section vorgenommen. Nach dem einmaligen Aufruf der Methode \texttt{drop\_packet()} werden die üblichen Checks durchgeführt. Besonders interessant ist Zeile 13, in der getestet wird, ob die Methode \texttt{get\_packet\_at\_index()} für den Index 0 einen Null-Pointer zurückgibt. In Zeile 15 wird sicher gestellt, dass es beim erneuten Aufruf von \texttt{drop\_packet()} nicht zu einer Exception kommt. Die letzten drei Zeilen können mit Z. 11 - 13 verglichen werden. + +\begin{lstlisting} [caption= {Sektion ,,drop\_packet''} \label{pc6}] +SECTION("drop_packet", "[]") { + + SECTION("default") { + PacketInfo* pkt_info_0 = pkt_container->get_empty_packet(); + CHECK(pkt_container->get_number_of_polled_packets() == 0); + CHECK(pkt_container->get_total_number_of_packets() == 1); + + pkt_container->drop_packet(0); + CHECK(pkt_container->get_total_number_of_packets() >= + pkt_container->get_number_of_polled_packets()); + CHECK(pkt_container->get_number_of_polled_packets() == 0); + CHECK(pkt_container->get_total_number_of_packets() == 1); + CHECK(pkt_container->get_packet_at_index(0) == nullptr); + + CHECK_NOTHROW(pkt_container->drop_packet(0)); + CHECK(pkt_container->get_number_of_polled_packets() == 0); + CHECK(pkt_container->get_total_number_of_packets() == 1); + CHECK(pkt_container->get_packet_at_index(0) == nullptr); + } + +}\end{lstlisting} + +Der letzte beispielhafte Codeabschnitt für den \texttt{PacketContainer} ist der für den Test von\\ \texttt{poll\_packets}. Auch hierfür wird zu Beginn überprüft, ob die Anzahl der Pakete null ist. In Zeile 6 wird der vorzeichenlose 16-bit-Ganzzahlwert \texttt{nb\_pkts\_polled} definiert. Zum Aufruf der Methode \texttt{poll\_packets} kommt es in Z. 7. Anschließend wird erneut geprüft, ob die Anzahl der Pakete richtig ist. Die Zeilen 13 - 17 sind für den Test von \texttt{get\_packet\_at\_index()} mit der Übergabe des Wertes \texttt{nb\_pkts\_polled - 1} wichtig. So darf es auch hier nicht zum Wurf einer Exception kommen und sowohl \texttt{pkt\_info} als auch \texttt{pkt\_info->get\_mbuf()} dürfen kein Null-Pointer sein. Damit soll getestet werden, ob die Variablen, die von den entsprechenden Getter-Methoden zurückgegeben werden, richtig berechnet worden sind. + +\begin{lstlisting} [caption= {Sektion ,,poll\_packets''}, label={pc7}] +SECTION("poll_packets", "[]") { + + CHECK(pkt_container->get_number_of_polled_packets() == 0); + CHECK(pkt_container->get_total_number_of_packets() == 0); + + uint16_t nb_pkts_polled; + pkt_container->poll_packets(nb_pkts_polled); + CHECK(pkt_container->get_number_of_polled_packets() > 0); + CHECK(pkt_container->get_total_number_of_packets() == + pkt_container->get_number_of_polled_packets()); + CHECK(nb_pkts_polled == pkt_container->get_number_of_polled_packets()); + + CHECK_NOTHROW(pkt_container->get_packet_at_index(nb_pkts_polled - 1)); + PacketInfo* pkt_info = + pkt_container->get_packet_at_index(nb_pkts_polled - 1); + CHECK(pkt_info != nullptr); + CHECK(pkt_info->get_mbuf() != nullptr); + +}\end{lstlisting} + +Auf die Sektion ,,poll\_packets'' würde noch eine weitere Sektion mit neun Untersektionen folgen, die das Senden von Paketen testet. Diese Sektion wird hier jedoch nicht näher beschrieben. + +\subsubsection{Beispiel aus der PacketInfo} +... %\todo +\textcolor{red}{toDo für Tobias} + +\subsection{Inspection} +Die Unit-Tests der \texttt{Inspection} können in drei Teile gegliedert werden. +Der erste Teil muss die korrekte Instanziierung überprüfen, der zweite Teil testet die korrekte Identifizierung der einzelnen Angriffe und der dritte und letzte Teil testet die korrekte Erstellung der Statistik und Auswertung der Paketdaten. + +Die korrekte Instanziierung testet, ob aus der Konfiguration korrekte Werte geladen werden, die auch für das System verwendbar sind. Zu diesen Werten zählen beispielsweise nur positive Zahlen. Nicht nur aus den Konfigurationswerten werden während der Instanziierung die Startwerte für die \texttt{Inspection} gebildet. Die Werte der Konfiguration werden aus dem Dummy \texttt{Inspection\_config.json} übergeben und in der Konfiguration eingelesen. + +Im zweiten Teil wird jede Form des Angriffs einzeln auf korrekte Erkennung überprüft. Hierzu werden für SYN-Fin, SYN-FIN-ACK, Zero-Window und Small-Window Angriffe jeweils ein \texttt{PacketContainer} und in diesem \texttt{PacketContainer} wird ein Paket mit den jeweiligen Anforderungen (bestimmte Flags gesetzt oder kleine Window Größe) erstellt. Um eine falsch-richtige und richtig-falsche Erkennung ausschließen zu können, werden daneben auch \texttt{PacketContainer} mit korrekten Paketen ohne diese Anforderungen an die Erkennung erstellt und getestet. Nach jedem Durchlauf eines \texttt{PacketContainers} durch die \texttt{Inspection} muss dieser Container leer sein für richtige Angriffserkennung und nicht leer für eine richtige Nicht-Angriff-Erkennung. + +\begin{lstlisting}[language=C++, caption={Test von SYN-FIN Angriffen in \texttt{Inspection\_test.cpp}}, label=synfininspection] +SECTION("SYN-FIN Attack", "[]") { + PacketInfoIpv4Tcp* pkt_info = pkt_container->get_empty_packet(IPv4TCP); + // create packet with SYN-FIN Flag into packet container + // all values are obsolete except the flags + pkt_info->fill_payloadless_tcp_packet("00:00:00:00:00:00","00:00:00:00:00:00",0,0,0,0,0,0,0b00000011,0); + // packet container to inspection + testInspection.analyze_container(pkt_container); + // SYN-FIN Flag should be detected and packet removed + // Check if packetContainer is empty + CHECK(pkt_container->get_total_number_of_packets() == 0); +} +\end{lstlisting} + +Für das korrekte Erkennen von UDP-/TCP-/ICMP-Flood Attacken werden jeweils mehrere \texttt{PacketContainer} erstellt, die mit mehr Paketen gefüllt werden als der Threshold in der Instanziierung vorgibt. Werden bei dem jeweils zweiten \texttt{PacketContainer} weniger Pakete weitergesendet, ist die Erkennung korrekt erfolgt. Um auch bei diesen Tests eine richtig-falsch und falsch-richtige Erkennung ausschließen zu können, werden daneben noch \texttt{PacketContainer} mit weniger Paketen, als der Threshold zulässt, erstellt und der \texttt{Inspection} übergeben. Diese \texttt{PacketContainer} müssen die Zahl ihrer Pakete beibehalten. + +Die lokale Statistik kann einfach überprüft werden, indem der \texttt{update\_stats()}-Methode feste Werte nach der Instanziierung übergeben werden und die berechneten Werte genau den erwarteten Werten entsprechen müssen. + +\subsection{Treatment} +Die Lines of Code der Unit-Test-Datei der Klasse \texttt{Treatment} belaufen sich auf weit über 1000. Diese vergleichsweise hohe Menge ist unter anderem darauf zurückzuführen, dass in der Datei \texttt{Treatment\_test.cpp} auch die verwendete Hash-Funktion (XXH3) und die verwendete Map (\texttt{dense\_hash\_map}) auf Tauglichkeit für die spätere Verwendung in der Klasse getestet wurde. Desweiteren wurde ein Benchmark erstellt und durchgeführt, welcher die Performance einer \texttt{std::unordered\_map} mit der einer \texttt{dense\_hash\_map} vergleicht. + +Durch die Einführung der Friend-Klasse \texttt{Treatment\_friend} ist es möglich, auf die in der Klasse \texttt{Treatment} auf \texttt{private} gesetzten Attribute und Methoden mit Hilfe von Get-Methoden zuzugreifen. Der Wert des Attributs \texttt{\_s\_timestamp} kann außerdem durch eine Set-Methode neu zugewiesen werden. + +Entgegen dem in der zweiten Phase vorgestellten Entwurf, ist aus Gründen der Kapselung die Methode \texttt{create\_cookie\_secret()} nicht mehr Teil der Klasse \texttt{Treatment}. An dessen Stelle tritt nun die Methode \texttt{get\_random\_64bit\_value()} aus der Klasse \texttt{Rand}, welche die gleiche Funktionalität wie \texttt{create\_cookie\_secret()} aufweist. Um dennoch die bereits zuvor erstellten Unit-Tests ausführen zu können, welche die Methode \texttt{create\_cookie\_secret()} verwenden, gibt es in der Klasse \texttt{Treatment\_friend} eine Methode mit dem ebendiesem Namen, welche allerdings die Methode \texttt{get\_random\_64bit\_value()} der Klasse \texttt{Rand} aufruft. + +\texttt{Treatment\_friend} ist in der Datei \texttt{Treatment\_test.cpp} definiert (vgl. Codeausschnitt \ref{friend}). + +\begin{lstlisting} [caption= {Friend-Klasse \texttt{Treatment\_friend} in der Datei \texttt{Treatment\_test.cpp}}, label = {friend}] +class Treatment_friend{ + + Treatment* treatment = new Treatment(); + + public: + + static void s_increment_timestamp(){ + return Treatment::s_increment_timestamp(); + } + + void treat_packets_to_inside(){ + treatment-> treat_packets_to_inside(); + } + + void treat_packets_to_outside(){ + treatment-> treat_packets_to_outside(); + } + + u_int32_t calc_cookie_hash(u_int8_t _s_timestamp, u_int32_t _extip, u_int32_t _intip, u_int16_t _extport, u_int16_t _intport){ + return treatment->calc_cookie_hash(_s_timestamp, _extip, _intip, _extport, _intport); + } + + bool check_syn_cookie(u_int32_t cookie_value, const Data &d){ + return treatment->check_syn_cookie(cookie_value, d); + } + + u_int64_t create_cookie_secret(){ + return Rand::get_random_64bit_value(); + } + + //Getter + u_int8_t get_s_timestamp(){ + return treatment->_s_timestamp; + } + + u_int64_t get_cookie_secret(){ + return treatment->_cookie_secret; + } + + PacketContainer* get_packet_to_inside(){ + return treatment->_packet_to_inside; + } + + PacketContainer* get_packet_to_outside(){ + return treatment->_packet_to_outside; + } + + google::dense_hash_map get_densemap(){ + return treatment->_densemap; + } + + //Setter + void set_s_timestamp(u_int8_t value){ + treatment->_s_timestamp = value; + } + +};\end{lstlisting} + +Zuvor wurde in der Headerdatei der Klasse \texttt{Treatment} angegeben, dass sie mit \texttt{Treatment\_friend} befreundet ist (vgl. Codeausschnitt \ref{friend2}). + +\begin{lstlisting} [caption= {Deklaration der friend\_klasse \texttt{Treatment\_friend} in der Datei \texttt{Treatment.h}}, label = {friend2}] +... +class Treatment{ + ... + friend class Treatment_friend; +};\end{lstlisting} + +In den folgenden Sektionen werden beispielhaft einzelne, ausgewählte Testfälle erläutert: +\subsubsection{Beispiel: \texttt{check\_syn\_cookie()}} +\begin{lstlisting}[caption= {Methode: \texttt{check\_syn\_cookie()} in \texttt{Treatment.cpp}}, label=checksc] +bool Treatment::check_syn:cookie(u_int32_t cookie_value, const Data\& d){ + // Extract the first 8 bit of the cookie (= timestamp) + u_int8_t cookie_timestamp = cookie_value \& 0x000000FF; + + u_int8_t diff = _s_timestamp - cookie_timestamp; + + if(diff<1){ + // Calculate hash + u_int32_t hash; + + // Case: same time interval + if(diff == 0){ + // calculate expected cookie-hash + hash = calc_cookie_hash(_s_timestamp, d._extip, d._intip, d.extport , d._intport); + hash = hash \& 0xFFFFFF00; + // stuff cookie-hash with 8 bit _s_timestamp + hash |= (u_int8_t) _s_timestamp; + } + if(diff == 1){ + // calculate expected cookie-hash + hash = calc_cookie_hash((_s_timestamp-1), d._extip, d._intip, d.extport, d._intport); + hash = hash \& 0xFFFFFF00; + // stuff cookie-hash with 8 bit _s_timestamp + hash |= (u_int8_t) (_s_timestamp-1); + } + // test whether the cookie is as expected; if so, return true + if(hash == cookie_value){ + return true; + } + } + + //return false, so that treat_packets is able to continue + return false; + }\end{lstlisting} +Die Methode \texttt{check\_syn\_cookie()} (vgl. Codeausschnitt \ref{checksc}) überprüft, ob der empfangene SYN-Cookie im richtigen Zeitintervall am System angekommen ist. Jenes ist zutreffend, sofern der Timestamp des empfangenen Cookie nicht mehr als eine Zeiteinheit von dem aktuellen Timestamp-Wert \texttt{\_s\_timestamp} abweicht. Falls dem so ist, wird überprüft, ob der empfangene Cookie dem erwarteten Cookie entspricht. Die Methode gibt \texttt{true} zurück, falls dies der Fall ist so ist. Sollte dem nicht so sein, so ist der Rückgabewert \texttt{false}. + +\begin{lstlisting} [caption= {Unit-Test für die Methode \texttt{check\_cookie\_secret()} in \texttt{Treatment\_test.cpp}}, label = ut_checksc] +TEST_CASE("check_syn_cookie", "[]"){ + .... + SECTION("check_syn_cookie(): diff==1 with random numbers (without using the PacketDissection)", "[]"){ + //Create a Treatment object + Treatment_friend treat; + + //Generate a random 8-bit-number + u_int8_t ran_num = (u_int8_t) rand(); + + //increment _s_timestamp up to ran_num + for(int i=0; i255>size if u_int8_t)", "[]"){ + Treatment_friend treat; + + u_int8_t count = 0; + + for(int i=0; i<1000; i++){ + CHECK(treat._s_timestamp == count); + treat.s_increment_timestamp(); + count++; + } + } + ... +}\end{lstlisting} +Trotz des vergleichbar kleinen Funktionsumfangs muss getestet werden, wie sich die Variable \texttt{\_s\_timestamp} verhält, wenn sie öfter als 255 mal inkrementiert wurde. Denn der Datentyp der Membervariable \texttt{\_s\_timestamp} ist \texttt{u\_int8\_t} , welcher allerdings lediglich 8 Bit umfasst und somit einen Wertebereich von 0 bis 255 hat. Aus diesem Grund ist das erwartete Verhalten, dass es beim 256. Inkrementieren zum arithmetischen Überlauf kommt. Ab hier beginnt \texttt{\_s\_timestamp} wieder bei 0. %... + +\subsubsection{Beispiel: Benchmark} +Um herauszufinden, welche Map sich am besten für das Softwareprojekt eignet, wurde ein Benchmark erstellt. Dieser vergleicht die Performance einer Unordered-Map mit der einer Dense-Map. + +\begin{lstlisting} [caption= {Benchmark zum Vergleich der Performance einer Unordered-Map und einer Dense-Map}, label = benchmark] +TEST_CASE("Benchmark", "[]"){ + typedef std::unordered_map unordered; + unordered unord; + google::dense_hash_map densemap; + clock_t tu; + clock_t tr; + clock_t td; + densemap.set_empty_key(Data(0,0,0,0)); + + Info flix; + flix._offset = 123; + flix._finseen = 0; + + //-------------------------------------------------------- + + //-------------------------------------------------------- + + long runs = 15; + clock_t uclock [runs] = {}; + clock_t dclock [runs] = {}; + long runner = 600000; + Data arr [runner] = {}; + + for(long r = 0; r < runs; ++r) { + + + for(long i = 0; i < runner; ++i){ + arr[i]._extip = rand(); + arr[i]._intip = rand(); + arr[i]._extport = rand(); + arr[i]._intport = rand(); + } + + auto startu = std::chrono::high_resolution_clock::now(); + tu = clock(); + for(long i = 0; i < runner; ++i){ + unord.emplace(arr[i], flix); + iu->first.extip << std::endl; + } + for(long i = 0; i < runner; ++i){ + unord.find(arr[i-1 % runner]); + unord.find(arr[i]); + unord.find(arr[i+1 % runner]); + unord.find(arr[i+50 % runner]); + iu->first.extip << std::endl; + } + tu = clock() - tu; + auto finishu = std::chrono::high_resolution_clock::now(); + + + auto startd = std::chrono::high_resolution_clock::now(); + td = clock() ; + for(long i = 0; i < runner; ++i) { + densemap.insert(std::pair(arr[i], flix)); + } + for(long i = 0; i < runner; ++i){ + densemap.find(arr[i-1 % runner]); + densemap.find(arr[i]); + densemap.find(arr[i+1 % runner]); + densemap.find(arr[i+50 % runner]); + id->first.extip << std::endl; + } + td = clock() - td; + auto finishd = std::chrono::high_resolution_clock::now(); + + std::chrono::duration elapsedu = finishu - startu; + std::chrono::duration elapsedd = finishd - startd; + dclock[r] = td; + uclock[r] = tu; + BOOST_LOG_TRIVIAL(info) << "Elapsed time of unordered: " << elapsedu. count(); + BOOST_LOG_TRIVIAL(info) << "Elapsed time of dense: " << elapsedd. count(); + + } + int sumd = 0; + int sumu = 0; + for (long x = 0; x < runs; ++x) { + sumd = sumd + dclock[x]; + sumu = sumu + uclock[x]; + } + BOOST_LOG_TRIVIAL(info) << "This is the average clock count of densemap of " << runs << " rounds, of each " << runner << " elements inserted, and " << 4*runner << " elements searched : " << sumd/runs; + BOOST_LOG_TRIVIAL(info) << "This is the average clock count of unordered_map of " << runs << " rounds, of each " << runner << " elements inserted, and " << 4*runner << " elements searched : " << sumu/runs; +} \end{lstlisting} +Das Ergebnis des Benchmarks in Abb. \ref{benchmark} zeigt, dass die Densemap in jedem Durchlauf für die exakt selben Operationen durchschnittlich nur halb so viele CPU-Ticks braucht, wenn die \texttt{std::unordered\_map} als Vergleich verwendet wird. Eine Beispielhafte Ausgabe ist in Codeausschnitt \ref{result_bench} zu erkennen. + +\begin{lstlisting}[caption= {Beispielhaftes Ergebnis des Benchmarks zum Vergleich der Performance einer Unordered-Map und einer Dense-Map}, label = result_bench] + This is the average clock count of densemap of 10 rounds, of each 600000 elements inserted, and 2400000 elements searched : 224847 + This is the average clock count of unordered\_map of 10 rounds, of each 600000 elements inserted, and 2400000 elements searched : 614058 +\end{lstlisting} + +Ebenfalls getestet wurde die Patchmap von 1ykos. Hierbei stellte sich allerdings heraus, dass die Performanz nicht den Erwartungen genügte. + +\subsubsection{Beispiel: Densemap} +Um die Verhaltensweise der Densemap vor deren Nutzung im Code besser kennenzulernen, wurden mehrere Tests geschrieben, welche die Grundfunktionalitäten der Map wie zum Beispiel das Löschen oder Hinzufügen von Werten testen. +\begin{lstlisting}[caption= {Unit-Tests zum Löschen von Elementen in der Densemap}, label = ut_densemap] +TEST_CASE("Map", "[]"){ + ... + SECTION("Densemap: Erase one element whose key is known", "[]"){ + google::dense_hash_map densemap; + + Data empty; + empty._extip = 0; + empty._intip = 0; + empty._extport = 0; + empty._intport = 0; + densemap.set_empty_key(empty); + + Data deleted; + deleted._extip = 0; + deleted._intip = 0; + deleted._extport = 1; + deleted._intport = 1; + densemap.set_deleted_key(deleted); + + Data d1; + d1._extip = 12345; + d1._intip = 12334; + d1._extport = 123; + d1._intport = 1234; + + Info i1; + i1._offset = 3; + i1._finseen = false; + // calculates index over d1, store d1 as first an i1 as second + densemap[d1] = i1; + + Data d2; + d2._extip = 12345; + d2._intip = 12334; + d2._extport = 123; + d2._intport = 1234; + + Info i2; + i2._offset = 3; + i2._finseen = false; + densemap[d2] = i2; + + CHECK(densemap.size() == 2); + densemap.erase(d1); + CHECK(densemap.size() == 1); + densemap.erase(d2); + CHECK(densemap.size() == 0); + } +... +} \end{lstlisting} + +Der obige Unit-Test (vgl. Abb. \ref{ut_densemap}) verdeutlicht, dass direkt nach Anlegen der Densemap die Methode \texttt{set\_empty\_key()} aufgerufen werden muss. Ohne diese Methodenaufruf ist auch nicht der Aufruf weiterer \texttt{dense\_hash\_map}-Methoden möglich. Deshalb wird in den Zeilen 7 bis 12 ein solcher empty-Key angelegt und als Arugment der Methode \texttt{set\_empty\_key()} übergeben. Dieses Argument darf kein Schlüsselwert sein und wird niemals für legitime Einträge in der Map genutzt, der Wert Data(0,0,0,0) ist hierfür besonders gut geeignet, da legitime Verbindungen nie beide IPs auf den Wert null gesetzt haben. + +Zudem wird zum Löschen von Einträgen in der Densemap mit der Methode \texttt{erase()} das Aufrufen der Methode \texttt{set\_deleted\_key()} benötigt. Dieser deleted-Key muss sich vom empty-Key unterscheiden, darf allerdings auch kein legitimer Schlüssel sein. +Da in diesem Unit-Test das Löschen von Elementen getestet werden soll, wird ein Datenobjekt mit dem Namen \texttt{deleted} angelegt, befüllt und der Methode \texttt{set\_deleted\_key()} übergeben. + +Danach wird die Map mit zwei weiteren Einträgen befüllt. Somit muss die Densemap nun die Größe von zwei haben. Nach dem Löschen von \texttt{d1} wird überprüft, ob die Größe der Densemap sich nun auf eins verringert hat. Nachdem der zweite Eintrag gelöscht wurde, wird in Zeile 47 nochmals auf das Übereinstimmen der Densemap-Größe mit dem Wert null getestet. + +\subsection{RandomNumberGenerator} + +In den folgenden Teilkapiteln geht es zunächst um den Zweck und die Funktionsweise des \texttt{RandomNumberGenerators}, worauf beispielhaft einige Unit-Tests vorgestellt werden. + +\subsubsection{Grundlegende Erläuterungen} +Der \texttt{RandomNumberGenerator} (RNG) ist ein Pseudozufallszahlengenerator, der auf dem Xorshift-Algorithmus basiert. Dieser hat das Ziel, auf effiziente Weise möglichst zufällig verteilte Ganzzahlen zu generieren, welche vom Angreifer als Portnummern und IP-Adressen für von ihm ausgehende Pakete verwendet werden können. Der entwickelte RNG enthält jeweils eine Methode zur Berechnung von 16-bit, 32-bit und 64-bit-Zahlen, weshalb die Typen \texttt{uint16\_t}, \texttt{uin32\_t} und \texttt{uint64\_t} verwendet werden. Außerdem enthält er eine Methode, die einen pseudozufälligen \texttt{uint16\_t}-Wert in einem bestimmten Intervall zurückgibt. Das ist nötig, weil wir die registrierten, aber nicht standardisierten Ports 1024 - 49151 verwenden. + +Im Header \texttt{RandomNumberGenerator.h} werden mittels Member Initializer Lists die drei Seeds mit durch \texttt{rand()} generierten zufälligen Werten initialisiert. Wie sich im Code im Codeausschnitt \ref{rng} erkennen lässt, wird dieser Wert daraufhin in den Methoden durch Xor- und Shift-Operationen so verändert, dass die Ergebnisse pseudozufällig sind, also scheinbar zufällig, aber berechenbar. Für weiterführende Erläuterungen und Informationen empfiehlt sich eine Ausarbeitung von George Marsaglia \cite{xorshift}. + +\subsubsection{Einfache Tests} + +Durch die im Codeausschnitt \ref{rngseed} dargestellten Tests soll geprüft werden, ob der Algorithmus bei gleichem Seed auch die gleiche Zahl generiert. Dies ist eine typische Eigenschaft von Pseudozufallszahlengeneratoren. + +\begin{lstlisting}[caption= {Test der Gleichheit der generierten Zahlen bei zwei RNGs mit gleichem Seed}, label = rngseed] +TEST_CASE("random_number_generator_basic", "[]") { + ... + SECTION("Check whether the same numbers are generated with the same seed " + "for 16 bit","[]") { + RandomNumberGenerator xor_shift_1; + RandomNumberGenerator xor_shift_2; + // set the seed to the same value in both RNGs + xor_shift_1._seed_x16 = 30000; + xor_shift_2._seed_x16 = 30000; + u_int16_t test_1_16_bit = xor_shift_1.gen_rdm_16_bit(); + u_int16_t test_2_16_bit = xor_shift_2.gen_rdm_16_bit(); + // check whether the results are the same too + CHECK(test_1_16_bit == test_2_16_bit); + std::cout << std::endl; + } + + ... +}\end{lstlisting} + +Wie oben wurde dieser Test sowohl für die 16-bit-Methode als auch für die 32- und 64-bit-Methode geschrieben. Zunächst werden, wie in Zeile 5 f. zu sehen, zwei Objekte der Klasse \texttt{RandomNumberGenerator} erzeugt. Anschließend wird der mit \texttt{rand()} erzeugte Seed verändert und bei beiden RNGs auf den gleichen Wert gesetzt. In Zeile 10 und 11 wird dann für jedes der beiden RNG-Objekte die Methode zum Generieren einer 16-bit-Zahl aufgerufen. Nun kann in Z. 13 sichergestellt werden, dass die Zahlen \texttt{test\_1\_16\_bit} und \texttt{test\_2\_16\_bit} auch wirklich gleich sind. + +\subsubsection{Test der Verteilung der Zufallszahlen} +Eine wichtige Eigenschaft des entwickelten \texttt{RandomNumberGenerator} muss eine gute Verteilung sein. Das heißt, dass z. B. nicht jede zehnte Zahl zehn mal so oft wie die theoretische Häufigkeit generiert werden darf und alle anderen Zahlen nie vom RNG ausgegeben werden. Dabei wird angenommen, dass die Wahrscheinlichkeit für eine Zahl, dass genau sie generiert wird, im Idealfall für alle Zahlen gleich hoch sein sollte. Schon vor dem Testen steht fest, dass der Algorithmus keine echt zufällige Zahlen generieren kann und dieses Ziel nicht erfüllt, allerdings kann das auch nie der Anspruch an einen Pseudozufallszahlengenerator sein. +Um festzustellen, wie sehr die tatsächlichen Häufigkeiten von den theoretischen abweichen, eignet sich ein Chi-Quadrat-Test, welcher in dem Codeausschnitt \ref{chisquare} zu sehen ist. + +\begin{lstlisting}[caption= {Chi-Quadrat-Test}, label = chisquaretest] +TEST_CASE("RandomNumberGeneratorStatistics", "[]") { + SECTION("ChiSquare16", "[]") { + RandomNumberGenerator xor_shift; + // 65536 = 2 ^ 16 different numbers can be generated + int r = 65536 - 1; + // 1,000,000 numbers are generated + int n = 1000000; + u_int16_t t; + // this array counts how often each number from 0 to r is returned as a + // result + int f[r] = {}; + for (int i = 0; i < r; i++) { + f[i] = 0; + } + for (int i = 1; i < n; i++) { + t = xor_shift.gen_rdm_16_bit(); + f[t]++; + } + double chisquare = 0.0; + for (int i = 0; i < r; i++) { + // chi square is calculated + chisquare = chisquare + ((f[i] - n / r) * (f[i] - n / r) / (n / r)); + } + std::cout << "chi square is: " << chisquare << std::endl; + double k = sqrt(chisquare / (n + chisquare)); + std::cout << "k is: " << k << std::endl; + CHECK(k < 1.0); + } +}\end{lstlisting} + +Es ist zu erkennen, dass zunächst eine obere Intervallgrenze \texttt{r} festgelegt wird. Der RNG generiert demzufolge Zahlen von 0 bis \texttt{r} (Z. 5). Ein Array von 0 bis \texttt{r} wird in Z. 11 - 14 initialisiert und vollständig mit dem Wert 0 belegt. Schließlich werden in Z. 15 - 18 \texttt{n} Zahlen \texttt{t} generiert und durch die Inkrementierung der Werte im Array \texttt{f[]} die Häufigkeiten gezählt. Anschließend wird der Wert \texttt{chisquare} nach der folgenden Formel berechnet: +\begin{align} \label {chisquare} + \chi^2 = \sum \frac{(\text{beobachtete Häufigkeit}-\text{theoretische Häufigkeit})^2}{\text{theoretische Häufigkeit}} +\end{align} +Im geschriebenen Test kann in Z. 22 erkannt werden, dass die beobachtete Häufigkeit mit \texttt{f[i]} und die tatsächliche Häufigkeit mit \texttt{n / r} zu vergleichen ist. + +Ein Problem des Chi-Quadrats ist allerdings die Abhängigkeit von n. Da sich bei Verdopplung von der Häufigkeiten auch das errechnete Ergebnis verdoppeln würde, ist dieser Wert allein nicht aussagekräftig. Aus diesem Grund wird in Z. 25 noch der Kontingenzkoeffizient \texttt{k} nach der Formel \ref{kontingenz} berechnet. +\begin{align} \label{kontingenz} + K = \sqrt{\frac{\chi^2}{n+\chi^2}} +\end{align} +Das hierbei errechnete Ergebnis ist eine Zahl zwischen 0 und K\textsubscript{max} mit K\textsubscript{max}$\approx$1, welche \texttt{n} berücksichtigt. Eine niedriger Kontingenzkoeffizient heißt, dass die generierten Zahlen gut verteilt sind und die tatsächlichen Werte nah an die theoretischen heranreichen. Ein höherer Kontingenzkoeffizient bedeutet, dass vermehrt Zahlen häufiger bzw. seltener als gewollt vorkommen. + +Mit dem oben dargestellten Test hat sich ein \texttt{k} von ca. 0,003 ergeben, was sich als ein sehr gutes Ergebnis bezeichnen lässt. Wird allerdings die Methode \texttt{gen\_rdm\_16\_bit\_in\_interval()} der im Codeausschnitt \ref{rng} dargestellten Datei aufgerufen und dabei die Werte 1024 und 49151 übergeben, so verschlechtert sich der Kontingenzkoeffizient auf einen mittelmäßig guten Wert von ca. 0,59. + +Es lässt sich somit festhalten, dass die Verkleinerung des Intervalls der zurückgegebenen Zahl auf das von validen Portnummern eine Verschlechterung der Zufälligkeit des Algorithmus mit sich bringt, was allerdings kein Problem darstellt. Das ist eine logische Konsequenz aus der Tatsache, dass keine Zahlen außerhalb des Intervalls mehr zurückgegeben werden. + +Die Methode \texttt{gen\_rdm\_32\_bit()} und \texttt{gen\_rdm\_64\_bit()} konnte wegen Fehlern aufgrund zu vieler zu großer Zahlen leider nicht ähnlich durchgeführt werden. Dennoch besteht Grund zu der Annahme, dass die berechneten integer-Werte ähnlich gut verteilt sind. Schließlich muss erneut darauf hingewiesen werden, dass bei beiden Methoden die Effizienz und nicht unbedingt die Qualität des Zufalls an erster Stelle steht. + +\subsubsection{Zeitlicher Vergleich mit rand()} +Da der auf Xorshift basierende \texttt{RandomNumberGenerator} insbesondere aufgrund einer besseren Effizienz als die der Standardfunktion \texttt{rand()} implementiert wurde, ist ein Vergleich beider Zufallszahlengeneratoren von Interesse. Der für einen Vergleich benutze Test wird im Codeausschnitt \ref{rngtime} beispielhaft für die Methode \texttt{gen\_rdm\_32\_bit()} gezeigt. Es wurden ebenfalls zwei äquivalente Sections für die andere Methode geschrieben. Dabei wurde stets darauf geachtet, dass die mit \texttt{rand()} generierten Zahlen das gleiche Intervall und die gleiche Größe wie beim RNG haben. + +\begin{lstlisting}[caption= {Test zum Vergleich der Zeiten vom RandomNumberGenerator mit rand()}, label = rngtime] +TEST_CASE("RandomNumberGeneratorTime", "[]") { + ... + SECTION("TestTime32", "[]") { + double time1 = 0.0, tstart; + tstart = clock(); + RandomNumberGenerator xor_shift; + long n = 10000000; + uint32_t test_value; + for (long i = 0; i < n; i++) { + test_value = xor_shift.gen_rdm_32_bit(); + } + time1 += clock() - tstart; + std::cout << "time needed to generate " << n + << " 32 bit numbers: " << time1 / CLOCKS_PER_SEC << " s" + << std::endl; + CHECK(time1 / CLOCKS_PER_SEC < 1.0); + } + SECTION("TestTime32Rand", "[]") { + double time1 = 0.0, tstart; + tstart = clock(); + long n = 10000000; + uint32_t test_value; + for (long i = 0; i < n; i++) { + test_value = (uint16_t)rand(); + test_value |= (uint16_t)rand() << 16; + } + time1 += clock() - tstart; + std::cout << "time needed to generate " << n + << " 32 bit numbers with rand() and shifting: " + << time1 / CLOCKS_PER_SEC << " s" << std::endl; + CHECK(time1 / CLOCKS_PER_SEC < 1.0); + } +}\end{lstlisting} + +Zum Erfassen der Zeiten wurde \texttt{time.h} inkludiert und in Z. 4 f. sowie 19 f. ein Timer initialisiert und gestartet. In diesem Fall werden 10 Mrd. integer-Werte generiert und kurzzeitig in einer Variable \texttt{test\_value} gespeichert, was in den beiden for-Schleifen zu sehen ist. Es fällt auf, dass in Z. 25 eine Verschiebe-Operation verwendet wird, um sicherzustellen, dass es sich tatsächlich um 32 bit Zufall handelt. In den Zeilen 12 f. und 27 f. wird die Differenz zwischen Start- und Endzeitpunkt berechnet, welche darauf hin in Sekunden ausgegeben wird. Auch bei dem 64-bit-Vergleich wurde darauf geachtet, dass es sich bei der mit \texttt{rand()} generierten Zahl um echte 64 bit Zufall handelt. Das heißt, dass auch dafür mehrmals \texttt{rand()} aufgerufen werden musste. + +Die Ergebnisse sind in der folgenden Tabelle dargestellt. Selbstverständlich unterscheiden sich die Zeiten bei jedem Ausführen des Tests, jedoch lediglich meist nur in hier nicht angegebenen Nachkommastellen. +\begin{longtable}[H]{p{9,5cm}c c} + \toprule + \textbf{Größe des generierten Wertes} & \textbf{Xorshift-RNG} & \textbf{rand()} \\ \toprule \endhead + 16 bit & 0,15 s & 0,17 s \\ + 32 bit & 0,15 s & 0,33 s \\ + 64 bit & 0,15 s & 0,52 s \\ + \bottomrule +\end{longtable} +Wie zu erkennen ist, sind die Unterschiede zwischen dem selbst implementierten RNG und \texttt{rand()} minimal. Es bleibt anzumerken, dass der Unterschiede bei den anderen Größen auch nur deshalb größer wird, weil \texttt{rand()} dort mehrfach aufgerufen wird. Das ist allerdings auch nötig, weil sonst keine 32 bzw. 64 bit echten Zufall erhalten wird. + +Es kann also festgehalten werden, dass sich Xorshift umso mehr lohnt, je größer die generierten Zahlen sein sollen. Auch George Marsaglia empfiehlt den Algorithmus in seinen Ausarbeitungen erst ab einer Größe von 32 bit \cite{xorshift}. + +\subsection{Angreifer} +% Zusätzliche grundlegende Erläuterungen, was der Angreifer überhaupt macht, wozu er da ist +% +% +Die vom \texttt{RandomNumberGenerator} generierten Werte können als IP-Adressen und als Portnummern vom Angreifer verwendet werden. Dieser \texttt{Attacker} wird zum Generieren der SYN-Flut benötigt. Somit ist er vor allem zum Testen dieser Attacke gedacht. Beim Angreifer kommt es nach allen notwendigen Initialisierungen zum Erstellen der benötigten \texttt{PacketContainer} für die Worker-Threads und zum Start sowie dem Ende der Attacke. + + + +\begin{lstlisting} [caption= {Testen des Timers in \texttt{Attacker\_test.cpp}}, label = timer] +TEST_CASE("tsc timer", "[]"){ + const uint64_t MAX_SECONDS = 30; + uint64_t cycles_old = 0; + uint64_t cycles = 0; + uint64_t hz = rte_get_tsc_hz(); + uint64_t seconds = 0; + uint66_t delta_t = 0; + + std::cout << "cycles : " << cycles << "\t" + << "hz : " << hz << "\t" + << "seconds : " << seconds << "\t" << std::endl; + + while (seconds < MAX_SECONDS{ + cycles_old = cycles; + cycles = rte_get_tsc_cycles(); + hz = rte_get_tsc_hz(); + + delta_t =uint64_t(1/hz * (cycles - cycles_old)); + seconds += delta_t; + + std::cout << "cycles : " << cycles << "\t" + << "hz : " << hz << "\t" + << "seconds : " << seconds << "\t" << std::endl; + } +} \end{lstlisting} +Im Testfall ,,tsc timer'' werden die seit dem Teststart vergangenen Sekunden gezählt und ausgegeben (vgl. Codeausschnitt \ref{timer}). Der Code zur Ausgabe befindet sich in den Zeilen 9 bis 11 und 21 bis 23. +Nach 30 Sekunden endet der Test. %Wofür wird dieser Test benötigt? Kein Check bzw. Require +\section{Testen anhand des Testdrehbuchs} + +\section{Sonstige Tests am Testbed} + +\textcolor{red}{toDo} + +\end{document} diff --git a/doc/review_3/chapters/5-anforderungen.pdf b/doc/review_3/chapters/5-anforderungen.pdf new file mode 100644 index 0000000..e764217 Binary files /dev/null and b/doc/review_3/chapters/5-anforderungen.pdf differ diff --git a/doc/review_3/chapters/5-anforderungen.tex b/doc/review_3/chapters/5-anforderungen.tex new file mode 100644 index 0000000..30e5555 --- /dev/null +++ b/doc/review_3/chapters/5-anforderungen.tex @@ -0,0 +1,242 @@ +\documentclass[../review_3.tex]{subfiles} +\graphicspath{{\subfix{../img/}}} +\begin{document} + +\chapter{Überprüfung der Anforderungen}\thispagestyle{fancy} + +In diesem Kapitel wird geklärt, in wie fern das entwickelte System den zu Beginn des Projekts aufgestellten funktionalen und nichtfunktionalen Anforderungen gerecht wird. Dafür wird zunächst kurz auf deren Priorisierung eingangen. Anschließend werden die Anforderungen aufgelistet und kurz erklärt, ob diese erfüllt worden oder nicht. + +\section{Priorisierung der Anforderungen}\thispagestyle{fancy} +Um Anforderungen zu strukturieren und nach Wichtigkeit zu priorisieren, wird in der Regel ein System zur Klassifizierung der Eigenschaften verwendet. Hier wurde eine Priorisierung nach der \textbf{MuSCoW}-Methode vorgenommen: +\begin{description} + \item{\textbf{Must}:} Diese Anforderungen sind unbedingt erforderlich und nicht verhandelbar. Sie sind erfolgskritisch für das Projekt. + \item{\textbf{Should}:} Diese Anforderungen sollten umgesetzt werden, wenn alle Must-Anforderungen trotzdem erfüllt werden können. + \item{\textbf{Could}:} Diese Anforderungen können umgesetzt werden, wenn die Must- und Should-Anforderungen nicht beeinträchtigt werden. Sie haben geringe Relevanz und sind eher ein \glqq Nice to have\grqq. + \item{\textbf{Won't}:} Diese Anforderungen werden im Projekt nicht explizit umgesetzt, werden aber eventuell für die Zukunft vorgemerkt. +\end{description} + +\section{Funktionale Anforderungen} + +Die funktionalen und die nichtfunktionalen werden in einzelnen Unterkapiteln getrennt behandelt. Für beide Arten wird zunächst die Tabelle aus dem Pflichtenheft erneut dargestellt. Nach der Auflistung wird eine Überprüfung zum einen anhand des Testdrehbuch und nach anderen Methoden vorgenommen. + +\subsection{Auflistung der funktionalen Anforderungen} +% Sie beschreiben das Systemverhalten durch Spezifikation der erwarteten Input-/Output-Beziehungen +% --> Was soll umgesetzt werden? + +Funktionale Anforderungen legen konkret fest, was das System können soll. Hier wird unter anderem beschrieben, welche Funktionen das System bieten soll. Die folgende Tabelle zeigt diese funktionalen Anforderungen. + +\begin{longtable} [h] {p{1cm} p{4cm} p{7cm} l} + \toprule + \textbf{ID} & \textbf{Name} & \textbf{Beschreibung} & \textbf{MuSCoW} \\ \midrule \endhead + F01 & Lokale Administration & Das System muss lokal per Command-Line-Interface administriert werden können. & Must \\ + F02 & Angriffsarten & Das System muss die Folgen der aufgelisteten (D)DoS-Angriffe abmildern können: \begin{itemize} \setlength{\parskip}{-2pt} + \item SYN-Flood + \item SYN-FIN Attack + \item SYN-FIN-ACK Attack + \item TCP-Small-Window Attack + \item TCP-Zero-Window Attack + \item UDP-Flood + \end{itemize} + Dabei ist vorausgesetzt, dass das Ziel eines Angriffes eine einzelne Station in einem Netzwerk ist und kein Netzwerk von Stationen. Es sind also direkte Angriffe auf einzelne Server, Router, PC, etc. gemeint. & Must \\ + F03 & Keine zusätzliche Angriffsfläche & Besonders darf das System den unter ,,Angriffsarten'' spezifizierten Angriffen keine zusätzliche Angriffsfläche bieten, d.h. es darf es auch nicht durch Kenntnis der Implementierungsdetails möglich sein, das System mit diesen Angriffen zu umgehen. & Must \\ + F04 & L3/ L4 Protokolle & Das System muss mit gängigen L3/ L4 Protokollen klarkommen. & Must \\ + F05 & Modi & Passend zum festgestellten Angriffsmuster muss das System eine passende Abwehrstrategie auswählen und ausführen. & Must \\ + F06 & Position & Das System soll zwischen dem Internet-Uplink und dem zu schützenden System oder einer Menge von Systemen platziert werden. & Must \\ + F07 & Weiterleiten von Paketen & Das System muss legitime Pakete vom externen Netz zum Zielsystem weiterleiten können. & Must \\ + F08 & Installation und Deinstallation & Das System muss durch Befehle in der Kommandozeile zu installieren und zu deinstallieren sein. Hilfsmittel hierzu sind: Installationsanleitung, Installationsskript, Meson und Ninja. & Must \\ + F09 & Mehrere Angriffe nacheinander und zeitgleich & Das System muss mehreren Angriffen nacheinander und zeitgleich standhalten, hierbei muss berücksichtigt werden, dass auch verschiedene Angriffsarten und Muster zur gleichen Zeit erkannt und abgewehrt werden müssen. & Must \\ + F10 + + & IPv4 & Das System muss mit IPv4-Verkehr zurechtkommen. & Must \\ + F11 & Hardware & Das System soll nicht Geräte- bzw. Rechnerspezifisch sein. & Should \\ + F12 & Zugriff & Der Zugriff auf das lokale System soll per SSH oder Ähnlichem erfolgen, um eine Konfiguration ohne Monitor zu ermöglichen. & Should \\ + F13 & Betrieb & Das System soll auf Dauerbetrieb ohne Neustart ausgelegt sein. & Should \\ + F14 & Privacy & Das System soll keine Informationen aus der Nutzlast der ihm übergebenen Pakete lesen oder verändern. & Should \\ + F15 & Konfiguration & Der Administrator soll die Konfiguration mittels Konfigurationsdateien ändern können. & Can \\ + F16 & Abrufen der Statistik & Der Administrator soll Statistiken über das Verhalten des Systems abrufen können. & Can \\ + F17 & Starten und Stoppen des Systems & Der Administrator soll das System starten und stoppen können. & Can \\ + F18 & Informieren des Anwenders & Der Anwender soll über Angriffe informiert werden. & Can \\ + F19 & Administration über graphische Oberfläche & Das System soll über eine graphische Oberfläche administriert werden können. & Can \\ + F20 & IPv6 & Das System soll mit IPv6-Verkehr zurechtkommen können & Can \\ + F21 & Weitere Angriffsarten & Das System schützt weder vor anderen außer den genannten DoS-Angriffen (siehe F02 \glqq Angriffsarten\grqq)-insbesondere nicht vor denjenigen, welche auf Anwendungsebene agieren-, noch vor anderen Arten von Cyber-Attacken, die nicht mit DoS in Verbindung stehen. + So bleibt ein Intrusion Detection System weiterhin unerlässlich. & Won't \\ + F22 & Anzahl der zu schützenden Systeme & Das System wird nicht mehr als einen Server, Router, PC, etc. vor Angriffen schützen. & Won't \\ + F23 & Fehler des Benutzers & Das System soll nicht vor Fehlern geschützt sein, da es durch eine nutzungsberechtigte Person am System ausgeführt wird. So sollen beispielsweise Gefährdungen, welche aus fahrlässigem Umgang des Administrators mit sicherheitsrelevanten Softwareupdates resultieren, durch das zu entwickelnde System nicht abgewehrt werden. & Won't \\ + F24 & Softwareupdates & Das System soll keine Softwareupdates erhalten und soll nicht gewartet werden. & Won't \\ + F25 & Router-/Firewall-Ersatz & Das System soll nicht als Router oder als Firewall-Ersatz verwendet werden. & Won't \\ + F26 & Hardware-Ausfälle & Das System soll keine Hardwareausfälle (zum Beispiel auf den Links) beheben. & Won't \\ + F27 & Fehler in Fremdsoftware & Das System kann nicht den Schutz des Servers bei Fehlern in Fremdsoftware garantieren. & Won't \\ \bottomrule +\end{longtable} %fehlt: Pakete weiterleiten, Pakete verwerfen + +\subsection{Überprüfung der funktionalen Anforderungen} +Jede einzelne funktionale Anforderungen wird hier unter einer eigenen kleinen Überschrift überprüft. Die Reihenfolge ist dabei die gleiche wie in der oben stehenden Tabelle. + +\subsubsection{F01: Lokale Administration} +% Leon fragen, Screenshot? %cli2 könnte laufen +Diese Muss-Anforderung wurde erfüllt. Ein Command-Line-Interace wurde mithilfe von Konventionen wie ,,Human first design'' oder der Forderung, dass das Programm bei der Benutzung unterstützt. So soll es beispielsweise vorschlagen, was als Nächstes gemacht werden kann. + +Mit Hilfe der Kommandos ,,help'' oder ,,h'' bekommt der Nutzer alle gültigen Eingaben angezeigt. + +Die Eingabe von ,,exit'' + +\subsubsection{F02: Angriffsarten} +% Test Nr. 6 (Überprüfung Widerstandsfähigkeit der Miditation-Box) +% Das System muss die Folgen der aufgelisteten (D)DoS-Angriffe abmildern können: +Das System kann alle geforderten (D)DoS Angriffe abmildern oder gänzlich verhindern. SYN-FIN, SYN-FIN-ACK sowie Zero Window und Small Window können vollständig erkannt und abgewehrt werden. Die UDP-/TCP- und ICMP-Flood können in ihrer Form abgemildert werden. + +\subsubsection{F03: Keine zusätzliche Angriffsfläche} +% Test Nr. 4 (Überprüfung Abwehrmaßnahmen) +% Besonders darf das System den unter ,,Angriffsarten'' spezifizierten Angriffen keine zusätzliche Angriffsfläche bieten, d.h. es darf es auch nicht durch Kenntnis der Implementierungsdetails möglich sein, das System mit diesen Angriffen zu umgehen. +Durch unvollständigkeit des Systems kann diese Anforderung nicht vollständig überprüft werden jedoch ist diese soweit das System implementiert wurde beständig. + +\subsubsection{F04: L3/L4 Protokolle} +Sowohl Protokoll L3, zuständig für die Vermittlung von Daten über einzelne Verbindungsanschnitte und Netzwerkknoten und Adressierung der Kommunikationspartner, als auch Protokoll L4, das die Quell- und Zieladressen und weitere Informationen des Pakets enthält, werden vom System akzeptiert und verwendet. + +\subsubsection{F05: Modi} +% Test Nr. 3 (Kalibirierung von (D)DOS Erkennung) +% Passend zum festgestellten Angriffsmuster muss das System eine passende Abwehrstrategie auswählen und ausführen. +Das System erkennt und unterscheidet verschiedene Angriffsmethoden und errechnet selbst die passende Abwehrstrategie sowie eine Anpassung der Durchlassrate von Paketen. Die Abwehrstrategie wird von jedem Worker-Thread an seinen eigenen Verkehr angepasst. + +\subsubsection{F06: Position} +In Kapitel 2 ist auf Seite \pageref{fig:Versuchsaufbau} in Abbildung \ref{fig:Versuchsaufbau} der Versuchsaufbau zu sehen. Das System wurde im Labor im Zusebau der TU Ilmenau auch tatsächlich in dieser Reihenfolge aufgebaut. Damit ist diese Anforderung erfüllt. + +\subsubsection{F07: Weiterleiten von Paketen} +Zur Überprüfung dieser Anforderung ist der erste Test des in der Planungs- und Entwurfsphase geschriebenen Testdrehbuchs gedacht. Für den Test der Paketweiterleitung werden zunächst Pakete mit DPDK von einem Port der Netzwerkkarte entgegengenommen und auf den andern Port weitergegeben. Danach wurde begonnen, einzelne Ping-Anfragen vom äußeren System über die Mitigation-Box zum Server laufen zu lassen. Im Anschluss wurde ein Lasttest durchgeführt. Diese Tests haben ergeben, dass... % to do! + +\subsubsection{F08: Installation und Deinstallation} +Das System wird mit einer Installationsanleitung und Installationsskripten ausgeliefert. Das Installationsskript für abhängige und notwendige Systemeinstellungen und Programme installiert alle notwendigen zusätzlichen Programme und Bibiliotheken und nimmt alle notwendigen Systemeinstellungen vor, soweit möglich. Bei Fehlern wird dem Benutzer ein Hinweis zur Lösung des Problems angezeigt. Die Installation kann auch selbst mit der Installationsanleitung vorgenommen werden, die einzelnen Schritte sind in eigenen Unterkapiteln genauer erklärt. +Für einen lokalen Bau der Software kann \texttt{Meson} und \texttt{Ninja} verwendet werden, deren Benutzung in der die Installationsanleitung fortführenden Seite \texttt{Usage} erklärt wird. Dort ist auch ein kurzer Einstieg in die Benutzung von \texttt{AEGIS} erklärt. +Zur systemweiten Installation von \texttt{AEGIS} kann ebenfalls \texttt{Meson} genutzt werden, das durch ein Installationsskript erweitert wurde. +Das gesamte Programm und zusätzlich installierten Programme können mit einem Deinstallationsskript wieder vom System gelöscht werden. + +Diese Anforderung ist erfüllt. + +\subsubsection{F09: Mehrere Angriffe nacheinander und zeitgleich} +% Test Nr. 4 (Überprüfung Abwehrmaßnahmen) +Das System ist darauf spezifiziert, einzelne Angriffe und kombinierte Angriffe zu erkennen. Kombinierte Angriffe aus unterschiedlichen angriffsmethoden können erkannt und abgewehrt werden. Ein vollständiger Test konnte mit dem bisherigen Stand des Projektes noch nicht durchgeführt werden. + +\subsubsection{F10: IPv4} +% Das System muss mit IPv4-Verkehr zurechtkommen. +Das System kann IPv4 Verkehr vollständig verarbeiten, verwalten und stößt auf keine Probleme dabei. Diese Anforderung ist erfüllt. + +\subsubsection{F11: Hardware} +Diese Should-Anforderung wurde mit dem entwickelten System nicht erfüllt. Die Software läuft im derzeitigen Zustand ausschließlich auf dem Testbed im Rechenlabor. Durch kleine Anpassung kann die Nutzung auf alternativer Hardware allerdings ermöglicht werden. + +Eine zusätzliche Beschränkung der Hardware besteht in der Nutzung von DPDK und Kernel Bypässen die von der verbauten Netzwerkkarte der Hardware unterstützt werden muss. + +\subsubsection{F12: Zugriff} +Es ist ein ssh-Zugriff auf das System möglich. Die Konfiguration ermöglicht die Authentifizierung nicht mit einem Passwort möglich ist. Mithilfe des Befehls \texttt{ssh-keygen} kann ein Schlüsselpaar generiert werden und ein Public-Key ist in einer Textdatei zu finden. Weiterhin ist es möglich, zu überprüfen, welche anderen Teammitglieder derzeit auf diese Art mit dem System verbunden sind. + +\subsubsection{F13: Betrieb} +% Das System soll auf Dauerbetrieb ohne Neustart ausgelegt sein. +Zum Zeitpunkt des Projektendes konnte das System in kurzer Zeit betriebsfähig gehalten werden jedoch kein Dauerbetrieb mit Dauerbelastung ausreichend getestet werden. Neustarts zwischen starten und stoppen des Systems waren nicht notwendig. +Die Anforderung wurde teilweise erfüllt. + +\subsubsection{F14: Privacy} +% Das System soll keine Informationen aus der Nutzlast der ihm übergebenen Pakete lesen oder verändern. +Dem System ist es möglich auf Paketdaten zuzugreifen und zu lesen. Die Implementierung wurde jedoch genau darauf eingeschränkt, nur Teile der Paketdaten, die zur Erfüllung der Systemaufgaben notwendig sind, zu lesen und gegebenfalls zu verändern. Von dem System kann dadurch keine Nutzlast von Paketen gelesen oder verändert werden. Wird ein Paket als (D)DoS Attacke erkannt wird das Paket mitsamt seiner Nutzlast gelöscht. Ein Eingriff in Privatsphäre oder Analyse von Benutzerdaten außerhalb der systemrelevanten Spezifikationen erfolgt zu keiner Zeit. + +Damit ist diese Anforderung erfüllt. + +\subsubsection{F15: Konfiguration} +Der Administrator kann die Einstellungen von \texttt{AEGIS} durch zwei Konfigurationsdateien anpassen. Innerhalb der \texttt{meson\_options.txt} Datei kann der Bau von Unit Tests und der Dokumentation ein- und ausgeschalten werden. In der Datei \texttt{config.json} können verschiedene Einstellungen wie die zu verwendenden Systemkerne und Durchlassraten eingestellt werden. + +\subsubsection{F16: Abrufen der Statistiken} +% Der Administrator soll Statistiken über das Verhalten des Systems abrufen können. +Die globale Statistik mit Informationen über den Datenverkehr soll innerhalb des CLI abgerufen und angezeigt werden. Die Implementierung ist jedoch nicht bis dahin entwickelt. Die Anforderung ist nicht erfüllt. + +\subsubsection{F17: Starten und Stoppen des Systems} +% Der Administrator soll das System starten und stoppen können. +Das Programm \texttt{AEGIS} kann über ein Terminal vom Benutzer gestartet und gestoppt werden. Das CLI unterstützt den Nutzer dabei mit Hinweisen. Die Anforderung ist erfüllt. + +\subsubsection{F18: Informieren über graphische Oberfläche} +%GUI +Für die Präsentation ist eine graphische Oberfläche in Arbeit. Diese ist bisher aber nicht fertig und erfüllt die Anforderung noch nicht. + +\subsubsection{F19: Administration über grafische Oberfäche} +Diese Can-Anforderung wurde aus Zeitgründen nicht umgesetzt. Die Administration erfolgt stattdessen über das in F01 beschriebene CLI. + +\subsubsection{F20: IPv6} +%schon einzelne Dinge da, auf die darauf aufgebaut werden kann, oder? +Das System ist vorerst auf IPv4 ausgelegt und funktionsfähig aber schon darauf ausgerichtet auch IPv6 zu unterstützen. Die Anforderung ist nicht erfüllt kann aber später ohne große Abänderung am gegebenen System hinzugefügt werden. + +\subsubsection{F21: Weitere Angriffsarten} +% Bei den Anforderungen F21 - F27 handelt es sich solche, die mit Won't priorisiert wurden. Das heißt, dass sie nicht erfüllt werden dürfen. Bei dem während des Softwareprojekts entwickelten System gibt es auch keine Anzeichen dafür, dass es ungewollter Weise vor anderen Gefahren schützt. +% Das System schützt weder vor anderen außer den genannten DoS-Angriffen (siehe F02 \glqq Angriffsarten\grqq)-insbesondere nicht vor denjenigen, welche auf Anwendungsebene agieren-, noch vor anderen Arten von Cyber-Attacken, die nicht mit DoS in Verbindung stehen. So bleibt ein Intrusion Detection System weiterhin unerlässlich. +Das System schützt nur vor den in F02 angegebenen (D)DoS Angriffen und stellt keinen Schutz gegen weitere mögliche Angriffe. Die Verwendung weiterer Sicherheitsmechanismen ist weiterhin unerlässlich. + +\subsubsection{F22: Anzahl der zu schützende Systeme} +Das System schützt nur ein einzelnes System. Mit moderaten Änderungen kann die Software aber auf anderen Servern etc. installiert werden und dadurch mehrere schützen. Aus hardwareseitigen Gründen wurde dies aber auch nicht getestet. + +\subsubsection{F23: Fehler des Benutzers} +Auch nach der Entwicklung kann festgehalten werden, dass das System durch einen kompetenten Administrator installiert und genutzt werden muss. +Fehler durch den Benutzer oder falsche Verwendung können vom System selbst nicht behoben werden. + +\subsubsection{F24: Softwareupdates} +Das System, das während des Projekts entwickelt wurde, wird nach Abschluss der Lehrveranstaltung nicht direkt weiterentwickelt. Während der Erstellung dieses Dokuments wird allerdings davon ausgegangen, dass einige Studierende der Gruppe auch nach Abschluss der Lehrveranstaltung evtl. noch an dem System weiterarbeiten. Features, die möglicherweise dadurch noch hinzugefügt werden, können allerdings nicht als Softwareupdates angesehen werden. + +\subsubsection{F25: Router-/Firewall-Ersatz} +Auch diese Won't-Anforderung wurde nicht erfüllt. Eine Firewall oder vergleichbare schützende Systeme bleiben nach wie vor unerlässlich. + +\subsubsection{F26: Hardware-Ausfälle} +Aegis kann keine Hardwareausfälle beheben. + +\subsubsection{F27: Fehler in Fremdsoftware} +Ebenso gibt keine Möglichkeit das System vor Fehlern einer anderen Software oder Softwareabhängigkeit zu schützen. Der Benutzer ist für die korrekte Installation und Konfiguration aller notwendigen Fremdsoftware und Abhängigkeiten verantwortlich. Die Installations- und Deinstallationsskripte, sowie Dokumentation bieten dem Benutzer Hilfe diese Fehler zu umgehen. + + +\section{Nichtfunktionale Anforderungen} +% beschreiben qualitative, aber auch quantitative Faktoren des zu entwickelnden Zielsystems +% --> Wie sollen die funktionalen Anforderungen umgesetzt werden? + +Auch hier wird genau wie bei den funktionalen Anforderungen vorgegangen. + +\subsection{Auflistung der nichtfunktionalen Anforderungen} + +Nichtfunktionale Anforderungen gehen über die funktionalen Anforderungen hinaus und beschreiben, wie gut das System eine Funktion erfüllt. Hier sind zum Beispiel Messgrößen enthalten, die das System einhalten soll. Im folgenden werden diese nichtfunktionalen Anforderungen beschrieben. + +\begin{longtable}[ht] { p{1cm} p{4cm} p{7cm} l } + \toprule + \textbf{ID} & \textbf{Name} & \textbf{Beschreibung} & \textbf{MuSCoW} \\ \midrule \endhead + NF01 & Betriebssystem & Die entwickelte Software muss auf einer Ubuntu 20.04 LTS Installation laufen. DPDK muss in Version 20.11.1 vorliegen und alle Abhängigkeiten erfüllt sein. & Must \\ + NF02 & Verfügbarkeit & Die Verfügbarkeit des Systems soll bei mindestens 98\% liegen. Verfügbarkeit heißt hier, dass das System in der Lage ist, auf legitime Verbindungsanfragen innerhalb von 10 ms zu reagieren. & Must \\ + NF03 & Datenrate & Die anvisierte Datenrate, welche vom externen Netz durch das zu entwickelnde System fließt, muss bei mindestens 20 Gbit/s liegen. & Must \\ + NF04 & Paketrate & Die anvisierte Paketrate, welche vom zu entwickelnden System verarbeitet werden muss, muss bei mindestens 30 Mpps liegen. & Must \\ + NF05 & Transparenz & Der Anwender soll das Gefühl haben, dass die Middlebox nicht vorhanden ist. & Should \\ + NF06 & Abwehrrate SYN-Flood & Die für die Angriffe anvisierten Abwehrraten sind für die SYN-Flood, SYN-FIN und SYN-FIN-ACK jeweils 100\%. & Should \\ + NF07 & False Positive & Der maximale Anteil an fälschlicherweise nicht herausgefiltertem und nicht verworfenem illegitimen Traffic, bezogen auf das Aufkommen an legitimem Traffic, soll 10\% im Angriffsfall und 5\% im Nicht-Angriffsfall nicht überschreiten. & Should \\ + NF08 & False Negative & Der maximale Anteil an fälschlicherweise nicht verworfenem bösartigem Traffic, bezogen auf das Gesamtaufkommen an bösartigem Traffic, soll 5\% nicht überschreiten. & Should \\ + NF09 & Round Trip Time & Die Software soll die Round-Trip-Time eines Pakets um nicht mehr als 10 ms erhöhen. & Should \\ \bottomrule +\end{longtable} + +\subsection{Überprüfung der nichtfunktionalen Anforderungen} +Auch bei den nichtfunktionalen Anforderungen werden jeweils einzelne Unterkapitel genutzt. + +\subsubsection{NF01: Betriebssystem} +Die genannte Software, also Ubuntu 20.04 LTS und DPDK 20.11.1, wurde von allen Teammitgliedern installiert. Schließlich wurde auch nur mit diesen Versionen des Betriebssystems bzw. Frameworks entwickelt und getestet. Es kann also dokumentiert werden, dass das System unter diesen Voraussetzungen wie bei den anderen Tests beschrieben funktioniert. Es kann keine Aussage darüber getroffen werden, inwiefern das System unter anderen Versionen funktioniert. + +\subsubsection{NF02: Verfügbarkeit} +% Test Nr. 5 (Analyse Effekt auf Verbindungen) + +\subsubsection{NF03: Datenrate} +% Test Nr. 8 (allgemeine Bestimmungen) + +\subsubsection{NF04: Paketrate} +% Test Nr. 7 (Ermittlung maximaler Paketrate) + +\subsubsection{NF05: Transparenz} +% Test Nr. 5 (Analyse Effekt auf Verbindungen) + +\subsubsection{NF06: Abwehrrate SYN-Flood} +% Test Nr. 7 (Ermittlung maximaler Paketrate) + +\subsubsection{NF07: False Positive} +% Test Nr. 5 (Analyse Effekt auf Verbindungen) + +\subsubsection{NF08: False Negative} + + +\subsubsection{NF09: Round Trip Time} +% Test Nr. 5 (Analyse Effekt auf Verbindungen) + +\end{document} diff --git a/doc/review_3/chapters/6-bugreview.tex b/doc/review_3/chapters/6-bugreview.tex new file mode 100644 index 0000000..5c7e009 --- /dev/null +++ b/doc/review_3/chapters/6-bugreview.tex @@ -0,0 +1,36 @@ +\documentclass[../review_3.tex]{subfiles} +\graphicspath{{\subfix{../img/}}} +\begin{document} + +\chapter{Bug-Review}\thispagestyle{fancy} + +In diesem Bug-Review werden verschiedene Fehler gesammelt. Dabei wird auf die Datei, in der sie auftreten, auf eine Beschreibung und eine Kategorisierung eingegangen. Major Bugs sind versionsverhindernd, critical bugs können auch zur Arbeitsunfähigkeit anderer führen und minor bugs haben eine geringe Auswirkung und somit eine niedrige Priorität. + +\begin{longtable} [h] {p{3cm} p{8.7cm} l} + \toprule + \textbf{Datei} & \textbf{Beschreibung} & \textbf{Kategorie} \\ \endhead + \midrule + + PacketInfoIpv4Icmp & + Wenn von einem Paket die Header extrahiert werden soll (fill\_info), wird zuerst der mbuf in der \texttt{PacketInfo} verlinkt, dann IP version (IPv4) und Layer 4 Protokol (ICMP) ermittelt. Danach wird die \texttt{PaketInfo} in die entsprechende protokolspezifische \texttt{PacketInfo} gecastet. Auf dieser verwandelten \texttt{PacketInfo} wird set\_ip\_hdr ausgeführt und es kommt zum segmentation fault, der im Abbruch des Threads mündet. & + critical bug \\ + + %Ist dieser Bug nun gelöst? + %PacketInfo & + %Wenn in der internen IP-Pakete-Weiterleitungstabelle Änderungen vorgenommen werden, kommt %es beim nächsten Paket im Zuge der Headerextraction zu einem segmentation fault. & + %minor bug \\ + + Initializer & + Die maximale Anzahl an Threads ist 16. Das stellt kein Problem dar, weil nur 12 Threads benötigt werden. mlx5\_pci: port 1 empty mbuf pool; mlx5\_pci: port 1 Rx queue allocation failed: Cannot allocate memory. + + Dieser Fehler tritt beim Ausführen von rte\_eth\_dev\_start(port) auf. Womöglich handelt es sich dabei um ein mempool problem. + & minor bug \\ + + \bottomrule +\end{longtable} + +Schon während der Implementierungsphase wurden Bugs wenn möglich behoben. An den in dieser Tabelle genannten Problemen wird noch gearbeitet. Die Fehlerliste wird auch in der Validierungsphase laufend erweitert, sodass im Idealfall für das abschließende Review-Dokument eine vollständige Bug-Statistik erstellt werden kann. + +\textcolor{red}{toDo} + +\end{document} diff --git a/doc/review_3/chapters/7-statistiken.tex b/doc/review_3/chapters/7-statistiken.tex new file mode 100644 index 0000000..b689fec --- /dev/null +++ b/doc/review_3/chapters/7-statistiken.tex @@ -0,0 +1,290 @@ +\documentclass[../review_3.tex]{subfiles} +\graphicspath{{\subfix{../img/}}} +\begin{document} + +\chapter{Softwaremetriken und Statistiken}\thispagestyle{fancy} +Der Zweck von Softwaremetriken besteht in der ,,Definition von Software-Kenngrößen und Entwicklungsprozessen'' und der ,,Abschätzung des Prozess- und Kostenaufwands oder dem Qualitätsmanagement''\cite{swmetriken}. + +Da das Reviewdokument noch vor Projektende fertiggestellt werden musste, sind die Daten, auf denen dieses Kapitel basiert, vom 10.07.2021. +\section{Benennungs- und Programmierkonventionen} +Während der Meetings wurde sich auf zahlreiche Konventionen geeinigt. Diese wurden dann wiederum sowohl in den einzelnen Meetingprotokollen als auch in einem eigenen Wikieintrag festgehalten. + +Unter Programmierkonventionen werden ,,Vorgaben für Programmierer über die Gestaltung von Programmen''\cite{progkonv} verstanden. Sie zeigen auf, wie der Code sowohl formal als auch strukturell gestaltet sein soll. Diese Konventionen sollen zur Verbesserung der Softwarequalität führen, was sich unter anderem in der durch die Konventionen gesteigerten Verständlichkeit und Änderungsfreundlichkeit des Codes zeigt. + +\subsection{C/C++ und UML} +Das System wird in der Programmiersprache C++ entwickelt. Um dieses System zu entwerfen, wurden verschiedene Modelle und Diagramme im Rahmen des Softwareprojektes erstellt (z.B. Klassendiagramme, Aktivitätsdiagramme, Sequenzdiagramme). Diese Diagramme wurden mithilfe der Unified Modeling Language (UML) entwickelt. Die UML gibt bereits einige Richtlinien vor, wie zum Beispiel die grafische Notation oder die Bezeichner für die bei einer Modellierung wichtiger Begriffe. + +\subsubsection{Namenskonventionen} +Im Folgenden werden die Namenskonventionen im vorliegenden Softwareprojekt aufgezeigt und mit kurzen Beispielen unterlegt: + +\textbf{Klassen}, \textbf{Enumerations} und \textbf{Pakete} werden entsprechend der UpperCamelCase-Schreibweise dargestellt. Allerdings muss darauf geachtet werden, dass z. B. bei Akronymen nicht mehrere Buchstaben hintereinander in Großbuchstaben geschrieben werden. +Dementsprechend entspricht der Name \texttt{ConfigurationManagement} den Konventionen. Allerdings ist \texttt{TCPTreatment} syntaktisch nicht korrekt, da es den Vorgaben nicht entspricht, der Begriff \texttt{TcpTreatment} hingegen wäre syntaktisch korrekt. + +Für \textbf{Methoden} werden ausschließlich Kleinbuchstaben verwendet. Sollte sich der Methodenname aus mehreren Worten zusammensetzen, kann dies über einen Unterstrich erfolgen. Beispiele für richtig benannte Methoden sind demzufolge \texttt{send\_packets\_to\_port()} und \texttt{check\_syn\_cookie()}. Es bleibt anzumerken, dass statische Methoden durch ein \texttt{s\_} vor dem eigentlichen Namen gekennzeichnet werden, wie bei \texttt{s\_increment\_timestamp()}. + +Für \textbf{Variablen} gelten die gleichen Konventionen wie für Methoden, sodass \texttt{packet\_inside} als Bespiel dienen kann. Zusätzlich soll ein Unterstrich vor dem Namen darauf hinweisen, dass es sich um eine Membervariable handelt, wie z. B. bei \texttt{\_cookie\_secret}. Statische Membervariablen beginnen somit mit \texttt{\_s\_}, wie bei \texttt{\_s\_timestamp}. + +Auch bei \textbf{Objekten} gilt, dass nur Kleinbuchstaben verwendet werden sollen und mehrere Worte durch einem Unterstrich verbunden werden, wie in \texttt{nic\_man}. + +\subsubsection{Formatierungs-Richtlinien} +Die Formatierungsrichtlinien legen unter anderem fest, dass nur ASCII-Zeichen, also zum Beispiel keine Umlaute oder ß, verwendet werden dürfen. Die Einrückung im Code beträgt vier Leerzeichen. Zudem sollen ,,dauerhaft'' geschweifte Klammern verwendet werden. Das heißt zum Beispiel, dass auch geschweifte Klammern einzeiliger if-Blöcken verwendet werden. Nach Methoden- und Klassennamen (oder Ähnlichem) stehen öffnende geschweifte Klammer. Hier soll kein Zeilenumbruch entstehen. Dies zeigt der Codeausschnitt \ref{klammern} beispielhaft. +\begin{lstlisting} [caption= {Formatierungsrichtlinie: Setzen von Klammern}, label = {klammern}] +int i = rand(); + +// It should be like this: +if(i%2 == 0){ + ... +} + +//It should be not like this: +if(i%2 != 0) +{ + ... +} + +//And not like this: +if(i>100) + ...\end{lstlisting} + +\subsubsection{Kommentare} +Während in den Kommentaren das Festschreiben von ToDo's erlaubt ist, dürfen hier keine Fragen gestellt werden. In den Headerdateien soll die Doxygen-Syntax für Kommentare verwendet werden, um hiermit unter anderem die Entwicklerdokumentation zu generieren. Vor einem Block können mehrzeilige Kommentare verwendet werden. Derjenige Teil, der mit \texttt{*} beginnt, muss jeweils nochmals mit einem Leerzeichen eingerückt werden. +\begin{lstlisting} [caption= {Beispiel für einen mehrzeiligen Doxygen-Kommentar}] + /** +* Full description +* +* @brief short description +* @param msg message that is printed to the console +*/ +void log(const string* msg){ + std::cout << msg << std::endl; +} \end{lstlisting} +Einzeilige Kommentare werden dadurch erzeugt, indem hinter einer Codezeile \texttt{///<} eingegeben wird. Diese Art von Kommentaren wird von Doxygen als Kurzbeschreibung verwendet. +\begin{lstlisting} [caption= {Beispiel für einen einzeiligen Doxygen-Kommentar}] +string firstname; //< first name of person +string lastname; //< last name of person +\end{lstlisting} + +Es wurde sich auf die Verwendung folgender \textbf{Commands} geeinigt: + +Bei \texttt{@brief} lässt sich sofort erkennen, dass es sich um eine \textbf{Kurzbeschreibung} handelt. + +\texttt{@param} leitet hingegen die Beschreibung eines \textbf{Parameters}, der in eine Methode ein- oder von einer Methode ausgegeben wird, ein. \texttt{@param[in]} steht vor der Beschreibung eines \textbf{Eingabeparameters} und \texttt{@param[out]} vor einem \textbf{Ausgabeparameter}. Bei diesem handelt es sich um einen Parameter, der im C-Style einer Funktion mit Call-by-reference übergeben wird, damit er mit Werten gefüllt wird. Zur Beschreibung von Parametern, die sowohl ein- als auch ausgegeben werden, kann \texttt{@param[in, out]} verwendet werden. + +Der Command \texttt{@return} hilft bei der Beschreibung eines \textbf{Rückgabewertes} und \texttt{@file} zur Erklärung des \textbf{Zwecks} einer Datei, also z. B. einer Klasse oder eines structs. + +\subsubsection{Source-Dateien} +Pro Paket (vgl. Abb. \ref{fig:dospaketdiagramm}) wird ein Ordner in \texttt{source/} angelegt, der den gleichen Namen wie das Paket erhält. Alle zu diesem Paket zugehörigen Klassen befinden sich dann wiederum in diesem Ordner. Pro cpp-Klasse soll es eine eigene .cpp-Datei geben. Die dazugehörige Header-Datei wird mit \texttt{\#include } inkludiert. Im ganzen Projekt gibt es eine \texttt{main.cpp}-Datei in \texttt{source/} mit der main-Routine. + +\subsubsection{Header-Dateien} +Pro Source-Datei existiert eine Header-Datei. Jede dieser Header-Dateien wird mit \texttt{.hpp} benannt. Am Anfang der Datei wird \texttt{\#pragma once} verwendet. Externe Dateien werden mit \texttt{\#include } inkludiert. + +\subsection{Gitlab} +Im Softwareprojekt wird die GitLab zur Versionsverwaltung genutzt. Diese Webanwendung basiert auf Git. + +\subsubsection{Git} +Außer in Präsentationen und Review-Dokumenten werden in Git nur ASCII-Zeichen verwendet. Die \textbf{Sprache} ist auch hier grundsätzlich Englisch, wobei diese Festlegung auch bei den Präsentationen und Review-Dokumenten abweicht. + +Die \textbf{Branchnamen} sind klein geschrieben. Die Worte in diesem werden mit Unterstrich (,,\_'') verbunden. Dieselbe Regelungen gelten bei \textbf{Ordner- und Dateinamen}. + +\subsubsection{Issue} +Grundsätzlich werden die Issues in Englisch benannt. Lediglich deutsche ,,Eigennamen'' werden auf Deutsch geschrieben (Beispiel: Pflichtenheft). +Die Issues werden klein und im Imperativ geschrieben. Leerzeichen sind erlaubt. + +Wenn Issues nicht nur einer sondern mehreren Personen zugeordnet werden sollen, wird dem eigentlichen Issue-Namen mit ,,@'' die Namen der zuständigen Teammitgliedern angehängt. Das heißt, der Issue-Name weist folgende Struktur auf: \texttt{ @ }). + +Mit \texttt{@all} ist das Issue für alle Teammitglieder für die Bearbeitung offen. Derjenige, der mit der Bearbeitung dieser Aufgabe beginnt, löscht ,,@all'' trägt seinen eigenen Vornamen mit ,,@'' in den Issue-Namen ein. + +\subsubsection{Label} +In diesem Projekt werden die Labels grundsätzlich für zwei verschiedene Kategorien von Aufgaben verwendet verwendet: Zum einen für Status-Issues und zum anderen für Super-Issues (bzw. Tasks). Bei diesen Super-Issues handelt es sich um große Aufgaben, denen wiederum verschiedene Issues als Teilaufgaben zugeordnet werden. + +Die Benennung sieht Folgendes vor: + +\begin{figure} [h] + \centering + \includegraphics[width = 0.2\linewidth]{img/status.png} + \caption{Beispiel: Label für Status-Issus} + \label{status} +\end{figure} +Label für \texttt{Status-Issues} werden folgendermaßen benannt: \texttt{STATUS: