Differences

This shows you the differences between two versions of the page.

Link to this comparison view

so2:teme:tema4 [2018/01/05 20:52]
adrian.stanciu [Enunț]
so2:teme:tema4 [2020/05/03 22:28] (current)
constantin.ghioc [Tema 4 - Transport protocol]
Line 1: Line 1:
 +
 ~~NOTOC~~ ~~NOTOC~~
  
-====== Tema 4 - Driver de sistem de fișiere ​======+====== Tema 4 - Transport protocol ​======
  
-  * Termen de predare: ** TBA **+  * 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ț ===== ===== Enunț =====
  
-Să se scrie un modul de kernel ​care să implementeze sistemul ​de fișiere ''​PITIX'',​ versiunea 2. Acest sistem de fișiere va oferi suport doar pentru fișiere ​și directoare. Nu se vor implementa operațiile ​de suport pentru link-uri hard sau simbolice. De asemenea, nu se vor implementa operațiile de suport pentru fișiere speciale (pipe-uri, dispozitive caracter sau bloc). Practic va trebui să implementați următoarele operații:  +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).
-  * pentru directoare: ''​lookup'',​ ''​unlink'',​ ''​mkdir'',​ ''​rmdir'',​ ''​iterate''​ +
-  * pentru fișiere: ''​create'',​ ''​truncate'',​ funcția de bitmap (vezi [[http://​lxr.free-electrons.com/​source/​fs/​minix/​inode.c?​v=4.9#​L375| minix_get_block]])+
  
-Restul ​funcțiilor fie au implementări generice în kernelfie nu trebuie să le implementați+Protocolul STP are rolul unui protocol de nivel transport (multiplexare pe bază de porturi) dar funcționează la nivelul 3 (Rețea) din [[http://​en.wikipedia.org/​wiki/​OSI_model | stiva OSI]]peste nivelul Legătură de Date.
  
-Structura pe disk a sistemului de fișiere ​este: +Antetul STP este definit de structura ''​struct stp_header''​:
  
-{{ http://​elf.cs.pub.ro/​so2/​res/​teme/​pitix-layout.png }} 
  
-unde: +  struct stp_header { 
-  * ''​Superblock''​ conține superblocul (4096 octeți) +          ​__be16 dst; 
-  * ''​Imap''​ conține harta de biți (bitmap) a blocurilor ocupate de inode-uri (1 bloc) +          ​__be16 src; 
-  * ''​Dmap''​ conține harta de biți (bitmap) a blocurilor ocupate de date (1 bloc) +          ​__be16 len; 
-  * ''​Izone''​ conține inode-urile (32 blocuri) +          __u8 flags; 
-  ​* ''​Dzone''​ conține datele (conținutul efectiv al fișierelor) (8*block_size blocuri)+          __u8 csum; 
 +  ​};
  
- 
-Superblocul (**pe disc**) este descris de următoarea structură: ​ 
-<code cpp> 
-struct pitix_super_block { 
-        unsigned long magic; 
-        __u8 version; 
-        __u8 block_size_bits;​ 
-        __u8 imap_block; 
-        __u8 dmap_block; 
-        __u8 izone_block;​ 
-        __u8 dzone_block;​ 
-        __u16 bfree; 
-        __u16 ffree; 
-}; 
-</​code>​ 
  
 unde 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.
  
-  * ''​magic''​ trebuie să fie inițializat cu ''​PITIX_MAGIC''​  +Sockeții care folosesc acest protocol vor folosi familia ​''​AF_STP''​.
-  * ''​version''​ trebuie să fie inițializat cu 2 (''​PITIX_VERSION''​) +
-  * ''​block_size_bits''​ este dimensiunea blocului în puteri ale lui doi; dimensiunea blocului poate să fie 512, 1024, 2048, sau 4096  +
-  * ''​imap_block''​ este numărul blocului (relativ la dispozitiv) pentru vectorul de biți folosit pentru alocarea/​eliberarea inode-urilor  +
-  * ''​dmap_block''​ este numărul blocului (relativ la dispozitiv) pentru vectorul de biți folosit pentru alocarea/​eliberarea blocurilor de date  +
-  * ''​izone_block''​ este numărul primului bloc (relativ la dispozitiv) al zonei de inode-uri  +
-  * ''​dzone_block''​ este numărul primului bloc (relativ la dispozitiv) al zonei de date  +
-  * ''​bfree''​ este numărul de blocuri libere (nealocate)  +
-  * ''​ffree'' ​este numărul de inode-uri libere (nealocate) ​+
  
-Inode-urile vor fi stocate în zona de inode-uri și sunt descrise de următoarea structură:  +Protocolul trebuie să funcționeze direct peste Ethernet. Porturile folosite ​sunt între 1 si 65535. Portul 0 nu este folosit.
-<code cpp> +
-struct pitix_inode { +
-        __u32 mode; +
-        uid_t uid; +
-        gid_t gid; +
-        __u32 size; +
-        __u32 time; +
-        __u16 direct_data_blocks[INODE_DIRECT_DATA_BLOCKS];​ +
-        __u16 indirect_data_block;​ +
-}; +
-</​code>​+
  
-unde +Definiția structurilor și macro-urilor aferente STP se găsesc în [[http://​elf.cs.pub.ro/​so2/​res/​teme/​stp.h | header-ul temei]].
  
-  * ''​mode''​ reprezintă drepturile ​de acces și tipul inode-ului (fișier sau director) așa cum sunt reprezentate în kernel +==== Detalii ​de implementare ====
-  * ''​uid''​ reprezintă UID-ul așa cum este el reprezentat în kernel +
-  * ''​gid''​ reprezintă GID-ul așa cum este el reprezentat în kernel +
-  * ''​size''​ reprezintă dimensiunea fișierului/​directorului +
-  * ''​time''​ reprezintă timpul de modificare așa cum este el reprezentat în kernel +
-  * ''​direct_data_blocks''​ este un vector (de dimensiune ''​INODE_DIRECT_DATA_BLOCKS''​) ce conține indecșii blocurile de date directe +
-  * ''​indirect_data_block''​ este indexul unui bloc de date indirect+
  
-Indexul unui bloc de date (direct sau indirect) indică numărul acelui bloc de date relativ la zona de date (''​Dzone''​). +Modulul ​de kernel va avea numele **af_stp.ko**.
-Intrările nefolosite trebuie setate pe 0. Primul bloc, cel cu index 0, este tot timpul alocat când se face formatarea. Acest bloc nu va putea fi folosit și, în consecință,​ valoarea 0 într-un element din vectorul ''​direct_data_blocks''​ înseamnă bloc liber.+
  
-Directoarele au asociate ​un singur bloc de date în care vor fi stocate intrările de director. Acestea sunt descrise ​de următoarea structură:  +Trebuie înregistrată o structură de tip [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​include/​linux/​net.h#​L200 | 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 [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​include/​linux/​net.h#​L124 | câmpul ops al socket-ului]] cu lista de operații specifice familiei STP. Acest câmp referă o structură [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​include/​linux/​net.h#​L136 | proto_ops]] care trebuie să includă urmatoarele funcții: 
-<​code ​cpp+  * ''​release'':​ eliberează ​un socket de tip STP 
-struct ​pitix_dir_entry ​+  * ''​bind'':​ asociază un socket cu un port (eventual și o interfață) pe care se vor primi/​trimite pachete: 
-        ​__u32 ino; +    * pot exista sockeți bind-uiți doar pe un port (nu și pe o interfață) 
-        ​char name[PITIX_NAME_LEN];​+    * 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 [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​include/​linux/​hashtable.h | 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 [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​include/​linux/​skbuff.h#​L650 | 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_*''​) 
 + 
 +<​code>​ 
 + 
 +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,​
 }; };
 +
 </​code>​ </​code>​
 +Operațiile pe sockeți folosesc un tip de adrese numit ''​sockaddr_stp'',​ tip definit în [[http://​elf.cs.pub.ro/​so2/​res/​teme/​stp.h | 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.
  
-unde +Trebuie să înregistrați o structură [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​include/​linux/​netdevice.h#​L2222 | packet_type]],​ folosind apelul [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​net/​core/​dev.c#​L386 | dev_add_pack]] pentru a putea primi pachete STP de pe layer-ul de rețea.
  
-  * ''​ino'' ​reprezintă numărul ​inode-ului fișierului sau directorului;​ acest număr este un index în zona de inode-uri  +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 [[http://​elf.cs.pub.ro/​so2/​res/​teme/​stp.h | 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:<​code>​ 
-  * ''​name'' ​reprezintă numele ​fișierului sau al directorului;​ lungimea maximă a numelui este 16 octeți (''​PITIX_NAME_LEN''​); dacă lungimea numelui e mai mică decât 16 octețiatunci numele va fi terminat cu caracterul ASCII ce are codul 0 (la fel ca la șirurile ​de caractere) ​+RxPkts HdrErr CsumErr NoSock NoBuffs TxPkts 
 +</​code>​ 
 +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 ​socket-ului era plină 
 +  * ''​TxPkts'' ​- numărul de pachete trimise 
 +Pentru crearearespectiv ​ștergerea intrării precizate ​de ''​STP_PROC_FULL_FILENAME''​ recomandăm folosirea funcțiilor [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​include/​linux/​proc_fs.h#​L30 | proc_create]] și [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​fs/​proc/​generic.c#​L636 | proc_remove]].
  
-Toate valorile numerice sunt stocate pe disc în CPU byte-order.+===== Exemple de implementări de protocoale =====
  
-Directorul rădăcină va avea alocat inode-ul 0 și blocul de date 0.+Pentru exemple de implementare de protocoale, vă sugerăm în special implementarea de sockeți [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​net/​packet/​af_packet.c | PF_PACKET]] ​și diversele funcții din [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​net/​ipv4/​udp.c| implementarea UDP]] sau [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​net/​ipv4/​af_inet.c | implementarea IP]].
  
-Pentru ​simplificarela ''​mkdir'' ​nu este necesară crearea intrărilor ''​.''​ (dot)  ș''​..''​ (dot dot) în noul director; checker-ul se folosește de această presupunere.+===== Testare ===== 
 +/* 
 +Pentru ​simplificarea procesului de corectare a temelordar și pentru a reduce greșelile temelor trimise, corectarea temelor se va face automat cu ajutorul unor [[http://​elf.cs.pub.ro/​so2/​res/​teme/​5-stp.zip| teste publice]]. 
 +Indicații despre utilizarea suitei de teste se găsesc în fișierul ​''​README'' ​din cadrul arhivei. 
 +*/ 
 +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țurmătoarele comezi: 
 +<code bash> 
 +$ 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 
 +</​code>​
  
-În [[http://​elf.cs.pub.ro/​so2/​res/​teme/​pitix.h| header-ul temei]] găsiți structurile descrise anterior împreună cu unele macro-uri utile și declarațiile principalelor funcții de implementat. 
  
-Modulul de kernel va avea numele **pitix.ko**.+==== tcpdump ====
  
-===== Testare =====+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:<​code>​ 
 +tcpdump -i lo -XX 
 +</​code>​
  
-Pentru simplificarea procesului de corectare a temelor, dar șpentru a reduce greșelile temelor trimise, corectarea temelor se va face automat cu ajutorul unor [[http://​elf.cs.pub.ro/​so2/​res/​teme/​4-pitix.zip|teste publice]]. Indicații despre utilizarea suitei de teste se găsesc ​în fișierul ''​README'' ​din cadrul arhivei.+Putețfolosi o versiune statică de [[http://​elf.cs.pub.ro/​so2/​res/​teme/​tcpdump ​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:<​code>​ 
 +# Î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 
 +</​code>​
  
 ==== Sfaturi ==== ==== Sfaturi ====
Line 126: Line 158:
 ==== Notare ==== ==== Notare ====
  
-Datorită nivelului de dificultate mai ridicat, tema valorează ​două puncte.+Tema valorează ​1.5 puncte.
  
 ==== Depunctări ==== ==== Depunctări ====
Line 138: Line 170:
 Arhiva temei va fi submisă pe vmchecker, în conformitate cu [[:​so2:​reguli-notare#​reguli_de_trimitere_a_temelor|precizările din pagina de reguli]]. Arhiva temei va fi submisă pe vmchecker, în conformitate cu [[:​so2:​reguli-notare#​reguli_de_trimitere_a_temelor|precizările din pagina de reguli]].
  
-Din interfața vmchecker alegeți opțiunea ''​Sistem de fișiere'',​ aferentă acestei teme.+Din interfața vmchecker alegeți opțiunea ''​Transport protocol'',​ aferentă acestei teme. 
  
 ===== Resurse ===== ===== Resurse =====
  
-  * [[http://elf.cs.pub.ro/​so2/​res/teme/pitix.hheader-ul temei]] +  ​* Cursuri 
-  * [[so2:​laboratoare:lab08|Laboratorul 8. Drivere sisteme de fișiere (partea întăi)]] +    ​* [[http://ocw.cs.pub.ro/​so2/​cursuri/curs10 ​Curs 10 Gestiunea rețelei]] 
-  * [[so2:laboratoare:​lab09|Laboratorul ​9. Drivere sisteme de fișiere (partea a doua)]] +  ​* Laboratoare 
-  * [[http://lxr.free-electrons.com/​source/​fs/minix/?v=4.9|Sursele sistemului de fișiere Minix]]+    ​* [[http://​ocw.cs.pub.ro/​so2/​laboratoare/​lab10 ​Laborator 10 - Networking]] 
 +  ​* Sursele nucleului Linux 
 +    ​* [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​net/​packet/​af_packet.c | Implementarea sockeților PF_PACKET]] 
 +    * [[http://elixir.free-electrons.com/​linux/​v4.9.11/source/net/ipv4/udp.c | Implementarea protocolului UDP]] 
 +    * [[http://​elixir.free-electrons.com/​linux/​v4.9.11/​source/​net/​ipv4/​af_inet.c ​Implementarea protocolului IP]] 
 +  * Understanding Linux Network Internals 
 +    * capitolele 8-13 
 +  * [[http://​elf.cs.pub.ro/​so2/​res/​teme/​stp.h | Header-ul temei]]
  
 <note important>​ <note important>​
-Resursele temei ([[https://​github.com/​systems-cs-pub-ro/​so2-assignments/​tree/​master/​4-pitix-v2|4-pitix-v2]]) ​se găsesc și în repo-ul [[https://​github.com/​systems-cs-pub-ro/​so2-assignments|so2-assignments]] de pe GitHub. Repo-ul conține și un [[https://​github.com/​systems-cs-pub-ro/​so2-assignments/​blob/​master/​so2-create-repo.sh|script Bash]] care vă ajută să vă creați un repository privat pe instanța de [[https://​gitlab.cs.pub.ro|GitLab]] a facultății. Urmăriți indicațiile din [[https://​github.com/​systems-cs-pub-ro/​so2-assignments/​blob/​master/​README.md|README]] și de pe [[:​so2:​teme:​folosire-gitlab| pagina de Wiki dedicată pentru git]].+Resursele temei se găsesc și în repo-ul [[https://​github.com/​systems-cs-pub-ro/​so2-assignments|so2-assignments]] de pe GitHub. Repo-ul conține și un [[https://​github.com/​systems-cs-pub-ro/​so2-assignments/​blob/​master/​so2-create-repo.sh|script Bash]] care vă ajută să vă creați un repository privat pe instanța de [[https://​gitlab.cs.pub.ro|GitLab]] a facultății. Urmăriți indicațiile din [[https://​github.com/​systems-cs-pub-ro/​so2-assignments/​blob/​master/​README.md|README]] și de pe [[:​so2:​teme:​folosire-gitlab| pagina de Wiki dedicată pentru git]].
 </​note>​ </​note>​
- 
 ===== Întrebări ===== ===== Întrebări =====
- 
 Pentru întrebări legate de temă puteți consulta [[http://​cursuri.cs.pub.ro/​pipermail/​so2/​ | arhivele]] listei de discuții sau puteți trimite un [[so2@cursuri.cs.pub.ro |e-mail]] (trebuie să fiți [[http://​cursuri.cs.pub.ro/​cgi-bin/​mailman/​listinfo/​so2 | înregistrați]]). Pentru întrebări legate de temă puteți consulta [[http://​cursuri.cs.pub.ro/​pipermail/​so2/​ | arhivele]] listei de discuții sau puteți trimite un [[so2@cursuri.cs.pub.ro |e-mail]] (trebuie să fiți [[http://​cursuri.cs.pub.ro/​cgi-bin/​mailman/​listinfo/​so2 | înregistrați]]).
- 
 Vă rugăm să urmăriți și să respectați [[http://​ocw.cs.pub.ro/​courses/​so2/​resurse/​lista-discutii#​mailing-list-guidelines | indicațiile de utilizare a listei]]. Vă rugăm să urmăriți și să respectați [[http://​ocw.cs.pub.ro/​courses/​so2/​resurse/​lista-discutii#​mailing-list-guidelines | indicațiile de utilizare a listei]].
- 
so2/teme/tema4.1515178325.txt.gz · Last modified: 2018/01/05 20:52 by adrian.stanciu
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