Laboratorul 01 - Networking warmup

Lectură laborator

0. Despre laboratoarele de PCom

Laboratoarele de PCom presupun cunoștințe de USO și PC. În cadrul laboratoarelor vom implementa și utiliza protocoalele de comunicație(set de reguli bine definite pe care interlocutorii le urmeaza) discutate la curs. Vom vedea cum funcționează internetul și înțelege cum sunt dezvoltate aplicațiile peste acesta.

Înaintea oricărui laborator va trebui citită lectură recomandată, în cazul de față video-ul Sending digital information over a wire (4 minute) si capitolul The reference models din carte. Prima parte a laboratorului este de tip tutorial și se recomandă trecerea rapidă prin textul laboratorului. În a doua parte a laboratorului se găsesc exerciții. Exercițiile pot fi rezolvate atât în C cât și C++, cu toate aceste recomandăm implementări în versiuni moderne de C++. Recomandăm Visual Studio Code cu extensia de C/C++ pentru laboratoare și teme. Atât temele cât și laboratoarele se vor face pe Linux.

1. Noțiuni teoretice

Complexitatea internetului a crescut rapid, dacă la începutul anilor 1970 internetul se rezuma la comunicarea peste un cablu între două dispozitive printr-un protocol simplu, în doar câțiva ani complexitatea a crescut enorm. In figura de mai jos vedem precursorul internetului de astăzi, ARPANET.

Vom vedea cum plecând de la ceva foarte simplu, precum trimiterea unui bit(0 sau 1) folosind un semnal electric care poate lua doar două valori 0V sau 5V, ajungem la a trimite mesaje în partea cealaltă a globului și interconectarea a zeci de miliarde de dispozitive.

Pentru a modela cât mai ușor arhitectură internetului, cercetătorii de la acel timp au propus diferite modele de referință, dintre acestea Open Systems Interconnection (OSI), modelul propus de Huber Zimmemann, a fost cel mai influent. Totuși, în practică, modelul dominant de referință folosit este TCP/IP. În figura de mai jos putem vedea cele două modele de referință și o serie de protocoale la fiecare nivel. La laborator vom avea o abordare bottom-up. Vom porni de la protocoale simple pentru a conecta două calculatoare și vom ajunge la a implementa protocoale de nivel aplicație, precum HTTP, folosit astăzi mai ales de web browsers. Pentru o buna dezvoltare a protocoalelor, acestea sunt descrise in documente numite Request for Comment (RFC), de exemplu RFC 791 descrie protocolul IP.

2. Noțiuni generale Linux

În cadrul laboratorului de Protocoale vom folosi sisteme de operare bazate pe Linux. În continuare vom reaminti câteva cunoștințe utile pentru următoarele laboratoare și pentru teme.

  1. ls - listarea conținutului unui director; opțiuni comune:
    • -a (afișează și fișiere ascunse)
    • -l (afișează detalii despre fișiere)
    • -h (afișează dimensiunea fișierelor în format “human readable”)
  2. cd - change directory; schimba directorul curent
  3. pwd - print working directory
  4. mkdir - make directory
  5. rmdir - remove directory (funcționează doar pentru directoare goale)
  6. rm - remove; opțiuni comune:
    • -r (șterge directoare și conținutul lor în mod recursiv)
    • -f (force; ignora argumente greșite, șterge fără confirmare din partea utilizatorului)
    • –no-preserve-root ( ͡° ͜ʖ ͡°)
  7. cp - copy
  8. mv - move
  9. cat - vizualizarea conținutului unui fișier
  10. less - vizualizarea conținutului unui fișier pagina cu pagina
  11. head - vizualizarea începutului unui fișier
  12. tail - vizualizarea finalului unui fișier
  13. diff - vizualizare diferențe intre fișiere
  14. hexdump - vizualizarea conținutului unui fișier în format ASCII/octal/hexazecimal
  15. dd - utilitar folosit pentru a genera fișiere de o anumita dimensiune (exemple de folosire mai jos)

Următorul video prezintă funcționalitatea acestor comenzi în linia de comanda

3. Networking de mână

Accesarea unei pagini Web

1. În orice browser accesați website-ul http://example.com/

2. Vom realiza același lucru de mână:

  • Din CLI rulați telnet example.com http. Această comandă deschide o conexiune (flux de bytes) către un alt calculator (numit example.com) care rulează un serviciu http, protocolul Hyper-Text Transfer Protocol folosit de World Wide Web. Dacă totul a mers corect, veți vedea următoarele:
  • Scrieți GET / HTTP/1.1 - Path-ul către resursa pe care vrem să o accesăm
  • Scrieți Host: example.com - Specifică host-ul, partea după http
  • Scrieți Connection: close - Specifică serverului că nu vom mai trimite cereri după această.
  • Apăsați enter. Acesta trimite o linie goală ce reprezintă faptul că am terminat cu cererea HTTP.
  • Dacă totul a mers bine, veți vedea un răspuns asemănător cu ce ați văzut în browser.

Server simplu

Am văzut că telnet poate acționa ca un client care se conectează la un server. Vom vedea acuma cum este să fim un server.

  1. Într-un terminal vom rula netcat -v -l -p 9090 pentru a porni un simplu server ce ascultă pe portul 9090
  2. În alt terminal rulăm telnet localhost 9090 pentru a ne conecta local, pe calculator, la serverul care rulează pe portul 9090
  3. Dacă totul a mers bine, vom vedea în al doilea terminal următorul mesaj: Connection from localhost 53500 received!
  4. În oricare dintre ferestre, putem scrie orice, și după ce apăsăm enter, ajunge și în partea cealaltă.

4. Noțiuni generale C/C++


Va recomandăm următorul crash course de C pentru a vă reaminti concepte precum structuri, pointeri si alocarea de memorie.

Compilare

Sistemul de operare Unix/Linux dispune de un compilator pentru compilarea fișierelor scrise în C, numit gcc. Pentru C++ vom folosi g++. Să presupunem că avem un fișier sursă numit hello.c. Compilarea standard a acestui fișier este

gcc hello.c

Ca rezultat se obține un fișier a.out care se poate execută. Dacă însă compilăm folosind

gcc hello.c -o hello

se va obține fișierul executabil hello.

Compilatorul gcc suportă o serie de opțiuni de compilare:

  • Opțiunea -g va permite obținerea unui executabil care va conține informații de debug. Această opțiune este utilă în combinație cu un program de debugging cum ar fi gdb.

Pentru a detecta accesele invalide la memorie, vom compila programul nostru cu ASAN folosind următorul flag: -fsanitize=address. Mai mult, pentru a evita undefined behavior vom folosi -fsanitize=undefined. Aceste opțiuni sunt utile pentru a ne asigura că programul scris de noi nu are bug-uri ascunse.

Debugging

Cum networking-ul, în general, necesită programare low level, va trebui să știm cum să folosim un debugger pentru a depăna probleme într-un mod rapid. Va recomandăm articolul Debugging pentru a va reaminti de cum folosim GDB atât din CLI cât și prin intermediul unui IDE.

Apeluri de sistem pentru operații cu fișiere

Vom folosi următoarele funcții pentru a interacționa cu fișierele pe Linux: open, close, read, write și lseek. API-ul lor este descris în manual man read.2. Acestea lucrează cu ceea ce numim file descriptors (fd). Simplist, un file descriptor este un integer ce reprezintă un identificator în tabela de fișiere a unui program. De exemplu, dacă creăm deschidem un fișier, identificatorul ar putea fi numărul 4.

Un program, are inițial 3 file descriptori existenți: 0 - stdin, 1 - stdout si 2 - stderr. Dacă deschidem un fișier cu open, o sa primim ca file descriptor valoare 3.

Exemplu

Avem un exemplu de folosire a funcțiilor de acces la fișiere în programul de mai jos, care copiază fișierul ”sursă” în fișierul ”destinație”. Urmăriți comentările pentru a înțelege mai bine funcționalitatea.

Click to display ⇲

Click to hide ⇱

example.c
#include <unistd.h> /* pentru open(), exit() */
#include <fcntl.h> /* O_RDWR */
#include <stdio.h> /* perror() */
#include <errno.h>
#include <stdlib.h>
 
void fatal(char * mesaj_eroare)
{
    perror(mesaj_eroare);
    exit(0);
}
 
int main(void)
{
    int miner_sursa, miner_destinatie;
    int copiat;
    char buf[1024];
 
    /* Open primeste ca argumente path-ul catre un fisier
       si un flag, in cazul acesta flag-ul speicica ca urmeaza
       doar sa citim din fisier.
    */
    miner_sursa = open("sursa", O_RDONLY);
    /* O_CREAT este un flag special care specifica faptul ca daca
       fisierul nu exista, vom creea unul noi cu permisiunile 0644
    */
    miner_destinatie = open("destinatie", O_WRONLY | O_CREAT, 0644);
 
    /* Daca open returneaza o valoare mai mica de 0, atunci inseamna
       ca avem o eroare */
    if (miner_sursa < 0 || miner_destinatie < 0)
        fatal("Nu pot deschide un fisier");
 
    /* Fisierele sunt acuma identificate prin cele 2 int-uri,
       miner_sursa si miner_destinatie 
    */
 
    /* Cu ajutorul functie read citim din fisier, in cazul de fata
       citim din fisierul identificat prin miner_sursa date
       si le punem in buf. Citim maxim sizeof(buf), adica 1024 */
    while ((copiat = read(miner_sursa, buf, sizeof(buf)))) {
 
        /* read returneaza numarul de bytes cititi */
 
        if (copiat < 0)
            fatal("Eroare la citire");
 
         /* read muta si ceea ce numim cursorul, daca de exemplu
           fisierul are 2048 de bytes, primul apel de read va muta
           cursorul pe pozitia 1024, o citire ulterioara va returna
           date de la pozitia 1024 in sus. lseek este o functie speciala
           pentru a interactiona cu cursorul
         */
 
        /* write este similar cu read */
        copiat = write(miner_destinatie, buf, copiat);
        if (copiat < 0)
            fatal("Eroare la scriere");
    }
 
    /* Este good practice sa inchidem fisierele */
    close(miner_sursa);
    close(miner_destinatie);
    return 0;
}

Exerciții


  1. (2p) Am văzut faptul că in modelul de referință TCP/IP putem partiționă comunicarea în cinci nivele. Încadrați următoarele operații în unul dintre nivelele discutate. (de ce?):
    • Transmisie bit-to-bit între două dispozitive
    • Criptarea unui mesaj
    • Căutarea rutei optime într-o rețea
    • Adăugarea unui număr de secvență fiecărui pachet
    • Un router într-o rețea
    • Un middlebox într-o rețea care face deep packet inspection(DPI) pentru analiza traficului contra malware
  2. (3p) Vrem să ne pregătim pentru a trimite date in format binar peste un mediu de transmisie. În fișierul de aici avem un array de structuri de tipul Packet in format binar. Realizați un program în C/C++ care să citească array-ul cu elemente de tip Packet din acest fișier și să afișeze conținutul din payload al fiecărei intrări.
       struct Packet {
         char  payload[100];
         int   sum;
         int   size;
       };  
  3. (2p) Asemănător cu ce am făcut mai sus, vom folosi telnet pentru a ne conecta la următorul server TCP: towel.blinkenlights.nl. Acest server trimite un șir de biți către noi, iar telnet îl afișează pe ecran (stdout). Mai multe astfel de exemple le găsiți aici
  4. (3p) Pentru a ne obișnui cu programarea low level, scrieți un utilitar similar cu cat în C/C++. Acesta trebuie să afișeze conținutul unui fișier, linie cu linie la stdout.
pc/laboratoare/01.txt · Last modified: 2022/03/02 16:32 by radu.ciobanu
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