diff --git a/report/report.pdf b/report/report.pdf index e788c0f..4d480c0 100644 Binary files a/report/report.pdf and b/report/report.pdf differ diff --git a/report/report.tex b/report/report.tex index 4951cf5..9f45e01 100644 --- a/report/report.tex +++ b/report/report.tex @@ -1,4 +1,5 @@ -% !TEX TS-program = xelatex +% !TeX document-id = {d31ec456-0f13-4d95-9fc2-bd99b70397a1} +% !TeX TXS-program:compile = txs:///xelatex/[-shell-escape] % !TEX encoding = UTF-8 Unicode % !TEX spellcheck = el-GR % @@ -75,6 +76,44 @@ \usepackage{footnote} \usepackage{footmisc} +\usepackage{minted} +\usepackage{xcolor} % + +\setminted[python]{ + fontsize=\small, + breaklines, + autogobble, + baselinestretch=1.1, + obeytabs=true + tabsize=2, + numbersep=8pt, + startinline, + gobble=0 +} + +\setminted[json]{ + fontsize=\small, + breaklines, + autogobble, + baselinestretch=1.1, + obeytabs=true + tabsize=2, + numbersep=8pt, + startinline, + gobble=0 +} + +\setminted[bash]{ + fontsize=\small, + breaklines, + autogobble, + baselinestretch=1.1, + obeytabs=true + tabsize=2, + numbersep=8pt, + startinline, + gobble=0 +} %\definecolor{red-highlight}{rgb}{1.0, 0.11, 0.0} \definecolor{red-highlight}{rgb}{1.0, 0.31, 0.0} @@ -104,6 +143,16 @@ \newcommand{\dslDesignUrl}{\repoGithub/blob/HEAD/Mininet_DSL_Design.md} +% Experiment 1 +\newcommand{\dhcpExecutionUrl}{\repoGithub/blob/HEAD/exercise/results/exp1-dhcp/execution.txt} +\newcommand{\dhcpDecodedUrl}{\repoGithub/blob/HEAD/exercise/results/exp1-dhcp/dhcp-decoded.txt} +\newcommand{\dhcpPcapUrl}{\repoGithub/blob/HEAD/exercise/results/exp1-dhcp/dhcp.pcap} + +% Experiment 2 +\newcommand{\arpExecutionUrl}{\repoGithub/blob/HEAD/exercise/results/exp2-arp/execution.txt} +\newcommand{\arpDecodedUrl}{\repoGithub/blob/HEAD/exercise/results/exp2-arp/arp-decoded.txt} +\newcommand{\arpPcapUrl}{\repoGithub/blob/HEAD/exercise/results/exp2-arp/arp.pcap} + \begin{document} \InsertTitle @@ -144,7 +193,10 @@ Language -- DSL}, και δημιουργεί δυναμικά τους αντί \end{itemize} -\section{Υλοποίηση του Builder} +\section{Υλοποίηση} + + +% ========================= DSL ========================= \subsection{Δηλωτική γλώσσα περιγραφής τοπολογίας} @@ -312,6 +364,805 @@ Layer-3 διευθυνσιοδότηση και ενεργοποιημένη π ο σημασιολογικός έλεγχος πραγματοποιούνται σε \textbf{διαφορετικά στάδια}. +% ========================= Builder ========================= +\subsection{Υλοποίηση του Builder} + +Η υλοποίηση του builder ακολουθεί τον διαχωρισμό που εισάγει η DSL και οργανώνεται ως μία ακολουθία ανεξάρτητων +σταδίων, καθένα από τα οποία μετασχηματίζει την πληροφορία σε μία περισσότερο συγκεκριμένη αναπαράσταση. +Η δομή αυτή επιτρέπει τον σαφή διαχωρισμό ανάμεσα στην περιγραφή της τοπολογίας, στον έλεγχο της εγκυρότητάς της +και στην τελική δημιουργία και εκτέλεση του δικτύου στο Mininet. + +\subsubsection{Δομή των αρχείων} + +Ο πηγαίος κώδικας του builder βρίσκεται στον κατάλογο \texttt{source/}, ενώ η περιγραφή και τα αποτελέσματα της +συγκεκριμένης εργαστηριακής άσκησης διατηρούνται χωριστά στον κατάλογο \texttt{exercise/}. +Η βασική δομή του αποθετηρίου είναι η ακόλουθη: + +\begin{verbatim} + . + |-- source/ + | |-- build_network.py + | |-- topology_loader.py + | |-- topology_parser.py + | |-- topology_validator.py + | |-- topology_plan.py + | `-- topology_renderer.py + | + |-- exercise/ + | |-- topology.json + | |-- topology.generated.py + | `-- results/ + | + |-- Mininet_DSL_Design.md + `-- README.md +\end{verbatim} + +\begin{itemize} + \item Το \texttt{topology\_loader.py} είναι υπεύθυνο αποκλειστικά για τη φόρτωση του αρχείου JSON από το σύστημα + αρχείων και την παραγωγή της αντίστοιχης ακατέργαστης Python δομής. + + \item Το \texttt{topology\_parser.py} περιέχει τη γραμματική, τα \texttt{Enum} και τα \texttt{dataclass} αντικείμενα + που αποτελούν το τυποποιημένο σημασιολογικό μοντέλο της DSL. + + \item Το \texttt{topology\_validator.py} ελέγχει τη σημασιολογική συνέπεια της τοπολογίας, χωρίς να πραγματοποιεί + οποιαδήποτε μεταβολή στο δίκτυο. + + \item Το \texttt{topology\_plan.py} επιλύει τις αφηρημένες επιλογές της DSL και παράγει ένα πλήρως συγκεκριμένο + \texttt{ExecutionPlan}. + + \item Το \texttt{topology\_renderer.py} μετατρέπει το σχέδιο αυτό σε εκτελέσιμο Python κώδικα για το Mininet, οποίος + εξάγεται σε ξεχωριστό αρχείο και αποτελεί ένα ιδιότυπο \emph{python-listing} του renderer. + + \item Τέλος, το \texttt{build\_network.py} αποτελεί τον \textbf{coordinator} και το μοναδικό user-facing entry point, + συνδέοντας τα επιμέρους στάδια και αναλαμβάνοντας την τελική εκτέλεση του παραγόμενου προγράμματος. +\end{itemize} + + +\subsubsection{Χρήση} + +Η κανονική χρήση του builder απαιτεί μόνο το αρχείο JSON που περιγράφει την επιθυμητή τοπολογία. +Για την τοπολογία της παρούσας εργασίας η πλήρης κατασκευή και εκτέλεση πραγματοποιείται με την εντολή: + +\begin{minted}{bash} +sudo python3 source/build_network.py exercise/topology.json +\end{minted} + +Η εκτέλεση αυτή μεταγλωττίζει πρώτα την τοπολογία, δημιουργεί το αρχείο +\texttt{exercise/topology.generated.py} και στη συνέχεια εκτελεί \textbf{το ίδιο ακριβώς παραγόμενο αρχείο}. +Μετά την κατασκευή και παραμετροποίηση του δικτύου ανοίγει το Mininet CLI, από το οποίο εκτελούνται τα πειράματα. + +Κατά την ανάπτυξη είναι επίσης δυνατό να παραχθεί μόνο το ενδιάμεσο Python πρόγραμμα, χωρίς να δημιουργηθεί +κανένα network namespace ή άλλη κατάσταση στο σύστημα. +Για παράδειγμα: + +\begin{minted}{bash} +python3 source/build_network.py exercise/topology.json --emit [Optional file name] +\end{minted} + +Η μορφή αυτή δεν απαιτεί δικαιώματα root και είναι χρήσιμη για την επιθεώρηση του αποτελέσματος της +μεταγλώττισης ή για τον εντοπισμό σφαλμάτων πριν από την πραγματική εκτέλεση. +Το \texttt{--emit} μπορεί επιπλέον να δεχθεί συγκεκριμένο path για το παραγόμενο αρχείο. + +Πριν από την εκτέλεση μπορεί επίσης να πραγματοποιηθεί έλεγχος του περιβάλλοντος με: + +\begin{minted}{bash} +python3 source/build_network.py --env-check +\end{minted} + +Ο έλεγχος επιβεβαιώνει την παρουσία του Mininet, του Open vSwitch και των εργαλείων Linux που απαιτούνται από +τον builder και τα πειράματα, όπως τα \texttt{ip}, \texttt{tc}, \texttt{sysctl}, \texttt{dnsmasq}, +\texttt{dhclient}, \texttt{tcpdump} και \texttt{ping}. +Ο ίδιος έλεγχος πραγματοποιείται αυτόματα πριν από την πραγματική εκτέλεση μιας παραγόμενης τοπολογίας. + +\subsubsection{Διαχωρισμός περιγραφής τοπολογίας και runtime κατάστασης} + +Βασική σχεδιαστική επιλογή είναι ο \textbf{διαχωρισμός της δηλωμένης τοπολογίας από την κατάσταση που δημιουργείται + κατά την εκτέλεσή της}. +Το αρχείο JSON περιγράφει την επιθυμητή διαμόρφωση και τις πηγές από τις οποίες θα προκύψει η παραμετροποίηση, +χωρίς να επιχειρεί να αποθηκεύσει την παροδική κατάσταση ενός εκτελούμενου δικτύου. + +Για παράδειγμα, η δήλωση: + +\begin{minted}{json} +"net0": { + "addressing": "dhcp" +} +\end{minted} + +δεν καθορίζει ποια διεύθυνση IP θα έχει τελικά ο host. +Δηλώνει ότι η διεύθυνση πρέπει να αποκτηθεί δυναμικά από μία υπηρεσία DHCP κατά την εκτέλεση της τοπολογίας. +Αντίστοιχα, ένας στατικός σύνδεσμος στο JSON οδηγεί στη δημιουργία πραγματικών virtual Ethernet interfaces, +χωρίς τα τελικά ονόματα αυτών των interfaces να χρειάζεται να εμφανίζονται στην αρχική περιγραφή. + +Η ίδια διάκριση ισχύει για τις DHCP leases, τον ARP neighbor table, τους μετρητές πακέτων και τα αρχεία +\texttt{pcap}, τα οποία αποτελούν \emph{runtime state} και δεν ανήκουν στη δηλωτική γλώσσα. +Για τον ίδιο λόγο ο builder δεν εκκινεί αυτόματα το \texttt{dhclient} στους hosts που έχουν δηλωθεί ως DHCP +clients, καθώς η απόκτηση της διεύθυνσης αποτελεί μέρος της πειραματικής διαδικασίας της εργασίας. + +Η αρχή αυτή αποτυπώνεται συνοπτικά ως: + +\begin{verbatim} +Declarative topology Runtime state + +static address -------> configured IP address +static gateway -------> routing-table entry +DHCP addressing -------> dynamically acquired lease +link declaration -------> veth / Mininet interfaces +service declaration -------> running process +ARP entries +packet captures +counters +\end{verbatim} + +\subsubsection{Compiler-like αρχιτεκτονική} + +Η εσωτερική αρχιτεκτονική του builder σχεδιάστηκε με λογική παρόμοια με αυτή ενός \textbf{μεταγλωττιστή}. +Αντί η εφαρμογή να διαβάζει ένα JSON και να καλεί άμεσα συναρτήσεις του Mininet, η περιγραφή περνά από +διαδοχικά στάδια, καθένα από τα οποία έχει σαφώς περιορισμένη ευθύνη. + +Η συνολική ροή είναι: + +\begin{verbatim} + topology.json + ↓ + [load] + ↓ + raw JSON dictionary + ↓ + [parse] + ↓ +typed Topology model + ↓ + [validate] + ↓ + validated Topology + ↓ + [plan] + ↓ + ExecutionPlan + ↓ + [render] + ↓ +topology.generated.py + ↓ + [execute] + ↓ + Mininet CLI + ↓ + [cleanup] +\end{verbatim} + +Ο διαχωρισμός αυτός επιτρέπει σε κάθε στάδιο να εξετάζεται ανεξάρτητα και μεταφέρει σταδιακά την περιγραφή από +μία υψηλού επιπέδου δήλωση πρόθεσης σε μία πλήρως συγκεκριμένη ακολουθία ενεργειών Mininet και Linux. +Παράλληλα, κάθε κατηγορία σφάλματος ανήκει στο στάδιο που διαθέτει την απαραίτητη πληροφορία για να την εντοπίσει. + +\paragraph{Φόρτωση -- \texttt{topology\_loader.py}.} + +Το πρώτο στάδιο αφορά αποκλειστικά την πρόσβαση στο αρχείο και την ανάλυση της σύνταξης JSON. +Η δημόσια συνάρτηση \texttt{load\_topology()} διαβάζει το αρχείο και επιστρέφει ένα +\texttt{Dict[str, Any]}, χωρίς να γνωρίζει οτιδήποτε για τη σημασιολογία της DSL. + +\begin{verbatim} +raw = load_topology("exercise/topology.json") + +JSON file + | + `--> Dict[str, Any] +\end{verbatim} + +Σφάλματα όπως ανύπαρκτο αρχείο, μη έγκυρο JSON ή root αντικείμενο διαφορετικό από JSON object αναφέρονται ως +\texttt{TopologyLoadError}. +Στο σημείο αυτό δεν εξετάζεται ακόμη αν ένα \texttt{type} είναι έγκυρο ή αν ένας σύνδεσμος αναφέρεται σε +υπαρκτό κόμβο. + +\paragraph{Parsing -- \texttt{topology\_parser.py}.} + +Το δεύτερο στάδιο μετατρέπει την ακατέργαστη δομή JSON στο \textbf{τυποποιημένο σημασιολογικό μοντέλο} της DSL. +Η δημόσια συνάρτηση \texttt{parse\_topology()} κατασκευάζει αντικείμενα όπως \texttt{Topology}, \texttt{Node}, +\texttt{Interface}, \texttt{Link} και \texttt{Service}, χρησιμοποιώντας \texttt{Enum} για τα κλειστά σύνολα τιμών. + +Για παράδειγμα, ένα τμήμα JSON όπως: + +\begin{minted}{json} +{ + "node": "s1", + "interface": "auto" +} +\end{minted} + +δεν παραμένει ένα γενικό dictionary, αλλά μετατρέπεται εννοιολογικά σε: + +\begin{minted}{python} +LinkEndpoint( + node="s1", + interface=LinkInterfaceSelector.AUTO +) +\end{minted} + +Το στάδιο αυτό εφαρμόζει επίσης την \emph{αυστηρή γραμματική} της γλώσσας. +Άγνωστα πεδία ή μη υποστηριζόμενες τιμές προκαλούν \texttt{TopologyParseError}, αντί να αγνοούνται σιωπηρά. + +\paragraph{Validation -- \texttt{topology\_validator.py}.} + +Μετά το parsing η δομή είναι γραμματικά έγκυρη, αυτό όμως δεν συνεπάγεται ότι περιγράφει και συνεπές δίκτυο. +Η δημόσια συνάρτηση \texttt{validate()} πραγματοποιεί τους ελέγχους που απαιτούν γνώση περισσότερων από ενός +αντικειμένων της τοπολογίας. + +Για παράδειγμα, το ακόλουθο JSON μπορεί να αναλυθεί συντακτικά, αλλά δεν περιγράφει έγκυρη στατική διεπαφή: + +\begin{minted}{json} +"net0": { + "addressing": "static" +} +\end{minted} + +Το validation θα απορρίψει τη συγκεκριμένη περιγραφή επειδή απουσιάζει η στατική διεύθυνση. +Αντίστοιχα ελέγχονται αναφορές σε ανύπαρκτους κόμβους, επαναχρησιμοποίηση interfaces, διευθύνσεις και gateways, +διαθεσιμότητα ports για \texttt{auto}, καθώς και η συνέπεια των ρυθμίσεων της υπηρεσίας DHCP. + +Σε αντίθεση με ένα απλό fail-fast μοντέλο, ο validator συλλέγει τα προβλήματα σε αντικείμενα +\texttt{ValidationIssue} και τα επιστρέφει συγκεντρωτικά μέσω ενός \texttt{TopologyValidationError}. +Με αυτόν τον τρόπο ο χρήστης μπορεί να διορθώσει περισσότερα από ένα προβλήματα σε έναν κύκλο εκτέλεσης. + +\paragraph{Planning -- \texttt{topology\_plan.py}.} + +Το planning είναι το σημείο στο οποίο η έγκυρη πλέον περιγραφή μετατρέπεται σε μία +\textbf{πλήρως επιλυμένη ενδιάμεση αναπαράσταση}. +Η δημόσια συνάρτηση \texttt{plan()} παράγει ένα \texttt{ExecutionPlan}, στο οποίο δεν παραμένουν αφηρημένοι +selectors όπως \texttt{auto} ή άλλες αποφάσεις που πρέπει να ληφθούν από τον backend. + +Για παράδειγμα, έστω ένας switch(node) με δύο ρητά δηλωμένες λογικές διεπαφές: + +\begin{minted}{json} +{ + "name": "s1", + "type": "switch", + "interfaces": { + "client": {}, + "uplink": {} + } +} +\end{minted} + +και δύο σύνδεσμοι, από τους οποίους ο πρώτος χρησιμοποιεί \texttt{auto}, ενώ ο δεύτερος ζητά ρητά τη διεπαφή +\texttt{uplink}: + +\begin{minted}{json} +{ + "endpoints": [ + {"node": "h1", "interface": "net0"}, + {"node": "s1", "interface": "auto"} + ] +}, +{ + "endpoints": [ + {"node": "r0", "interface": "lan"}, + {"node": "s1", "interface": "uplink"} + ] +} +\end{minted} + +Πριν από την επίλυση των \texttt{auto}, ο planner εντοπίζει όλες τις ρητές αναφορές σε interfaces και δεσμεύει +τη \texttt{uplink}. +Το διαθέσιμο σύνολο διεπαφών του \texttt{s1} για αυτόματη επιλογή περιέχει επομένως μόνο τη \texttt{client}. + +\begin{minted}{python} + reserved["s1"] = {"uplink"} + free_interfaces["s1"] = ["client"] +\end{minted} + +Κατά το planning, το \texttt{auto} του πρώτου συνδέσμου επιλύεται συνεπώς στη λογική διεπαφή \texttt{client}. +Στο συγκεκριμένο παράδειγμα οι λογικές διεπαφές αντιστοιχίζονται στη συνέχεια και στα συγκεκριμένα ονόματα +διεπαφών που θα χρησιμοποιήσει το Mininet: + +\begin{verbatim} +h1:net0 -> h1-eth0 +s1:client -> s1-eth0 + +r0:lan -> r0-eth0 +s1:uplink -> s1-eth1 +\end{verbatim} + +Με αυτόν τον τρόπο μία προηγούμενη αυτόματη επιλογή δεν μπορεί να καταλάβει interface που έχει ζητηθεί ρητά από +μεταγενέστερο link. +Το \texttt{auto} λειτουργεί επομένως ως \emph{επιλογέας διαθέσιμης λογικής διεπαφής} και όχι ως όνομα +διεπαφής. + +Η συμπεριφορά είναι διαφορετική όταν το πεδίο \texttt{interfaces} έχει παραλειφθεί πλήρως από έναν κόμβο. +Σε αυτή την περίπτωση ο planner δημιουργεί συνθετικά λογικά ονόματα, όπως \texttt{auto0} και \texttt{auto1}, +τα οποία αντιστοιχίζονται στη συνέχεια σε φυσικά ονόματα όπως \texttt{s1-eth0} και \texttt{s1-eth1}. + +Στο ίδιο στάδιο αποφασίζονται και backend-specific λεπτομέρειες που δεν πρέπει να εμφανίζονται στη DSL. +Έτσι, η σημασιολογία ενός απλού Layer-2 switch μετατρέπεται από τον planner στην επιλογή +\texttt{switch\_fail\_mode = "standalone"} για το Open vSwitch. + +Ένα απλοποιημένο τμήμα του \texttt{ExecutionPlan} για το προηγούμενο παράδειγμα μπορεί επομένως να θεωρηθεί ότι +έχει τη μορφή: + +\begin{minted}{python} + PlannedLinkEndpoint( + node="s1", + logical_interface="client", + interface_name="s1-eth0" + ) + + PlannedLinkEndpoint( + node="s1", + logical_interface="uplink", + interface_name="s1-eth1" + ) + + PlannedNode( + name="s1", + type=NodeType.SWITCH, + switch_fail_mode="standalone" + ) +\end{minted} + +Ο planner δεν δημιουργεί interfaces και δεν τα εισάγει στο Mininet. +Η ευθύνη του τελειώνει όταν όλες οι αποφάσεις που απαιτούνται για την εκτέλεση έχουν αποτυπωθεί στο +\texttt{ExecutionPlan}. + +\paragraph{Rendering -- \texttt{topology\_renderer.py}.} + +Το renderer δέχεται το πλήρως επιλυμένο \texttt{ExecutionPlan} και το μετατρέπει σε ένα ολοκληρωμένο Python +πρόγραμμα μέσω της δημόσιας συνάρτησης \texttt{render\_python()}. +Σε αυτό το στάδιο \textbf{δεν πραγματοποιείται νέα σημασιολογική απόφαση}: ο renderer αποτυπώνει σε κώδικα τις +επιλογές που έχουν ήδη γίνει από τον planner. + +Για το προηγούμενο παράδειγμα μπορούν να παραχθούν εντολές της μορφής: + +\begin{minted}{python} + nodes['h1'] = net.addHost('h1', ip=None) + nodes['r0'] = net.addHost('r0', ip=None) + nodes['s1'] = net.addSwitch('s1', failMode='standalone') + + net.addLink( + nodes['h1'], + nodes['s1'], + intfName1='h1-eth0', + intfName2='s1-eth0', + ) + + net.addLink( + nodes['r0'], + nodes['s1'], + intfName1='r0-eth0', + intfName2='s1-eth1', + ) +\end{minted} + +Παρατηρείται ότι στο παραγόμενο πρόγραμμα δεν υπάρχει πλέον ο selector \texttt{auto}. +Η επιλογή \texttt{s1:client} έχει ήδη πραγματοποιηθεί από τον planner και ο renderer γνωρίζει μόνο το πλήρως +επιλυμένο ζεύγος \texttt{s1:client} και \texttt{s1-eth0}. + +Η χρήση του \texttt{ip=None} εμποδίζει το Mininet να εκχωρήσει τις δικές του προεπιλεγμένες διευθύνσεις στους +hosts, ώστε η διευθυνσιοδότηση να παραμένει αποκλειστικά υπό τον έλεγχο της DSL. +Ανάλογα με το σχέδιο, ο renderer παράγει επίσης εντολές για IP addresses, routes, sysctls, \texttt{TCLink} και +εκκίνηση του \texttt{dnsmasq}. + +Το παραγόμενο πρόγραμμα περιλαμβάνει ακόμη το πλήρες lifecycle του δικτύου: + +\begin{minted}{python} + net.build() + net.start() + + # interface configuration + # routes + # sysctls + # services + + CLI(net) + + # finally: + # stop started services + # net.stop() +\end{minted} + +\paragraph{Το παραγόμενο Python ως ενδιάμεση ``assembly''.} + +Το αρχείο \texttt{topology.generated.py} έχει σκόπιμα κεντρικό ρόλο στην αρχιτεκτονική και δεν αποτελεί απλώς +ένα προσωρινό implementation detail. +Μπορεί να θεωρηθεί ως η \emph{``assembly'' της DSL}: είναι χαμηλότερου επιπέδου από το JSON, πλήρως συγκεκριμένο, +άμεσα εκτελέσιμο και ταυτόχρονα αρκετά αναγνώσιμο ώστε να μπορεί να επιθεωρηθεί από τον χρήστη. + +Η αντιστοίχιση μπορεί να συνοψιστεί ως: + +\begin{verbatim} +topology.json: declarative source +Topology: typed semantic representation +ExecutionPlan: resolved intermediate representation +topology.generated.py: executable "assembly" +Mininet / Linux: runtime +\end{verbatim} + +Ο coordinator αποθηκεύει πάντοτε το παραγόμενο Python πριν από την εκτέλεση και στη συνέχεια εκτελεί ακριβώς +αυτό το αρχείο. +Δεν υπάρχει δεύτερος, ανεξάρτητος executor που να μεταφράζει απευθείας το \texttt{ExecutionPlan} σε Mininet +operations, αποφεύγοντας έτσι τον κίνδυνο διαφορετικής συμπεριφοράς μεταξύ του inspectable artifact και του +πραγματικού runtime. + +\paragraph{Εκτέλεση και συντονισμός -- \texttt{build\_network.py}.} + +Το \texttt{build\_network.py} συνθέτει τα προηγούμενα στάδια μέσω της συνάρτησης +\texttt{compile\_topology()}: + +\begin{minted}{python} + topology = parse_topology(load_topology(path)) + validate(topology) + execution_plan = plan(topology) + source = render_python(execution_plan) +\end{minted} + +Το παραγόμενο source γράφεται στο \texttt{topology.generated.py} και, στην κανονική λειτουργία, εκτελείται ως +ξεχωριστό Python process με τα ίδια δικαιώματα με τον coordinator. +Η πραγματική εκτέλεση απαιτεί root privileges, ενώ η φόρτωση, το parsing, το validation, το planning και το +rendering μπορούν να πραγματοποιηθούν χωρίς αυτά. + +Το generated πρόγραμμα δημιουργεί τους κόμβους και τα links, εφαρμόζει τη δικτυακή παραμετροποίηση, εκκινεί τις +προβλεπόμενες υπηρεσίες και παραδίδει τον έλεγχο στον χρήστη μέσω του Mininet CLI. +Η έξοδος από το CLI οδηγεί σε δομημένο cleanup μέσω \texttt{finally}, όπου τερματίζονται οι service processes που +ξεκίνησε το ίδιο το πρόγραμμα και στη συνέχεια καλείται το \texttt{net.stop()}. + +\subsubsection{Έλεγχος των επιμέρους σταδίων} + +Η ανεξαρτησία των σταδίων αξιοποιείται και κατά την ανάπτυξη και τον εντοπισμό σφαλμάτων. +Κάθε βασικό module του pipeline διαθέτει ένα μικρό \textbf{self-test του δημόσιου interface του}, το οποίο μπορεί +να εκτελεστεί χωρίς να δημιουργηθεί πραγματικό Mininet δίκτυο. + +Για παράδειγμα: + +\begin{minted}{bash} + python3 source/topology_loader.py + python3 source/topology_parser.py + python3 source/topology_validator.py + python3 source/topology_plan.py + python3 source/topology_renderer.py +\end{minted} + +Μία επιτυχής εκτέλεση καταλήγει σε μήνυμα της μορφής: + +\begin{verbatim} +topology_parser: self-test passed +\end{verbatim} + +Τα tests δεν περιορίζονται στην απλή εισαγωγή των modules, αλλά ελέγχουν την κύρια συμπεριφορά του αντίστοιχου +δημόσιου API. +Ο parser, για παράδειγμα, ελέγχει τόσο σωστή μετατροπή σε typed objects όσο και απόρριψη άγνωστων πεδίων, ενώ ο +validator εξετάζει τόσο έγκυρες όσο και πολλαπλά λανθασμένες τοπολογίες. + +Αντίστοιχα, ο planner ελέγχει την επίλυση του \texttt{auto}, την παραγωγή των physical interface names, την +επιλογή \texttt{standalone} για τους switches και την παραγωγή των απαιτούμενων sysctls. +Ο renderer ελέγχει ότι το παραγόμενο source είναι συντακτικά έγκυρο Python και ότι περιλαμβάνει τις αναμενόμενες +κλήσεις Mininet, τις υπηρεσίες και το cleanup. + +Ο διαχωρισμός αυτός αποδείχθηκε ιδιαίτερα χρήσιμος κατά την ανάπτυξη, καθώς ένα σφάλμα μπορεί να απομονωθεί στο +στάδιο στο οποίο εμφανίζεται, χωρίς να απαιτείται κάθε φορά εκκίνηση του Mininet με δικαιώματα root. +Το \texttt{--emit} συμπληρώνει αυτή τη διαδικασία, επιτρέποντας την επιθεώρηση της τελικής +\emph{``assembly''} πριν από οποιαδήποτε μεταβολή της πραγματικής δικτυακής κατάστασης του συστήματος. + + + +% ========================= Πείραμα 1 ========================= + +\section{Πείραμα 1 -- DHCP και διαδικασία D.O.R.A.} + +\subsection{Σκοπός και πειραματική διαδικασία} + +Σκοπός του πρώτου πειράματος είναι η παρατήρηση της διαδικασίας μέσω της οποίας ένας host χωρίς στατική +διευθυνσιοδότηση αποκτά τις απαραίτητες ρυθμίσεις δικτύου από έναν DHCP server. +Ειδικότερα, εξετάζεται η ακολουθία \textbf{Discover--Offer--Request--ACK (D.O.R.A.)} και τα αντίστοιχα πεδία των +Ethernet, IP και UDP headers. + +Η τοπολογία εκκινήθηκε από τον κατάλογο \texttt{exercise/} με την εντολή: + +\begin{minted}{bash} + sudo python3 ../source/build_network.py topology.json +\end{minted} + +Πριν από την καταγραφή αφαιρέθηκε το αποθηκευμένο lease του \texttt{dhclient}, ώστε ο \texttt{h1} να ξεκινήσει +τη διαδικασία χωρίς να επιχειρήσει να επαναχρησιμοποιήσει διεύθυνση από προηγούμενη εκτέλεση. +Αφαιρέθηκε επίσης τυχόν προηγούμενο αρχείο καταγραφής και στη συνέχεια ξεκίνησε το \texttt{tcpdump} στη διεπαφή +\texttt{h1-eth0}. + +Οι εντολές που εκτελέστηκαν στο Mininet CLI ήταν: + +\begin{minted}{bash} + minted> h1 rm -f /var/lib/dhcp/dhclient.leases + minted> h1 rm -f /tmp/dhcp.pcap + minted> h1 tcpdump -i h1-eth0 -n -w /tmp/dhcp.pcap & + minted> h1 ps aux | grep tcpdump + minted> h1 dhclient h1-eth0 + minted> h1 pkill -INT tcpdump + minted> h1 tcpdump -n -e -vv -r /tmp/dhcp.pcap +\end{minted} + +Η επιλογή \texttt{-e} κατά την ανάγνωση της καταγραφής επιτρέπει την εμφάνιση των Ethernet headers και επομένως +των MAC διευθύνσεων που απαιτούνται για την ανάλυση. +Η καταγραφή ολοκληρώθηκε χωρίς απώλειες από το \texttt{tcpdump}, με 12 πακέτα να έχουν καταγραφεί συνολικά. + +Η πλήρης συνεδρία της εκτέλεσης βρίσκεται στο \href{\dhcpExecutionUrl}{\texttt{execution.txt}}. +Το αρχικό αρχείο καταγραφής διατίθεται στο \href{\dhcpPcapUrl}{\texttt{dhcp.pcap}}, ενώ η αναλυτικά +αποκωδικοποιημένη μορφή του βρίσκεται στο \href{\dhcpDecodedUrl}{\texttt{dhcp-decoded.txt}}. + +\subsection{Αποτελέσματα καταγραφής -- Ζητούμενο 1.1} + +Η καταγραφή περιλαμβάνει την αναμενόμενη ακολουθία +\textbf{Discover $\rightarrow$ Offer $\rightarrow$ Request $\rightarrow$ ACK}, η οποία ολοκληρώθηκε επιτυχώς. +Στην πραγματική εκτέλεση καταγράφηκαν δύο DHCP Discover με το ίδιο transaction ID πριν από το πρώτο Offer. + +Η χρονική ακολουθία των σχετικών DHCP μηνυμάτων ήταν: + +\begin{verbatim} +10:58:29.655251 DHCP Discover +10:58:32.214530 DHCP Discover +10:58:32.670897 DHCP Offer +10:58:32.671193 DHCP Request +10:58:32.673025 DHCP ACK +10:58:32.678132 DHCP Offer +\end{verbatim} + +Μεταξύ του πρώτου Discover και του πρώτου Offer παρατηρήθηκαν τρία ARP probes από τον DHCP server για τη +διεύθυνση \texttt{192.168.1.131} και στη συνέχεια ένα ICMP Echo Request προς την ίδια διεύθυνση. +Ο έλεγχος αυτός εισήγαγε καθυστέρηση στην πρώτη απάντηση του server, με αποτέλεσμα ο \texttt{dhclient} να +επαναμεταδώσει το Discover περίπου τρία δευτερόλεπτα μετά το πρώτο. + +Και τα δύο Discover χρησιμοποιούν το ίδιο transaction ID, \texttt{0x0ecb5a2e}, επομένως αποτελούν μέρος της +ίδιας προσπάθειας απόκτησης διεύθυνσης και όχι δύο ανεξάρτητες διαδικασίες DHCP. +Παρά την επαναμετάδοση, η διαδικασία ολοκληρώθηκε κανονικά με Offer, Request και ACK και ο \texttt{h1} έλαβε τη +διεύθυνση \texttt{192.168.1.131}. +Μετά το ACK καταγράφηκε ακόμη ένα Offer, χωρίς αυτό να επηρεάζει την ήδη ολοκληρωμένη παραχώρηση της διεύθυνσης. + +Η βασική τετράδα D.O.R.A. δίνει τα ακόλουθα στοιχεία: + +\begin{table}[H] + \centering + \small + \begin{tabularx}{\textwidth}{ + >{\raggedright\arraybackslash}p{1.3cm} + >{\raggedright\arraybackslash}p{5.2cm} + >{\raggedright\arraybackslash}p{2.0cm} + >{\raggedright\arraybackslash}X + } + \textbf{Μήνυμα} & + \textbf{IP} & + \textbf{UDP port} & + \textbf{MAC} \\ \hline + +% \textbf{Μήνυμα} & +% \textbf{από $\rightarrow$ προς} & +% \textbf{UDP port από $\rightarrow$ προς} & +% \textbf{MAC από $\rightarrow$ προς} \\ \hline + + Discover & + \texttt{0.0.0.0 $\rightarrow$ 255.255.255.255} & + \texttt{68 $\rightarrow$ 67} & + \texttt{12:07:f8:bf:6e:ff $\rightarrow$ ff:ff:ff:ff:ff:ff} \\ + + Offer & + \texttt{192.168.1.1 $\rightarrow$ 192.168.1.131} & + \texttt{67 $\rightarrow$ 68} & + \texttt{82:3b:16:e9:bb:17 $\rightarrow$ 12:07:f8:bf:6e:ff} \\ + + Request & + \texttt{0.0.0.0 $\rightarrow$ 255.255.255.255} & + \texttt{68 $\rightarrow$ 67} & + \texttt{12:07:f8:bf:6e:ff $\rightarrow$ ff:ff:ff:ff:ff:ff} \\ + + ACK & + \texttt{192.168.1.1 $\rightarrow$ 192.168.1.131} & + \texttt{67 $\rightarrow$ 68} & + \texttt{82:3b:16:e9:bb:17 $\rightarrow$ 12:07:f8:bf:6e:ff} \\ + \end{tabularx} + \caption{Διευθύνσεις και UDP θύρες των μηνυμάτων της διαδικασίας D.O.R.A.} +\end{table} + +\subsection{Ανάλυση της διαδικασίας D.O.R.A.} + +Στο \textbf{Discover} ο \texttt{h1} δεν διαθέτει ακόμη διεύθυνση IPv4 και αναζητά διαθέσιμο DHCP server στο +τοπικό δίκτυο. +Για τον λόγο αυτό χρησιμοποιεί ως source IP την \texttt{0.0.0.0} και μεταδίδει το μήνυμα προς την περιορισμένη +broadcast διεύθυνση \texttt{255.255.255.255}, με Ethernet destination \texttt{ff:ff:ff:ff:ff:ff}. + +Στο \textbf{Offer} ο DHCP server του \texttt{r0}, με διεύθυνση \texttt{192.168.1.1}, προσφέρει στον client τη +διεύθυνση \texttt{192.168.1.131}. +Στο ίδιο μήνυμα περιλαμβάνονται επίσης η μάσκα \texttt{255.255.255.0}, η broadcast διεύθυνση +\texttt{192.168.1.255} και η προεπιλεγμένη πύλη \texttt{192.168.1.1}. + +Ο \texttt{h1} απαντά με \textbf{Request}, στο οποίο δηλώνει τόσο τον server που επέλεξε όσο και τη διεύθυνση που +ζητά να του εκχωρηθεί. +Τα αντίστοιχα options που καταγράφηκαν είναι: + +\begin{minted}{text} +Server-ID Option: 192.168.1.1 +Requested-IP Option: 192.168.1.131 +\end{minted} + +Τέλος, με το \textbf{ACK} ο server επιβεβαιώνει την παραχώρηση της \texttt{192.168.1.131} και αποστέλλει τις +τελικές παραμέτρους του lease. +Στην καταγραφή το lease time είναι \texttt{43200} δευτερόλεπτα και η προεπιλεγμένη πύλη που παρέχεται στον +client είναι η \texttt{192.168.1.1}. + +\subsection{Broadcast μετάδοση του DHCP Request -- Ζητούμενο 1.2} + +Το DHCP Request της αρχικής διαδικασίας απόκτησης διεύθυνσης μεταδόθηκε ως \textbf{broadcast} τόσο στο επίπεδο +Ethernet όσο και στο επίπεδο IP. +Στην καταγραφή παρατηρούνται συγκεκριμένα: + +\begin{minted}{text} +Ethernet destination: ff:ff:ff:ff:ff:ff +IP destination: 255.255.255.255 +UDP ports: 68 -> 67 +\end{minted} + +Κατά τη στιγμή αποστολής του Request η παραχώρηση της διεύθυνσης δεν έχει ακόμη ολοκληρωθεί και ο client +συνεχίζει να χρησιμοποιεί την \texttt{0.0.0.0} ως source IP. +Η broadcast μετάδοση επιτρέπει επιπλέον σε όλους τους DHCP servers του τοπικού δικτύου να παρατηρήσουν ποια +προσφορά επέλεξε ο client. + +Ο επιλεγμένος server αναγνωρίζεται από το \texttt{Server-ID} του Request, ενώ τυχόν άλλοι servers μπορούν να +διαπιστώσουν ότι οι δικές τους προσφορές δεν επιλέχθηκαν. +Το ACK του \texttt{192.168.1.1} ολοκληρώνει τελικά τη διαδικασία και επιβεβαιώνει ότι η +\texttt{192.168.1.131} μπορεί πλέον να χρησιμοποιηθεί από τον \texttt{h1}. + + + +% ========================= Πείραμα 2 ========================= + +\section{Πείραμα 2 -- Πρωτόκολλο ARP} + +\subsection{Σκοπός και πειραματική διαδικασία} + +Σκοπός του δεύτερου πειράματος είναι η παρατήρηση της διαδικασίας αντιστοίχισης μιας γνωστής διεύθυνσης IPv4 +στην αντίστοιχη διεύθυνση MAC μέσω του πρωτοκόλλου \textbf{ARP}. +Ειδικότερα, εξετάζεται η αρχική επίλυση της διεύθυνσης του \texttt{h2} από τον \texttt{h1} και η διαφορετική +μορφή μετάδοσης του ARP Request και του ARP Reply. + +Η τοπολογία εκκινήθηκε εκ νέου και οι δύο hosts του Subnet A απέκτησαν διευθύνσεις μέσω DHCP. +Οι διευθύνσεις που εκχωρήθηκαν κατά τη συγκεκριμένη εκτέλεση ήταν: +\begin{itemize} + \item \texttt{\textbf{192.168.1.131/24}} για τον \texttt{\textbf{h1}} και + \item \texttt{\textbf{192.168.1.164/24}} για τον \texttt{\textbf{h2}}. +\end{itemize} + +Οι αρχικές εντολές της εκτέλεσης ήταν: + +\begin{minted}{bash} + sudo python3 ../source/build_network.py topology.json + + minted> h1 ip -4 -br addr + minted> h2 ip -4 -br addr + minted> h1 dhclient h1-eth0 + minted> h2 dhclient h2-eth0 + minted> h1 ip -4 -br addr + minted> h2 ip -4 -br addr +\end{minted} + +Πριν από το ping διαγράφηκε ο neighbor table του \texttt{h1}, ώστε να μην υπάρχει ήδη γνωστή αντιστοίχιση +μεταξύ της IP και της MAC διεύθυνσης του \texttt{h2}. +Η κενή κατάσταση του πίνακα επιβεβαιώθηκε με την εντολή \texttt{ip neigh show}. + +\begin{minted}{bash} + minted> h1 ip neigh flush all + minted> h1 ip neigh show + minted> h1 rm -f /tmp/arp.pcap + minted> h1 tcpdump -i h1-eth0 -n arp -w /tmp/arp.pcap & + minted> h1 ping -c 1 192.168.1.164 + minted> h1 ip neigh show + minted> h1 pkill -INT tcpdump + minted> h1 tcpdump -n -e -vv -r /tmp/arp.pcap +\end{minted} + +Το φίλτρο \texttt{arp} του \texttt{tcpdump} περιορίζει την καταγραφή στα ARP frames, ενώ η επιλογή \texttt{-e} +κατά την ανάγνωση εμφανίζει τα Ethernet headers και επομένως τις MAC διευθύνσεις πηγής και προορισμού. +Η καταγραφή ολοκληρώθηκε χωρίς απώλειες, με τέσσερα ARP frames να έχουν καταγραφεί συνολικά. + +Η πλήρης συνεδρία της εκτέλεσης βρίσκεται στο \href{\arpExecutionUrl}{\texttt{execution.txt}}. +Το αρχικό αρχείο καταγραφής διατίθεται στο \href{\arpPcapUrl}{\texttt{arp.pcap}}, ενώ η αποκωδικοποιημένη μορφή του βρίσκεται στο +\href{\arpDecodedUrl}{\texttt{arp-decoded.txt}}. + +\subsection{Αποτελέσματα καταγραφής} + +Μετά την εκκαθάριση του neighbor table, το πρώτο ping από τον \texttt{h1} προς τον \texttt{h2} απαίτησε την +επίλυση της MAC διεύθυνσης που αντιστοιχεί στην \texttt{192.168.1.164}. +Το πρώτο ζεύγος πακέτων της καταγραφής ήταν: + +\begin{minted}{text} +4a:6f:2a:18:f4:80 > ff:ff:ff:ff:ff:ff +Request who-has 192.168.1.164 tell 192.168.1.131 + +82:c0:6d:ae:3f:56 > 4a:6f:2a:18:f4:80 +Reply 192.168.1.164 is-at 82:c0:6d:ae:3f:56 +\end{minted} + +Από την καταγραφή προκύπτει επομένως ότι στη συγκεκριμένη εκτέλεση οι δύο hosts είχαν τα ακόλουθα στοιχεία: + +\begin{table}[H] + \centering + \begin{tabular}{lll} + \textbf{Host} & \textbf{IPv4} & \textbf{MAC} \\ \hline + \texttt{h1} & \texttt{192.168.1.131} & \texttt{4a:6f:2a:18:f4:80} \\ + \texttt{h2} & \texttt{192.168.1.164} & \texttt{82:c0:6d:ae:3f:56} \\ + \end{tabular} + \caption{Διευθύνσεις των hosts που συμμετέχουν στο πείραμα ARP.} +\end{table} + +Το ping ολοκληρώθηκε επιτυχώς και, μετά την ανταλλαγή ARP, ο neighbor table του \texttt{h1} περιείχε (λογικά) την +αντιστοίχιση: + +\begin{minted}{text} +192.168.1.164 dev h1-eth0 lladdr 82:c0:6d:ae:3f:56 REACHABLE +\end{minted} + +Το αποτέλεσμα αυτό επιβεβαιώνει ότι ο \texttt{h1} έμαθε τη MAC διεύθυνση του \texttt{h2} μέσω της προηγούμενης +ανταλλαγής ARP και μπορεί πλέον να δημιουργεί απευθείας Ethernet frames προς αυτόν. + +\subsection{Ανάλυση του ARP Request και Reply -- Ζητούμενο 2.1} + +Κατά την αποστολή του αρχικού ARP Request ο \texttt{h1} γνωρίζει τη διεύθυνση IP του προορισμού, αλλά +\textbf{δεν γνωρίζει ακόμη τη MAC διεύθυνσή του}. +Δεν μπορεί επομένως να δημιουργήσει ένα unicast Ethernet frame προς τον \texttt{h2}. + +Για τον λόγο αυτό το ARP Request μεταδίδεται προς την Ethernet broadcast διεύθυνση: + +\begin{minted}{text} +Source MAC: 4a:6f:2a:18:f4:80 +Destination MAC: ff:ff:ff:ff:ff:ff + +Who has 192.168.1.164? +Tell 192.168.1.131 +\end{minted} + +Όλοι οι κόμβοι του ίδιου Layer-2 broadcast domain μπορούν να λάβουν το Request, αλλά μόνο ο host στον οποίο +ανήκει η \texttt{192.168.1.164} πρέπει να απαντήσει. +Στην συγκεκριμένη περίπτωση ο host αυτός είναι ο \texttt{h2}. + +Το ARP Reply, αντίθετα, μεταδίδεται ως \textbf{unicast}: + +\begin{minted}{text} +Source MAC: 82:c0:6d:ae:3f:56 +Destination MAC: 4a:6f:2a:18:f4:80 + +192.168.1.164 is-at 82:c0:6d:ae:3f:56 +\end{minted} + +Ο \texttt{h2} μπορεί να στείλει την απάντηση απευθείας στον \texttt{h1}, επειδή το ARP Request περιλαμβάνει τα +στοιχεία του αποστολέα και επομένως παρέχει στον \texttt{h2} τη MAC διεύθυνση στην οποία πρέπει να απαντήσει. +Δεν υπάρχει συνεπώς ανάγκη το Reply να μεταδοθεί σε όλους τους κόμβους του broadcast domain. + +\textbf{Η MAC προορισμού του αρχικού ARP Request είναι επομένως \texttt{ff:ff:ff:ff:ff:ff}, +ενώ το ARP Reply επιστρέφει ως unicast στη MAC \texttt{4a:6f:2a:18:f4:80} του \texttt{h1}.} + +\subsection{Πρόσθετη παρατήρηση} + +Στην καταγραφή εμφανίστηκε περίπου πέντε δευτερόλεπτα αργότερα και ένα δεύτερο ζεύγος ARP Request/Reply προς +την αντίθετη κατεύθυνση: + +\begin{minted}{text} +82:c0:6d:ae:3f:56 > 4a:6f:2a:18:f4:80 +Request who-has 192.168.1.131 tell 192.168.1.164 + +4a:6f:2a:18:f4:80 > 82:c0:6d:ae:3f:56 +Reply 192.168.1.131 is-at 4a:6f:2a:18:f4:80 +\end{minted} + +Σε αντίθεση με το αρχικό Request, το δεύτερο Request έχει ήδη ως Ethernet destination τη MAC του +\texttt{h1} και επομένως είναι unicast. +Η ανταλλαγή αυτή σχετίζεται με τη διαχείριση και επιβεβαίωση της κατάστασης του neighbor cache από το Linux και +δεν αποτελεί την αρχική επίλυση που προκαλεί το ping. + +Για το ζητούμενο του πειράματος εξετάζεται συνεπώς το \textbf{πρώτο ζεύγος Request/Reply}, στο οποίο φαίνεται +καθαρά η μετάβαση από broadcast αναζήτηση σε unicast απάντηση. +Παρόλα αυτά το γεγονός ότι υπήρχε δεύτερο ζεύγος μας προβλημάτισε ιδιαίτερα μέχρι να ανακαλύψουμε τον λόγο ύπαρξής του! + + + + + + + + + \subsection{Συμπεράσματα}