Report: Added implementation, and experiments 1 and 2
This commit is contained in:
Binary file not shown.
+853
-2
@@ -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 encoding = UTF-8 Unicode
|
||||||
% !TEX spellcheck = el-GR
|
% !TEX spellcheck = el-GR
|
||||||
%
|
%
|
||||||
@@ -75,6 +76,44 @@
|
|||||||
\usepackage{footnote}
|
\usepackage{footnote}
|
||||||
\usepackage{footmisc}
|
\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.11, 0.0}
|
||||||
\definecolor{red-highlight}{rgb}{1.0, 0.31, 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}
|
\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}
|
\begin{document}
|
||||||
|
|
||||||
\InsertTitle
|
\InsertTitle
|
||||||
@@ -144,7 +193,10 @@ Language -- DSL}, και δημιουργεί δυναμικά τους αντί
|
|||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
|
|
||||||
\section{Υλοποίηση του Builder}
|
\section{Υλοποίηση}
|
||||||
|
|
||||||
|
|
||||||
|
% ========================= DSL =========================
|
||||||
|
|
||||||
\subsection{Δηλωτική γλώσσα περιγραφής τοπολογίας}
|
\subsection{Δηλωτική γλώσσα περιγραφής τοπολογίας}
|
||||||
|
|
||||||
@@ -312,6 +364,805 @@ Layer-3 διευθυνσιοδότηση και ενεργοποιημένη π
|
|||||||
ο σημασιολογικός έλεγχος πραγματοποιούνται σε \textbf{διαφορετικά στάδια}.
|
ο σημασιολογικός έλεγχος πραγματοποιούνται σε \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{Συμπεράσματα}
|
\subsection{Συμπεράσματα}
|
||||||
|
|||||||
Reference in New Issue
Block a user