Tema 4 - Transport protocol

  • Termen de predare: Duminică, 17 Mai 2020, ora 23:00

Obiectivele temei

  • Dobândirea de cunoștințe legate de funcționarea subsistemului de networking în nucleul Linux.
  • Obținerea de deprinderi de lucru cu structurile de bază din subsistemul de networking în Linux.
  • Aprofundarea noțiunilor legate de protocoale de comunicație și rețelistică prin implementarea unui protocol într-o stivă de protocoale existentă.

Enunț

Implementați, în nucleul Linux, un protocol numit STP (SO2 Transport Protocol) de nivel rețea și transport care funcționează pe bază de datagrame (nu este orientat conexiune și nu deține elemente de control al fluxului).

Protocolul STP are rolul unui protocol de nivel transport (multiplexare pe bază de porturi) dar funcționează la nivelul 3 (Rețea) din stiva OSI, peste nivelul Legătură de Date.

Antetul STP este definit de structura struct stp_header:

struct stp_header {
        __be16 dst;
        __be16 src;
        __be16 len;
        __u8 flags;
        __u8 csum;
};

unde

  • len este lungimea pachetului în octeți (inclusiv header-ul);
  • dst și src sunt porturile destinație, respectiv sursă;
  • flags conține diverse flag-uri, pe moment nefolosite (marcate reserved);
  • csum este suma de control (checksum) a întregului pachet incluzând headerul; suma de control este calculată prin SAU exclusiv (XOR) între toți octeții.

Sockeții care folosesc acest protocol vor folosi familia AF_STP.

Protocolul trebuie să funcționeze direct peste Ethernet. Porturile folosite sunt între 1 si 65535. Portul 0 nu este folosit.

Definiția structurilor și macro-urilor aferente STP se găsesc în header-ul temei.

Detalii de implementare

Modulul de kernel va avea numele af_stp.ko.

Trebuie înregistrată o structură de tip net_proto_family, care să ofere operația de creat sockeți de tip STP. Sockeții proaspăt creați nu sunt asociați cu niciun port și nicio interfață și nu pot primi/trimite pachete. În această funcție, trebuie inițializat câmpul ops al socket-ului cu lista de operații specifice familiei STP. Acest câmp referă o structură proto_ops care trebuie să includă urmatoarele funcții:

  • release: eliberează un socket de tip STP
  • bind: asociază un socket cu un port (eventual și o interfață) pe care se vor primi/trimite pachete:
    • pot exista sockeți bind-uiți doar pe un port (nu și pe o interfață)
    • sockeții asociați doar cu un port vor putea primi pachetele trimise către acel port pe toate interfețele (analog cu sockeții UDP asociați doar cu un port); acești sockeți nu pot trimite pachete pentru că nu se poate specifica interfața de pe care să fie trimise prin API-ul standard de sockeți
    • nu se pot bind-ui doi sockeți pe aceeași combinație port-interfață:
      • dacă există un socket deja bind-uit cu un port și o interfață atunci nu se poate bind-ui un al doilea socket pe același port și aceeași interfață sau fără o interfață specificată
      • dacă există un socket deja bind-uit cu un port dar fără o interfață specificată atunci nu se poate bind-ui un al doilea socket pe același port (cu sau fără o interfață specificată)
    • recomandăm folosirea unui hash table pentru bind în loc de alte structuri de date (listă, array); în nucleu există o implementare de hash table în header-ul hashtable.h
  • connect: asociază un socket cu un port și o adresă hardware (adresă MAC) remote către care se vor trimite/dinspre care se vor primi pachete:
    • acest lucru ar trebui să permită operații de send/recv pe socket în loc de sendmsg/recvmsg sau sendto/recvfrom
    • odată conectați cu un host, sockeții nu vor mai accepta pachete decât de la host-ul respectiv
    • odată conectați, sockeții nu mai pot fi deconectați
  • sendmsg, recvmsg: se trimite, respectiv se primește o datagramă pe un socket STP:
    • pentru partea de receive, metainformații despre host-ul care a trimis pachetul se pot păstra în câmpul cb din sk_buff
  • poll: va trebui folosită funcția predefinită datagram_poll
  • pentru restul operațiilor vor trebui folosite stub-urile predefinite în kernel (sock_no_*)
static const struct proto_ops stp_ops = {
        .family = PF_STP,
        .owner = THIS_MODULE,                                                                                                                         
        .release = stp_release,
        .bind = stp_bind,
        .connect = stp_connect,
        .socketpair = sock_no_socketpair,
        .accept = sock_no_accept,
        .getname = sock_no_getname,
        .poll = datagram_poll,
        .ioctl = sock_no_ioctl,
        .listen = sock_no_listen,
        .shutdown = sock_no_shutdown,
        .setsockopt = sock_no_setsockopt,
        .getsockopt = sock_no_getsockopt,
        .sendmsg = stp_sendmsg,
        .recvmsg = stp_recvmsg,
        .mmap = sock_no_mmap,
        .sendpage = sock_no_sendpage,
};

Operațiile pe sockeți folosesc un tip de adrese numit sockaddr_stp, tip definit în header-ul temei. Pentru operația de bind se vor lua în considerare doar portul și indexul interfeței pe care se bind-uiește socketul. Pentru operația de receive se vor completa doar câmpurile addr și port din structură cu adresa MAC a host-ului care a trimis pachetul și cu portul de pe care a fost trimis. De asemenea, la trimiterea unui pachet, host-ul destinație se va obține din câmpurile addr și port ale acestei structuri.

Trebuie să înregistrați o structură packet_type, folosind apelul dev_add_pack pentru a putea primi pachete STP de pe layer-ul de rețea.

Protocolul va trebui să ofere o interfață prin sistemul de fișiere procfs pentru statistici despre pachetele trimise/primite. Fișierul trebuie să se numească /proc/net/stp_stats, specificat prin macro-ul STP_PROC_FULL_FILENAME din header-ul temei. Formatul trebuie să fie de tip tabel simplu cu 2 rânduri: pe primul rând header-ul tabelului, iar pe al doilea rând statisticile corespunzătoare coloanelor. Coloanele tabelului trebuie să fie, în ordine:

RxPkts HdrErr CsumErr NoSock NoBuffs TxPkts

unde:

  • RxPkts - numărul de pachete primite
  • HdrErr - numărul de pachete primite cu erori în header (pachete prea scurte sau cu porturi sursă sau destinație 0)
  • CsumErr - numărul de pachete primite cu erori de checksum
  • NoSock - numărul de pachete primite pentru care nu s-a găsit un socket destinație
  • NoBuffs - numărul de pachete primite care nu au putut fi recepționate pentru că coada de receive a socket-ului era plină
  • TxPkts - numărul de pachete trimise

Pentru crearea, respectiv ștergerea intrării precizate de STP_PROC_FULL_FILENAME recomandăm folosirea funcțiilor proc_create și proc_remove.

Exemple de implementări de protocoale

Pentru exemple de implementare de protocoale, vă sugerăm în special implementarea de sockeți PF_PACKET și diversele funcții din implementarea UDP sau implementarea IP.

Testare

Pentru simplificarea procesului de corectare a temelor, dar și pentru a reduce greșelile temelor trimise, corectarea temelor se va face automat cu ajutorul testelor publice care se găsesc în noua infrastructură. Pentru testarea locală folosiți următoarele comezi:

$ git clone https://github.com/linux-kernel-labs/linux.git
$ cd linux/tools/labs
$ LABS=assignments/4-stp make skels 
# dezvoltarea temei se va efectua în directorul 4-stp/
$ make build
$ make copy
$ make boot

tcpdump

Pentru depanarea pachetelor trimise, puteți folosi utilitarul tcpdump. Testele folosesc interfața de loopback; pentru a urmări pachetele trimise puteți folosi o linie de comandă de forma:

tcpdump -i lo -XX

Puteți folosi o versiune statică de tcpdump. Pentru a o avea în PATH în mașina virtuală, copiați acest fișier în qemu-so2/fsimg/bin. Creați directorul dacă nu există. Nu uitați să dați fișierului tcpdump permisiuni de execuție:

# În directorul qemu-so2
mkdir fsimg/bin
wget http://elf.cs.pub.ro/so2/res/teme/tcpdump -O fsimg/bin/tcpdump
chmod 755 fsimg/bin/tcpdump

Sfaturi

Pentru a vă mări șansele de a obține nota maximă, citiți și respectați coding style-ul kernelului Linux descris din documentul Coding Style.

De asemenea, folosiți următoarele tool-uri de analiza statică pentru a verifica codul:

checkpatch.pl

  •  /path/to/linux-4.9.11/scripts/checkpatch.pl --no-tree --terse -f /path/to/your/src-file.c 

sparse

  •  sudo apt-get install sparse 
  •  cd /path/to/linux-4.9.11 
     make C=2 /path/to/your/src-file.c 

cppcheck

  •  sudo apt-get install cppcheck 
  •  cppcheck /path/to/your/src-file.c 

Notare

Tema valorează 1.5 puncte.

Depunctări

Depunctările generale pentru teme se găsesc pe pagina de Indicații generale.

În cazuri excepționale (tema trece testele prin nerespectarea cerințelor) și în cazul în care tema nu trece toate testele se poate scădea mai mult decât este menționat mai sus.

Submitere

Arhiva temei va fi submisă pe vmchecker, în conformitate cu precizările din pagina de reguli.

Din interfața vmchecker alegeți opțiunea Transport protocol, aferentă acestei teme.

Resurse

Resursele temei se găsesc și în repo-ul so2-assignments de pe GitHub. Repo-ul conține și un script Bash care vă ajută să vă creați un repository privat pe instanța de GitLab a facultății. Urmăriți indicațiile din README și de pe pagina de Wiki dedicată pentru git.

Întrebări

Pentru întrebări legate de temă puteți consulta arhivele listei de discuții sau puteți trimite un e-mail (trebuie să fiți înregistrați). Vă rugăm să urmăriți și să respectați indicațiile de utilizare a listei.

so2/teme/tema4.txt · Last modified: 2020/05/03 22:28 by constantin.ghioc
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