De citit inainte de laborator:
Lectura optionala:
Materiale video optionale:
Pentru a simula o retea virtuala vom folosi Mininet. Vom avea nevoie de pachetele mininet si openvswitch-testcontroller pentru a simula reteaua si python-click si python-scapy pentru checker.
sudo apt update sudo apt install mininet openvswitch-testcontroller tshark python3-click python3-scapy xterm sudo pip3 install mininet
python-click
si python-scapy
fiind folosit Python 2.
În primele laboratoare am implementat protocoale de nivel DataLink pentru a comunica între două dispozitive. Astăzi vom face o implementare priliminara a protocolului IP de nivel Network pentru a permite comunicarea la distanță folosind dispozitive de rutare. În cadrul laboratorului vom implementa dată plane-ul unui router simplu. Dacă dată plane-ul se ocupă cu procesarea pachetelor, control plane-ul se referă la procesul de populare al tabelelor de rutare. Un router are mai multe interfețe și poate recepționa datagrame pe oricare dintre acestea. Routerul trebuie să transmită pachetul mai departe în funcție de mulțimea de reguli din tabela de rutare. În general, tabela de rutare conține următoarele informații:
Se cere implementarea procesului de forward pe baza unei tabele statice. Nu trebuie sa implmentati algoritmii de populare a tabelei de rutare precum RIP, OSPF, BGP.
Procesul de forward constă în primirea unui pachet, investigarea tabelei de rutare, descoprirea rutei corespunzătoare și rutarea pachetului, adică transmiterea pachetului mai departe conform rutei.
Rutele se găsesc în tabela de rutare și constau din două elemente:
partea de match: adresa de rețea destinație (adresă și mască de rețea) partea de acțiune: următorul dispozitiv de rutare (next hop) (sau interfața de ieșire)
În momentul în care un dispozitiv care rutează (un ruter) primește un pachet, extrage adresa IP destinație, localizează adresa de rețea destinație în tabela de rutare și apoi rutează (dirijează, retransmite) pachetul către următorul ruter (next hop). Procesul se reia pe următorul ruter până când pachetul ajunge la destinație.
Rutarea este un proces care are loc la nivelul 3 (Rețea) din stiva OSI, lucrând cu adresa IP.
Tabela de routare din cadrul laboratorului va fi structurata ca in exemplul de mai jos.
Prefix Mask Next hop Metric Interface 192.168.0.0 255.255.255.0 192.168.0.2 100 0 192.168.1.0 255.255.255.0 192.168.1.2 100 1 192.168.2.0 255.255.255.0 192.168.2.2 100 2 192.168.3.0 255.255.255.0 192.168.3.2 100 3
Ulterior, pentru bonus tabela va contine si intrari IPv6.
fd00::0:0 ffff:ffff:ffff:ffff:ffff:ffff:ffff:: fe80::0:2 100 0 fd00::1:0 ffff:ffff:ffff:ffff:ffff:ffff:ffff:: fe80::1:2 100 1 fd00::2:0 ffff:ffff:ffff:ffff:ffff:ffff:ffff:: fe80::2:2 100 2 fd00::3:0 ffff:ffff:ffff:ffff:ffff:ffff:ffff:: fe80::3:2 100 3
In cadrul laboratorului vom lucra cu pachete reale din internet. Astfel, vor fi folosite urmatoarele protocoale discutate la curs:
Header Ethernet
+-----------------+------------+-------------+ | Bytes 0-5 | Bytes 6-11 | Bytes 12-13 | +------------------------------+-------------+ | Destination MAC | Source MAC | EtherType | +-----------------+------------+-------------+
In cadrul laboratorului puteti folosi urmatoarea structura pentru un pachet Ethernet. Pentru campul EtherType ne intereseaza valoruile ETHERTYPE_IP
(0x0800) si ETHERTYPE_IPV6
(0x86DD).
Header IPv4
+----+---------------+---------------+---------------+---------------+ |Word| 1 | 2 | 3 | 4 | +----+---------------+---------------+---------------+---------------+ |Byte|0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9|0|1| +----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0|Version| IHL |Type of Service| Total Length | +----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 4| Identification |Flags| Fragment Offset | +----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 8| Time to Live | Protocol | Header Checksum | +----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 12| Source Address | +----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 16| Destination Address | +----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 20| | +----+ + | ...| Options | +----+ +-+-+-+-+-+-+-+-+ | 56| | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
In cadrul laboratorului puteti folosi urmatoarea structura pentru un pachet IPv4. De notat faptul ca astazi nu vom atinge decat campurile Time to Live si checksum dintr-o datagrama IPv4.
Implementarea routerului se va face in router.c. Datagramele primite/trimise de router vor avea urmatorul format:
typedef struct { int len; char payload[MAX_LEN]; int interface; } msg;
In scheletul routerului se gasesc doua functii: get_packet care primeste o datagrama si send_packet care trimite o datagrama pe o interfata specificata.
/* Blocking function, blocks if there is no packet to be received. On success returns 0 and populates m. m must be allocated! */ int get_packet(msg *m); int send_packet(int interface, msg *m);
Pentru a calcula suma de control a unui pachet IP puteti utiliza urmatoarea functie pusa la dispozitie in schelet. Aceasta functie este similara cu cea de data trecuta.
/* Computes the checksum of a buffer, we will pass a pointer * to a msg structure. In this structure we will set the * checksum to 0 when calculating it via ip_checksum */ uint16_t ip_checksum(void* vdata, size_t length);
Routerul trebuie sa implementeze algoritmul de longest prefix match. Etapele algoritmului sunt urmatoarele:
Procesul complet de forwarding este urmatorul:
Recomandam acest ghid pentru a invata sa utilizati Wireshark. Pentru tcpdump exista urmatoarea referinta.
1. Scheletul laboratorului se regaseste aici. Comanda de mai jos va creea topologia retelei, va lansa routerul, va rula testele pentru verificarea conectivitatii si va prezenta un prompt prin care se poate interactiona cu topologia si hostii.
sudo pkill ovs-test sudo python topo.py
Se poate interactiona cu hostii prin intermediul promptului mininet. Unde se pot rula comenzi precum ping, wget, nc etc. Comenzile trebuie prefixate cu numele hostului. De exemplu, pentru a lansa comanda ping 192.168.3.2 pe host1 vom folosi host1 ping 192.168.3.2. Mai multe detalii gasiti in sectiunea Testare
.
Pe router va fi rulat programul rezultat in urma rularii comenzii make. In cadrul laboratorului vom lucra in fisierul router.c.
python topo.py
si output-ul este afisat in /tmp/debug.txt. In cazul in care vreti sa il porniti manual, astati comanda router pkill router && ./router &>/tmp/debug.txt & in promptul mininet.
2.1. Implementati procesul de forward pentru IPv4. Tabela de rutare este reprezentata printr-un array si poate fi accesata la un index prin variabila rtable[i]. rtable_size este dimensiunea tabelei. Nu trebuie implementata parsarea fisierului rtable.txt. Structura de date utilizate pentru o intrare din tabela este rtable_entry din skel.h.
struct in_addr
are un singur membru, s_addr de tip unsigned long (uint32_t)
2.2. Completati header-ul Ethernet al pachetului receptat. Astfel, in header-ul Ethernet va trebui completat MAC-ul destinatiei in functie de adresa destinatiei din header-ul IP. De asemenea, trebuie completat si campul sursei cu adresa MAC a interfetei pe care urmeaza sa trimiteti pachetul. Nu trebuie implementata parsarea fisierului nei_table.txt. Structura de date utilizate pentru o intrare din tabela este nei_entry din skel.h.
Hostii vor avea urmatoarele adrese alocate:
Host IPv4 IPv6 Site-Local IPv6 Link-Local MAC host0 192.168.0.2 fd00::0:2 fe80::0:2 de:ad:be:ef:00:00 host1 192.168.1.2 fd00::1:2 fe80::1:2 de:ad:be:ef:00:01 host2 192.168.2.2 fd00::2:2 fe80::2:2 de:ad:be:ef:00:02 host3 192.168.3.2 fd00::3:2 fe80::3:2 de:ad:be:ef:00:03
get_interface_mac(int interface, uint8_t *mac)
intoarce adresa MAC a interfetei router-ului.
Pentru testare puteti folosi comanda ping sau nc din terminalele pentru orice host, generand trafic catre alt host. De exemplu, in promptul mininet puteti executa:
host1 ping 192.168.2.2
3. Pentru fiecare pachet IPv4 verificati checksum-ul; daca este gresit, pachetul va fi distrus.
4. Pentru fiecare pachet scadeti TTL-ul sau Hop Limit-ul; daca TTL-ul este pozitiv, recalculati checksum-ul.
5. Folosind tshark sau wireshark, observati schimbarile care au loc asupra pachetelor de la sursa la destinatie(TTL, Hop Limit, checksum, MAC, etc)
6. Calcule IPv4. Completati tabelul de mai jos pentru diferite retele IP.
7. BONUS: Sortati tabela de rutare in functie de prefix si metric.
8. BONUS: Implementati un algoritm cu o complexitate mai buna de O(N) pentru longest prefix match.
9. BONUS: Implementati procesul de forwarding pentru IPv6. Vom folosi tabela de rutare din rtable_ip6.txt
.
Pentru testare vom folosi utilitarele puse la dispozitie de linux pe cei 4 hosti precum si cele doua teste automate. Reteaua simulata are urmatoarea structura:
Testele vor verifica conectivitatea intre toate perechile de hosti atat pentru IPv4,cat si pentru IPv6(exclunzand loopback).
Exmplu de output pentru comanda python topo.py
root@l4:~/lab4-rezolvare# python topo.py *** Error setting resource limits. Mininet's performance may be affected. *** Creating network *** Adding controller *** Adding hosts: host0 host1 host2 host3 router *** Adding switches: *** Adding links: (host0, router) (host1, router) (host2, router) (host3, router) *** Configuring hosts host0 host1 host2 host3 router *** Starting controller c0 *** Starting 0 switches ping PASSED ping6 PASSED *** Starting CLI: mininet>
Din terminal vom putea rula terminale pentru fiecare host:
mininet> host1 xterm&
Din terminalul deschis de pe host1 vom putea rula wireshark
sudo wireshark&
De asemenea, putem rula routerul implementat manual:
mininet> router xterm&
Din terminalul deschis
killall router make # Vom putea vedea output-ul de la printf ./router
raise Exception( 'Could not find a default OpenFlow controller' )
Exception: Please shut down the controller which is running on port 6653:
uint32_t
#include <arpa/inet.h> uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort)
struct iphdr *ip_hdr = (struct iphdr *)(packet.payload + sizeof(struct ether_header));
tcpdump
ip addr show
ip_checksum(ip_hdr, sizeof(struct iphdr)); /* initial header checksum = 0 */