This shows you the differences between two versions of the page.
so2:laboratoare:lab10:exercitii [2017/04/23 12:49] razvan.deaconescu [5. [3p] Socket UDP sender] |
so2:laboratoare:lab10:exercitii [2019/04/23 23:03] (current) constantin.ghioc [4. [2p] Acceptarea unei conexiuni în kernel space] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Laborator 10: Exerciții ====== | ====== Laborator 10: Exerciții ====== | ||
- | Pentru desfășurarea laboratorului pornim de la [[http://elf.cs.pub.ro/so2/res/laboratoare/lab10-tasks.zip|arhiva de sarcini a laboratorului]]. Descărcăm și decomprimăm arhiva în directorul ''so2/'' din directorul home al utilizatorului ''student'' de pe sistemul de bază (stația ''mjolnir''):<code bash> | + | ===== Pregătirea laboratorului ===== |
- | student@mjolnir:~$ cd so2/ | + | |
- | student@mjolnir:~/so2$ wget http://elf.cs.pub.ro/so2/res/laboratoare/lab10-tasks.zip | + | Pentru rezolvarea laboratorului, vom lucra în același director din care pornim mașina virtuală (''~/so2/linux/tools/labs''). |
- | student@mjolnir:~/so2$ unzip lab10-tasks.zip | + | |
- | student@mjolnir:~/so2$ tree lab10-tasks | + | Pașii de rezolvare sunt următorii: |
+ | * pregătirea scheletului de laborator | ||
+ | * compilarea modulelor de Kernel | ||
+ | * copierea modulelor pe mașina virtuală | ||
+ | * pornirea mașinii virtuale și testarea modulelor | ||
+ | |||
+ | ==== Pregătirea scheletului de laborator ==== | ||
+ | |||
+ | Pentru rezolvarea laboratorului trebuie sa activam suportul de netfilter din kernel. In meniul deschis cu ''make menuconfig'' | ||
+ | activati optiunea ''Networking support/Networking options/Network packet filtering framework (Netfilter)''. | ||
+ | |||
+ | Scheletul de laborator este generat din sursele din directorul ''tools/labs/templates''. Putem genera scheletele pentru toate laboratoarele folosind următoarea comanda: | ||
+ | |||
+ | <code bash> | ||
+ | tools/labs $ make skels | ||
</code> | </code> | ||
- | În cadrul directorului ''lab10-tasks/'' se găsesc resursele necesare pentru dezvoltarea exercițiilor de mai jos: fișiere schelet de cod sursă, fișiere Makefile și Kbuild, scripturi și programe de test. | ||
- | Vom dezvolta exercițiile pe sistemul de bază (stația ''mjolnir'') și apoi le vom testa pe [[:so2:resurse:masini-virtuale|mașina virtuală QEMU]]. După editarea și compilarea unui modul de kernel îl vom copia în directorul dedicat pentru mașina virtuală QEMU folosind o comandă de forma<code bash> | + | Pentru a genera scheletul pentru un singur laborator, vom folosi variabila de mediu ''LABS'': |
- | student@mjolnir:~/so2$ cp /path/to/module.ko ~/so2/qemu-vm/fsimg/root/modules/ | + | |
- | </code> unde ''/path/to/module.ko'' este calea către fișierul obiect aferent modulului de kernel. Apoi vom porni, din directorul ''~/so2/qemu-vm/'' mașina virtuală QEMU folosind comanda<code bash> | + | <code bash> |
- | student@mjolnir:~/so2/qemu-vm$ make | + | tools/labs $ make clean |
+ | tools/labs $ LABS=<lab name> make skels | ||
</code> | </code> | ||
- | După pornirea mașinii virtuale QEMU vom putea folosi comenzi în fereastra QEMU pentru a încărca și descărca modulul de kernel:<code> | + | <note important> |
- | # insmod modules/module-name.ko | + | Numele laboratorului curent este ''networking''. |
- | # rmmod module/module-name | + | </note> |
- | </code> unde ''module-name'' este numele modulului de kernel. | + | |
+ | Similar, putem genera și scheletul pentru un singur exercițiu, atribuind valoarea ''<lab_name>/<task_name>'' variabilei ''LABS''. | ||
<note> | <note> | ||
- | Pentru dezvoltarea laboratorului, este recomandat să folosim trei terminale sau, mai bine, trei tab-uri de terminal. Pentru a deschide un nou tab de terminal folosim combinația de taste ''Ctrl+Alt+t''. Cele trei tab-uri de terminal îndeplinesc următoarele roluri: | + | Scheletul este generat în directorul ''tools/labs/skels''. |
- | - În primul tab de terminal dezvoltăm modulul de kernel: editare, compilare, copiere în directorul dedicat pentru mașina virtaulă QEMU. Lucrăm în directorul aferent rezultat în urma decomprimării arhivei de sarcini a laboratorului. | + | </note> |
- | - În al doilea tab de terminal pornim mașina virtuală QEMU și apoi testăm modulul de kernel: încărcare/descărcare modul, rulare teste. Lucrăm în directorul aferent mașinii virtuale: ''~/so2/qemu-vm/''. | + | |
- | - În al treilea tab de terminal pornim [[:so2:laboratoare:lab02#minicom|minicom]] sau un server UDP care să primească [[:so2:laboratoare:lab02#netconsole|mesajele de netconsole]]. Nu contează în ce director ne aflăm. Folosim comanda<code bash> | + | ==== Compilarea modulelor ==== |
- | student@mjolnir:~$ netcat -lup 6666 | + | |
+ | Comanda ''make build'' compilează toate modulele din directorul ''skels''. | ||
+ | |||
+ | <code bash> | ||
+ | student@eg106:~/so2/linux/tools/labs$ make build | ||
+ | echo "# autogenerated, do not edit " > skels/Kbuild | ||
+ | echo "ccflags-y += -Wno-unused-function -Wno-unused-label -Wno-unused-variable " >> skels/Kbuild | ||
+ | for i in ./networking/1-2-netfilter/kernel ./networking/3-4-tcp-sock ./networking/5-udp-sock; do echo "obj-m += $i/" >> skels/Kbuild; done | ||
+ | ... | ||
</code> | </code> | ||
+ | |||
+ | ==== Copierea modulelor pe mașina virtuală ==== | ||
+ | |||
+ | Putem copia modulele generate pe mașina virtuală folosind target-ul ''copy'' al comenzii make, atunci când mașina virtuală este oprită. | ||
+ | |||
+ | <code bash> | ||
+ | student@eg106:~/so2/linux/tools/labs$ make copy | ||
+ | student@eg106:~/so2/linux/tools/labs$ make boot | ||
+ | </code> | ||
+ | |||
+ | Alternativ, putem copia fișierele prin ''scp'', pentru e evita repornirea mașinii virtuale. Pentru detalii despre folosirea interacțiunea prin rețea cu mașina virtuală citiți [[https://ocw.cs.pub.ro/courses/so2/resurse/masini-virtuale#interactiunea_cu_masina_virtuala|Interacțiunea cu mașina virtuală]]. | ||
+ | |||
+ | ==== Testarea modulelor ==== | ||
+ | |||
+ | Modulele generate sunt copiate pe mașina virtuală în directorul ''/home/root/skels/<lab_name>''. | ||
+ | |||
+ | <code bash> | ||
+ | root@qemux86:~/skels/networking# ls | ||
+ | 1-2-netfilter 3-4-tcp-sock 5-udp-sock | ||
+ | root@qemux86:~/skels/networking# ls 1-2-netfilter/kernel/ | ||
+ | filter.ko | ||
+ | </code> | ||
+ | |||
+ | După pornirea mașinii virtuale QEMU vom putea folosi comenzi în fereastra QEMU (sau în ''minicom'') pentru a încărca și descărca modulul de kernel:<code> | ||
+ | root@qemux86:~# insmod skels/<lab_name>/<task_name>/<module_name>.ko | ||
+ | root@qemux86:~# rmmod skels/<lab_name>/<task_name>/<module_name>.ko | ||
+ | </code> | ||
+ | |||
+ | <note> | ||
+ | Pentru dezvoltarea laboratorului, este recomandat să folosim trei terminale sau, mai bine, trei tab-uri de terminal. Pentru a deschide un nou tab de terminal folosim combinația de taste ''Ctrl+Shift+t''. Cele trei tab-uri de terminal îndeplinesc următoarele roluri: | ||
+ | - În primul tab de terminal dezvoltăm modulul de kernel: editare, compilare, copiere în directorul dedicat pentru mașina virtuală QEMU. Lucrăm în directorul aferent rezultat în urma decomprimării arhivei de sarcini a laboratorului. | ||
+ | - În al doilea tab de terminal pornim mașina virtuală QEMU și apoi testăm modulul de kernel: încărcare/descărcare modul, rulare teste. Lucrăm în directorul aferent mașinii virtuale: ''~/so2/linux/tools/labs''. | ||
+ | - În al treilea tab de terminal accesăm directorul ''~/so2/linux/'' cu sursele nucleului unde putem folosi [[:so2:laboratoare:lab01#cscope|Vim și cscope]] pentru parcurgerea codului sursă.<code> | ||
+ | student@eg106-pc:~$ netcat -lup 6666 | ||
+ | </code> | ||
+ | |||
+ | </note> | ||
+ | |||
+ | ===== Exerciții ===== | ||
+ | |||
+ | <note important> | ||
+ | Înainte de începerea rezolvării laboratorului, rulați comanda ''%%git pull --rebase%%'' in directorul ''~/so2/linux'', pentru a obține ultima versiune a scheletului de laborator. | ||
+ | |||
+ | De asemenea, trebuie să vă asigurați că este activat suportul pentru ''netfilter'' în kernel, prin opțiunea ''CONFIG_NETFILTER''. Pentru aceasta, rulați ''make menuconfig'' în directorul ''linux'' și verificați opțiunea ''Network packet filtering framework (Netfilter)'' din ''Networking support -> Networking options''. | ||
+ | În cazul în care nu era activată, activați-o (ca builtin, nu modul extern -- trebuie să fie marcată cu ''*''). | ||
+ | |||
+ | În cazul în care ați activat-o, trebuie să forțati recompilarea kernel-ului înainte de pornirea mașinii virtuale. Pentru aceasta, ștergeți fisierul ''linux/arch/x86/boot/bzImage''. După acest pas, la pornirea mașinii virtuale se va declanșa automat recompilarea. | ||
</note> | </note> | ||
Line 36: | Line 106: | ||
Va trebui să înregistrați un hook netfilter de tipul ''NF_INET_LOCAL_OUT'' așa cum este explicat în secțiunea [[:so2:laboratoare:lab10#netfilter|netfilter]]. | Va trebui să înregistrați un hook netfilter de tipul ''NF_INET_LOCAL_OUT'' așa cum este explicat în secțiunea [[:so2:laboratoare:lab10#netfilter|netfilter]]. | ||
- | [[:so2:laboratoare:lab10#structura_sk_buff|Structura sk_buff]] dă acces la antetele unui pachet. Antetul IP se obține într-o structură [[http://lxr.free-electrons.com/source/include/uapi/linux/ip.h?v=4.9#L85|iphdr]] folosind funcția [[http://lxr.free-electrons.com/source/include/linux/ip.h?v=4.9#L23|ip_hdr]], iar antetul TCP se obține într-o structură [[http://lxr.free-electrons.com/source/include/uapi/linux/tcp.h?v=4.9#L24|tcphdr]] folosind funcția [[http://lxr.free-electrons.com/source/include/linux/tcp.h?v=4.9#L28|tcp_hdr]]. | + | [[:so2:laboratoare:lab10#structura_sk_buff|Structura sk_buff]] dă acces la antetele unui pachet. Antetul IP se obține într-o structură [[http://lxr.free-electrons.com/source/include/uapi/linux/ip.h?v=4.9#L85|struct iphdr]] folosind funcția [[http://lxr.free-electrons.com/source/include/linux/ip.h?v=4.9#L23|ip_hdr]], iar antetul TCP se obține într-o structură [[http://lxr.free-electrons.com/source/include/uapi/linux/tcp.h?v=4.9#L24|struct tcphdr]] folosind funcția [[http://lxr.free-electrons.com/source/include/linux/tcp.h?v=4.9#L28|tcp_hdr]]. |
- | [[http://www.eventhelix.com/Realtimemantra/Networking/Tcp.pdf|Diagrama]] explică cum se realizează o conexiune TCP. Pachetul de inițiere a unei conexiuni are flagul SYN din atentul TCP activ, iar flagul ACK inactiv. | + | [[http://www.eventhelix.com/Realtimemantra/Networking/Tcp.pdf|Diagrama]] explică cum se realizează o conexiune TCP. Pachetul de inițiere a unei conexiuni are flagul ''SYN'' din atentul TCP activ, iar flagul ''ACK'' inactiv. |
<note tip> | <note tip> | ||
Line 50: | Line 120: | ||
Portul TCP sursă este, în antentul TCP, în formatul [[http://en.wikipedia.org/wiki/Byte_order#Endianness_in_networking|network byte-order]]. Parcurgeți secțiunea [[:so2:laboratoare:lab10#conversii|Conversii]]. Folosiți [[http://lxr.free-electrons.com/source/include/linux/byteorder/generic.h?v=4.9#L141|ntohs]] pentru conversie. | Portul TCP sursă este, în antentul TCP, în formatul [[http://en.wikipedia.org/wiki/Byte_order#Endianness_in_networking|network byte-order]]. Parcurgeți secțiunea [[:so2:laboratoare:lab10#conversii|Conversii]]. Folosiți [[http://lxr.free-electrons.com/source/include/linux/byteorder/generic.h?v=4.9#L141|ntohs]] pentru conversie. | ||
- | Pentru testare folosiți fișierul ''1-2-netfilter/test-1.sh''. Testul generează o conexiune către stația locală (pe ''localhost''), conexiune ce va fi interceptată și afișată de modulul de kernel. Îl copiați în cadrul mașinii virtuale QEMU și apoi îl rulați obținând un output similar celui de mai jos:<code> | + | Pentru testare folosiți fișierul ''1-2-netfilter/user/test-1.sh''. Testul generează o conexiune către stația locală (pe ''localhost''), conexiune ce va fi interceptată și afișată de modulul de kernel. Scriptul este copiat pe mașina virtuală la ''make copy'' doar dacă este marcat ca executabil. Scriptul folosește utilitarul compilat static ''netcat'' din ''skels/networking/netcat''; acest executabil trebuie să aibă permisiuni de execuție. |
+ | |||
+ | După rulare ar trebui să obținem un output similar celui de mai jos:<code> | ||
# ./test-1.sh | # ./test-1.sh | ||
[ 229.783512] TCP connection initiated from 127.0.0.1:44716 | [ 229.783512] TCP connection initiated from 127.0.0.1:44716 | ||
Line 73: | Line 145: | ||
* adresa destinație a pchetului (folosind funcția ''test_daddr'') | * adresa destinație a pchetului (folosind funcția ''test_daddr'') | ||
- | Pentru testare folosiți scriptul ''1-2-netfilter/test-2.sh''. Acest script are nevoie de compilarea fișierului ''1-2-netfilter/test.c'' în executabilul ''test''. Compilarea o faceți pe sistemul fizic folosind comanda<code> | + | Pentru testare folosiți scriptul ''1-2-netfilter/user/test-2.sh''. |
- | make -f Makefile.test | + | Acest script are nevoie de compilarea fișierului ''1-2-netfilter/user/test.c'' în executabilul ''test''. Compilarea se face automat pe sistemul fizic la rularea comenzii ''make build''. Scriptul de testare este copiat pe mașina virtuală doar dacă este marcat ca executabil. Scriptul folosește utilitarul compilat static ''netcat'' din ''skels/networking/netcat''; acest executabil trebuie să aibă permisiuni de execuție. |
- | </code> | + | |
- | Copiați modulul de kernel, împreună cu scriptul ''test-2.sh'' și executabilul ''test'' pe mașina virtuală QEMU pentru testare. În urma rulării testului veți obține un output similar celui de mai jos:<code> | + | În urma rulării testului veți obține un output similar celui de mai jos:<code> |
# ./test-2.sh | # ./test-2.sh | ||
[ 797.673535] TCP connection initiated from 127.0.0.1:44721 | [ 797.673535] TCP connection initiated from 127.0.0.1:44721 | ||
Line 86: | Line 157: | ||
Testul comandă filtrarea pachetelor întâi către adresa IP ''127.0.0.1'' și apoi către adresa IP ''127.0.0.2''. Primul pachet de inițiere de conexiune (către ''127.0.0.1'') este interceptat și afișat de filtru, în vreme ce al doilea (către ''127.0.0.2'') nu este interceptat. | Testul comandă filtrarea pachetelor întâi către adresa IP ''127.0.0.1'' și apoi către adresa IP ''127.0.0.2''. Primul pachet de inițiere de conexiune (către ''127.0.0.1'') este interceptat și afișat de filtru, în vreme ce al doilea (către ''127.0.0.2'') nu este interceptat. | ||
- | |||
==== 3. [2p] Socket TCP în starea listening ==== | ==== 3. [2p] Socket TCP în starea listening ==== | ||
- | Creați un modul de kernel care creează un socket TCP ce ascultă conexiuni pe portul ''60000'' pe interfața de loopback (în ''init_module''). Folosiți scheletul din ''3-4-tcp-sock/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab10-tasks.zip|arhiva de sarcini]] a laboratorului și completați zonele marcate cu ''TODO 1'' ținând cont de observațiile de mai jos. | + | Creați un modul de kernel care creează un socket TCP ce ascultă conexiuni pe portul ''60000'' pe interfața de loopback (în ''init_module''). Folosiți scheletul din ''3-4-tcp-sock/'' din scheletul laboratorului și completați zonele marcate cu ''TODO 1'' ținând cont de observațiile de mai jos. |
Parcurgeți secțiunile [[:so2:laboratoare:lab10#operatii_asupra_structurii_socket|Operații asupra structurii socket]] și [[:so2:laboratoare:lab10#structura_proto_ops|Structura proto_ops]] din laborator. | Parcurgeți secțiunile [[:so2:laboratoare:lab10#operatii_asupra_structurii_socket|Operații asupra structurii socket]] și [[:so2:laboratoare:lab10#structura_proto_ops|Structura proto_ops]] din laborator. | ||
- | Pentru a crea un socket folosiți funcția [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L1363|sock_create_kern]]. | + | Pentru a crea un socket folosiți funcția [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L1212|sock_create_kern]]. |
Socketul ''sock'' este un //server socket// și trebuie pus în starea listening. Adică pe socket trebuie aplicate operațiile de tip ''bind'' și ''listen''. Pentru echivalentul ''bind'', ''listen'' în kernel space va trebui să apelați funcții de tipul ''%%sock->ops->...%%''; exemple de astfel de funcții pe care să le apelați sunt ''%%sock->ops->bind%%'', ''%%sock->ops-listen%%'' etc. | Socketul ''sock'' este un //server socket// și trebuie pus în starea listening. Adică pe socket trebuie aplicate operațiile de tip ''bind'' și ''listen''. Pentru echivalentul ''bind'', ''listen'' în kernel space va trebui să apelați funcții de tipul ''%%sock->ops->...%%''; exemple de astfel de funcții pe care să le apelați sunt ''%%sock->ops->bind%%'', ''%%sock->ops-listen%%'' etc. | ||
Line 108: | Line 178: | ||
Să aveți în vedere eliberarea socket-ul în funcția de tip exit a modulului și în zona cu label-uri de eroare; folosiți funcția [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L571|sock_release]]. | Să aveți în vedere eliberarea socket-ul în funcția de tip exit a modulului și în zona cu label-uri de eroare; folosiți funcția [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L571|sock_release]]. | ||
- | Pentru testare, rulați scriptul ''3-4-tcp_sock/test-3.sh''. În urma rulării testului, se va afișa un socket TCP ascultând conexiuni pe portul ''60000''. | + | Pentru testare, rulați scriptul ''3-4-tcp_sock/test-3.sh''. Scriptul este copiat pe mașina virtuală la ''make copy'' doar dacă este marcat ca executabil. |
+ | |||
+ | În urma rulării testului, se va afișa un socket TCP ascultând conexiuni pe portul ''60000''. | ||
==== 4. [2p] Acceptarea unei conexiuni în kernel space ==== | ==== 4. [2p] Acceptarea unei conexiuni în kernel space ==== | ||
Line 114: | Line 186: | ||
Extindeți modulul de la exercițiul anterior pentru a permite acceptarea unei conexiuni din exterior (nu trebuie transmis mesaj, doar acceptată o nouă conexiune). Trebuie completate zonele marcate cu ''TODO 2''. | Extindeți modulul de la exercițiul anterior pentru a permite acceptarea unei conexiuni din exterior (nu trebuie transmis mesaj, doar acceptată o nouă conexiune). Trebuie completate zonele marcate cu ''TODO 2''. | ||
- | Parcurgeți secțiunile [[:so2:laboratoare:lab10#operatii_asupra_structurii_socket|Operații asupra structurii socket]] și [[:so2:laboratoare:lab10#structura_proto_ops|Structura proto_ops]] din laborator. Pentru echivalentul ''accept'' în kernel-space, consultați handler-ul de apel de sistem [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L1418|sys_accept4]]. Urmăriți în implementarea [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L1418|sys_accept4]] apelul ''%%sock->ops->accept%%''. Folosiți ''0'' ca valoarea pentru ultimul argument (''flags''). | + | Parcurgeți secțiunile [[:so2:laboratoare:lab10#operatii_asupra_structurii_socket|Operații asupra structurii socket]] și [[:so2:laboratoare:lab10#structura_proto_ops|Structura proto_ops]] din laborator. Pentru echivalentul ''accept'' în kernel-space, consultați handler-ul de apel de sistem [[http://lxr.free-electrons.com/source/net/socket.c?v=4.15#L1553|sys_accept4]]. Urmăriți în implementarea [[https://elixir.bootlin.com/linux/v4.15/source/drivers/staging/lustre/lnet/lnet/lib-socket.c#L513|lnet_sock_accept]] cum este folosit apelul ''%%sock->ops->accept%%''. Folosiți ''0'' ca valoarea pentru penultimul argument (''flags'') și ''false'' pentru ultimul argument (''kern''). |
+ | |||
+ | <note tip> | ||
+ | Socket-ul nou creat (''new_sock'') trebuie creat cu apelul [[https://elixir.bootlin.com/linux/v4.15/source/net/socket.c#L1069|sock_create_lite]] și apoi îi trebuie configurate operațiile folosind<code> | ||
+ | newsock->ops = sock->ops; | ||
+ | </code> | ||
+ | </note> | ||
Afișați adresa și portul socket-ului destinație. Pentru a afla numele peer-ului unui socket (adresa sa), consultați handler-ul de apel de sistem [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L1587|sys_getpeername]]. | Afișați adresa și portul socket-ului destinație. Pentru a afla numele peer-ului unui socket (adresa sa), consultați handler-ul de apel de sistem [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L1587|sys_getpeername]]. | ||
<note tip> | <note tip> | ||
- | Primul argument al funcției de tip ''getname'' va fi socket-ul de tip conexiune, adică ''new_sock'', cel inițializat cu ajutorul apelului ''accept''. | + | Primul argument al funcției ''%%sock->ops->getname%%'' va fi socket-ul de tip conexiune, adică ''new_sock'', cel inițializat cu ajutorul apelului ''accept''. |
- | Ultimul argument al funcției de tip ''getname'' va fi ''1'', însemnând că dorim aflarea de infomații despre capătul destinație (//remote end// sau //peer//). | + | Ultimul argument al funcției ''%%sock->ops->getname%%'' va fi ''1'', însemnând că dorim aflarea de infomații despre capătul destinație (//remote end// sau //peer//). |
- | Afișați adresa peer-ului (indicată de variabila ''raddr'') folosind macro-ul ''print_sock_address''. | + | Afișați adresa peer-ului (indicată de variabila ''raddr'') folosind macro-ul ''print_sock_address()'' definit mai sus în fișier. |
</note> | </note> | ||
Eliberați socket-ul nou creat (în urma acceptării conexiunii) în funcția de exit a modulului și în zona cu label-uri de eroare. După adăugarea codului care face ''accept'' în funcția de inițializare a modulului, operația de ''insmod'' se va bloca până se va realiza o conexiune. Puteți debloca folosind netcat pe portul respectiv. În consecință, scriptul de testare de la exercițiul precedent nu va ma funcționa. | Eliberați socket-ul nou creat (în urma acceptării conexiunii) în funcția de exit a modulului și în zona cu label-uri de eroare. După adăugarea codului care face ''accept'' în funcția de inițializare a modulului, operația de ''insmod'' se va bloca până se va realiza o conexiune. Puteți debloca folosind netcat pe portul respectiv. În consecință, scriptul de testare de la exercițiul precedent nu va ma funcționa. | ||
- | Pentru testarea, adică inițierea unei conexiuni din exterior, folosiți scriptul ''3-4-tcp_sock/test-4.sh''. Nu se va afișa ceva special (în buffer-ul kernel-ului). Reușita testului va fi dată de realizarea conexiunii. Apoi folosiți ''Ctrl+c'' pentru a încheia rularea scriptului de testare și apoi puteți elimina modulul din kernel. | + | Pentru testarea, adică inițierea unei conexiuni din exterior, folosiți scriptul ''3-4-tcp_sock/test-4.sh''. Scriptul este copiat pe mașina virtuală la ''make copy'' doar dacă este marcat ca executabil. |
+ | |||
+ | Nu se va afișa ceva special (în buffer-ul kernel-ului). Reușita testului va fi dată de realizarea conexiunii. Apoi folosiți ''Ctrl+c'' pentru a încheia rularea scriptului de testare și apoi puteți elimina modulul din kernel. | ||
==== 5. [3p] Socket UDP sender ==== | ==== 5. [3p] Socket UDP sender ==== | ||
- | Creați un modul de kernel care creează un socket UDP și transmite mesajul din macro-ul %%MY_TEST_MESSAGE%% pe socket către adresa loopback pe portul ''60001''. | + | Creați un modul de kernel care creează un socket UDP și transmite mesajul din macro-ul ''MY_TEST_MESSAGE'' pe socket către adresa loopback pe portul ''60001''. |
- | Porniți de la scheletul ''5-udp-sock/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab10-tasks.zip|arhiva de sarcini]] a laboratorului. | + | Porniți de la scheletul ''5-udp-sock/'' din scheletul laboratorului. |
Citiți secțiunile [[:so2:laboratoare:lab10#operatii_asupra_structurii_socket|Operații asupra structurii socket]] și [[:so2:laboratoare:lab10#structura_proto_ops|Structura proto_ops]] din laborator. | Citiți secțiunile [[:so2:laboratoare:lab10#operatii_asupra_structurii_socket|Operații asupra structurii socket]] și [[:so2:laboratoare:lab10#structura_proto_ops|Structura proto_ops]] din laborator. | ||
- | Pentru a vedea cum se face transmiterea de mesaje în kernel-space, consultați handler-ul de apel de sistem [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L1805|sys_send]] sau secțiunea [[:so2:laboratoare:lab10#transmitereareceptia_unui_mesaj|Transmiterea/recepția unui mesaj]]. | + | Pentru a vedea cum se face transmiterea de mesaje în kernel-space, consultați handler-ul de apel de sistem [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L1664|sys_send]] sau secțiunea [[:so2:laboratoare:lab10#transmitereareceptia_unui_mesaj|Transmiterea/recepția unui mesaj]]. |
<note important> | <note important> | ||
- | Câmpul ''msg_name'' al structurii ''struct msghdr'' trebuie inițializat la adresa destinație (de tipul ''struct sockaddr *''), iar câmpul ''msg_namelen'' la dimensiunea adresei. | + | Câmpul ''msg_name'' al structurii [[http://lxr.free-electrons.com/source/include/linux/socket.h?v=4.9#L41|struct msghdr]] trebuie inițializat la adresa destinație (de tipul ''struct sockaddr *''), iar câmpul ''msg_namelen'' la dimensiunea adresei. |
- | Inițializați câmpului ''msg_flags'' a structurii ''struct msghdr'' la valoarea ''0''. | + | Inițializați câmpului ''msg_flags'' a structurii [[http://lxr.free-electrons.com/source/include/linux/socket.h?v=4.9#L41|struct msghdr]] la valoarea ''0''. |
- | Inițializați respectiv câmpurile ''control'' și ''controllen'' ale structurii ''struct msghdr'' la ''NULL'' și ''0''. | + | Inițializați respectiv câmpurile ''msg_control'' și ''msg_controllen'' ale structurii [[http://lxr.free-electrons.com/source/include/linux/socket.h?v=4.9#L41|struct msghdr]] la ''NULL'' și ''0''. |
</note> | </note> | ||
- | Pentru transmiterea efectiva folosiți [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L635|kernel_sendmsg]]. | + | Pentru transmiterea efectivă folosiți [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L635|kernel_sendmsg]]. |
- | Parametrii de transmitere a mesajului sunt preluați din kernel-space. Faceți, pentru structura de tip ''struct iovec'' cast la ''struct kvec *'' în apelul ''kernel_sendmsg''. | + | Parametrii de transmitere a mesajului sunt preluați din kernel-space. Faceți, pentru pointer-ul la structura de tip [[http://lxr.free-electrons.com/source/include/uapi/linux/uio.h?v=4.9#L16|struct iovec]] cast la un pointer la structura [[http://lxr.free-electrons.com/source/include/linux/uio.h?v=4.9#L18|struct kvec]] în apelul [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L635|kernel_sendmsg]]. |
<note important> | <note important> | ||
- | Ultimii doi parametri ai funcției ''kernel_sendmsg'' sunt ''1'' (numărul de vectori I/O) și ''len'' (dimensiunea mesajului). | + | Ultimii doi parametri ai funcției [[http://lxr.free-electrons.com/source/net/socket.c?v=4.9#L635|kernel_sendmsg]] sunt ''1'' (numărul de vectori I/O) și ''len'' (dimensiunea mesajului). |
</note> | </note> | ||
- | Pentru testare folosiți scriptul ''test-5.sh'' și integrați executabilul static ''nc-static'' așa cum este descris în continuare. | + | Pentru testare folosiți scriptul ''test-5.sh''. Scriptul este copiat pe mașina virtuală la ''make copy'' doar dacă este marcat ca executabil. Scriptul folosește utilitarul compilat static ''netcat'' din ''skels/networking/netcat''; acest executabil trebuie să aibă permisiuni de execuție. |
- | <note important> | + | <note tip> |
- | Utilitarul ''nc'' din busybox din mașina virtuală QEMU nu are suport pentru UDP. De aceea vom folosi un alt executabil. | + | La o implementare corectă, rularea script-ului ''test-5.sh'' va conduce la afișarea mesajului //kernelsocket//, într-un output precum cel de mai jos: |
- | + | <code> | |
- | Pentru deschiderea unui socket UDP pe portul 60001 folosiți utilitarul netcat compilat static de la adresa http://www.stearns.org/nc/nc-static. Descărcați binarul pe sistemul fizic în directorul ''fsimg/root/'' al mașini virtuale QEMU. Binarul poate fi descărcat cu ajutorul comenzii<code> | + | /root # ./test-5.sh |
- | wget http://www.stearns.org/nc/nc-static | + | + pid=1059 |
+ | + sleep 1 | ||
+ | + nc -l -u -p 60001 | ||
+ | + insmod udp_sock.ko | ||
+ | kernelsocket | ||
+ | + rmmod udp_sock | ||
+ | + kill 1059 | ||
</code> | </code> | ||
- | Verificați că fișierul a fost descărcat corect verificând suma de control MD5 cu ajutorul comenzii<code> | ||
- | $ md5sum nc-static | ||
- | ec6aa710d3112808cb31a1d6ded775a1 nc-static | ||
- | </code> | ||
- | |||
- | După pornirea mașinii virtuale QEMU, acordați executabilului ''nc-static'' permisiuni de execuție folosind<code> | ||
- | chmod a+x nc-static | ||
- | </code> și apoi copiați executabilul ''nc-static'' în ''/bin/nc'':<code> | ||
- | cp nc-static /bin/nc | ||
- | </code> | ||
- | </note> | ||
- | |||
- | <note> | ||
- | Mesajul ''Punt!'' afișat în urma rulării testului ''test-5.sh'' este un mesaj afișat de utilitarul ''netcat'' în momentul în care procesul este oprit de rularea comenzii ''kill'' în cadrul scriptului. | ||
</note> | </note> | ||
Line 184: | Line 256: | ||
* [[http://elf.cs.pub.ro/so2/res/laboratoare/lab10-sol.zip|Soluții exerciții laborator 10]] | * [[http://elf.cs.pub.ro/so2/res/laboratoare/lab10-sol.zip|Soluții exerciții laborator 10]] | ||
- |