This is an old revision of the document!


Laboratorul 04 - Forwarding

Lectura laborator

De citit inainte de laborator:

Lectura optionala:

Materiale video optionale:

Inainte de laborator

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 python-click python-scapy xterm

Din incercarile noastre, Mininet nu functioneaza pe Windows Subsystem for Linux. Este recomandat sa folositi Linux nativ sau intr-un mediu virtualizat. La adresa https://ocw.cs.pub.ro/courses/pc/res/mv puteti gasi o masina virtuala cu Ubuntu 18.04.

Prezentare generala

Î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:

  • Interfata pe care trebuie sa trimita pachetul
  • Adresa IPv4 sau IPv6 a urmatorului hop
  • Prioritatea rutei(metric)

Se cere implementarea procesului de rutare pe baza unei tabele statice. (Nu trebuie sa implmentati algoritmii de populare a tabelei de rutare e.g. RIP, OSPF, BGP)

Procesul de forward

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

Protocoale utilizate

In cadrul laboratorului vom lucra cu pachete reale din internet. Astfel, vor fi folosite urmatoarele protocoale discutate la curs:

Ethernet

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).

Click to display ⇲

Click to hide ⇱

struct	ether_header {
	u_char	ether_dhost[6];
	u_char	ether_shost[6];
	u_short	ether_type;
};

IPv4

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.

Click to display ⇲

Click to hide ⇱

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u8	ihl:4,
		version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
	__u8	version:4,
  		ihl:4;
#else
#error	"Please fix <asm/byteorder.h>"
#endif
	__u8	tos;
	__u16	tot_len;
	__u16	id;
	__u16	frag_off;
	__u8	ttl;
	__u8	protocol;
	__u16	check;
	__u32	saddr;
	__u32	daddr;
	/*The options start here. */
};

struct iphdr este specific linux (aflat in <linux/ip.h>) pentru MacOS puteti folosi struct ip din <netinet/ip.h>.

IPv6 (optional)

Click to display ⇲

Click to hide ⇱

Header IPv6

+----+---------------+---------------+---------------+---------------+
|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| Traffic Class |           Flow Label                  |
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   4|         Payload Length        |  Next Header  |   Hop Limit   |
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   8|                                                               |
+----+                                                               +
|  12|                                                               |
+----+                         Source Address                        +
|  16|                                                               |
+----+                                                               +
|  20|                                                               |
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  24|                                                               |
+----+                                                               +
|  28|                                                               |
+----+                      Destination Address                      +
|  32|                                                               |
+----+                                                               +
|  36|                                                               |
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

In cadrul laboratorului puteti folosi urmatoarea structura pentru un pachet IPv6.

struct ip6_hdr
  {
    union
      {
        struct ip6_hdrctl
          {
            uint32_t ip6_un1_flow;   /* 4 bits version, 8 bits TC,
                                        20 bits flow-ID */
            uint16_t ip6_un1_plen;   /* payload length */
            uint8_t  ip6_un1_nxt;    /* next header */
            uint8_t  ip6_un1_hlim;   /* hop limit */
          } ip6_un1;
        uint8_t ip6_un2_vfc;       /* 4 bits version, top 4 bits tclass */
      } ip6_ctlun;
    struct in6_addr ip6_src;      /* source address */
    struct in6_addr ip6_dst;      /* destination address */
  };

struct ip6_hdr poate fi gasit in <netinet/ip6.h>.

Implementare data plane router

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:

  • Routerul cauta in tabela de rutare intrarile care fac match pe adresa destinatie a pachetului IPv4 sau IPv6 primit.
  • Dintre toate rutele pe care s-a facut match in etapa anterioara, este aleasa ruta cea mai specifica (adica ruta cu prefixul cel mai lung). Daca doua rute au aceeasi specificitate, se va folosi ruta cu cel mai mic metric.

Procesul complet de forwarding este urmatorul:

  1. Routerul primeste un pachet apeland functia get_packet.
  2. Routerul face LPM pentru a gasi urmatorul HOP.
  3. In cazul in care nici o intrare din tabela nu face match, ruterul arunca datagrama.
  4. Pentru IPv4, este verificat checksum-ul. Daca acesta este incorect, pachetul este aruncat.
  5. Routerul decrementeaza campul TTL din header-ul IPv4 sau Hop Limit pentru IPv6. In cazul in care TTL sau Hop Limit sunt 0 sau mai mici, pachetul este aruncat. Se recalculeaza checksum-ul la acest pas, doar pentru IPv4
  6. Routerul face update la adresa MAC sursa a pachetului in adresa proprie inainte de a il trimite la urmatorul HOP si adresa MAC destinatie a urmatorului HOP.
  7. Routerul foloseste functia send_packet pentru a trimite pachetul la next hop.

Debugging Wireshark & tcpdump

Recomandam acest ghid pentru a invata sa utilizati Wireshark. Pentru tcpdump exista urmatoarea referinta.

Cerinte laborator:

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 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.

In cazul in care apar probleme, consultati sectiunea FAQ.

Routerul este pornit automat la rularea comenzii 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.

Pentru structurile care au legatura cu IPv6 (e.g. proto) vom putea pune orice valoare deoarece nu le folosim la acest exercitiu.

Structura 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

Puteti folosi inet_pton a parsa adresele IPv4 sau IPv6 in binar. Pentru a parsa MAC-ul in binar puteti folosi functia hwaddr_aton pusa la dispozitie in skel.h.

Functia int 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)

Exercitii extra

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.

Testare

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

FAQ

  • Daca aveti eroarea de mai jos trebuie sa instalati openvswitch-testcontroller
    raise Exception( 'Could not find a default OpenFlow controller' ) 
  • In cazul in care aveti eroarea de mai jos rulati comanda pkill ovs-test
    Exception: Please shut down the controller which is running on port 6653:
  • Cum pot reprezenta o adresa IP in memorie?
    uint32_t 
  • Valorile primite pe retea nu coincid cu valorile la care ma asteptam.

  • Cum fac trecerea intre network si host byte order?
    #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)
  • Cum extrag header-ul IP dintr-un pachet de tip msg?
    struct iphdr *ip_hdr = (struct iphdr *)(packet.payload + sizeof(struct ether_header));
  • Cum pot vedea traficul de pe o interfata?
    tcpdump 
  • Cum afisez interfetele unui host?
    ip addr show 
  • Ce face daca se trimit multe pachete duplicate? make distclean din Linux si make distclean && make din cadrul terminalului deschis pentru router.
  • Cum folosesc ip_checksum?
     ip_checksum(ip_hdr, sizeof(struct iphdr)); /* initial header checksum = 0 */

Resurse Utile

pc/laboratoare/04.1647900789.txt.gz · Last modified: 2022/03/22 00:13 by vlad_andrei.badoiu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0