This shows you the differences between two versions of the page.
so2:laboratoare:lab11 [2013/05/09 12:28] razvan.deaconescu [Linux] |
so2:laboratoare:lab11 [2017/05/07 19:19] (current) octavian.purdila update lxr links to use 4.9 |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laborator 11 - Networking ====== | + | ====== Laborator 11 - Maparea memoriei ====== |
===== Obiectivele laboratorului ===== | ===== Obiectivele laboratorului ===== | ||
- | * Înțelegerea arhitecturii subsistemului de networking la nivelul nucleelor Linux și Windows | + | *familiarizarea cu mecanismele de mapare a spațiilor de adresă |
- | * Deprinderea unor abilități practice de gestiune a pachetelor IP în cadrul unui filtru de pachete/firewall | + | *prezentarea structurilor cele mai importante din Linux, legate de memory management |
- | * Familiarizarea cu modul de utilizare al sockeților la nivelul nucleului Linux | + | |
===== Cuvinte cheie ===== | ===== Cuvinte cheie ===== | ||
- | *stiva TCP/IP | + | *spațiu de adresă |
- | *protocol | + | *mmap |
- | *adresă IP | + | *''struct page'' |
- | *port | + | *''struct vm_area_struct'' |
- | *socket, BSD socket, INET socket | + | *''struct vm_struct'' |
- | *PF_INET, AF_INET | + | *''remap_pfn_range'' |
- | *SOCK_STREAM, SOCK_DGRAM | + | *SetPageReserved/ClearPageReserved |
- | *IPPROTO_TCP, IPPROTO_UDP | + | |
- | *interfața loopback | + | |
- | *bind, connect, listen, accept | + | |
- | *send, sendto, recv, recvfrom | + | |
- | *struct socket, struct sock, struct sk_buff | + | |
- | *htons, htonl, ntohs, ntohl | + | |
- | *NPI, TDI, NDIS, WSK | + | |
- | *NDIS Miniport/Protocol/Intermediate Drivers | + | |
- | *Filter-Hook/Firewall-Hook Drivers | + | |
- | *RtlUshortByteSwap, RtlUlongByteSwap | + | |
===== Materiale ajutătoare ===== | ===== Materiale ajutătoare ===== | ||
Line 30: | Line 21: | ||
*[[http://elf.cs.pub.ro/so2/res/extra/so2_reference.pdf|SO2 Reference Card]] | *[[http://elf.cs.pub.ro/so2/res/extra/so2_reference.pdf|SO2 Reference Card]] | ||
- | ===== Noțiuni teoretice ===== | + | ===== Maparea memoriei în Linux ===== |
- | Dezvoltarea Internet-ului a dus la creșterea exponențială a aplicațiilor de rețea și, drept consecință, la creșterea cerințelor de viteza și productivitate a subsistemului de rețea (networking) al unui sistem de operare. Subsistemul de networking nu este o componentă esențială a nucleului unui sistem de operare (kernel-ul de Linux poate fi compilat fără suport de rețea). Este, însă, destul de puțin probabil pentru un sistem de calcul (sau chiar un dispozitiv embedded) să conțină un sistem de operare fără suport de rețea, datorită nevoii de conectivitate. Atât sistemele Linux (Unix), cât și Windows folosesc [[http://en.wikipedia.org/wiki/TCP/IP_model|stiva TCP/IP]]. Nucleul acestora va conține implementate protocoalele pâna la nivelul transport inclusiv, urmând ca protocoalele de nivel aplicație să fie implementate în user-space (HTTP, FTP, SSH, etc.). | + | În kernel-ul Linux există posibilitatea mapării unui spaţiu de adresă kernel într-un spaţiu de adresă utilizator. În acest fel se elimină overhead-ul datorat copierii informaţiei din user-space în kernel-space (şi invers). Acest lucru poate fi realizat prin intermediul unui device driver şi a interfeţei dispozitiv (''/dev'') a acestuia în user-space. |
- | ==== Networking în user-space ==== | + | Această facilitate poate fi folosită prin completarea operaţiei [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/fs.h#L1708|mmap]] din structura [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/fs.h#L1696|struct file_operations]] asociată dispozitivului şi utilizând apelul ''mmap'' din user-space. |
- | În user-space abstracția comunicației în rețea este socket-ul. Socket-ul abstractizează un canal de comunicație și este interfața de interacțiune cu stiva TCP/IP implementată în kernel. Unui socket IP i se asociază o adresă IP, protocolul de nivel transport utilizat (TCP, UDP etc) și un port. Apelurile uzuale pe un socket sunt : apelul de creare (''socket''), de inițializare (''bind''), de conectare (''connect''), de așteptare de conexiuni (''listen'', ''accept'') și de închidere (''close''). | + | ==== Structuri de lucru cu memoria ==== |
- | Comunicația în rețea se realizează prin intermediul apelurilor ''read/write'' sau ''recv/send'' pentru socket-i TCP, respectiv ''recvfrom/sendto'' pentru socket-i UDP. Operațiile de transmitere și recepție sunt transparente aplicației, lăsând la latitudinea nucleului încapsularea și transmiterea acestora în rețea. Este, însă, posibilă implementarea stivei TCP/IP în user-space folosind socket-i raw (opțiunea ''PF_PACKET'' la crearea unui socket), sau implementarea unui protocol de nivel aplicație în kernel ([[http://en.wikipedia.org/wiki/TUX_web_server|TUX web server]]). | + | Înainte de a discuta despre mecanismul de memory-mapping peste un dispozitiv, vom prezenta câteva din structurile de bază legate de subsistemul de management al memoriei în kernel-ul Linux. |
- | Pentru mai multe detalii despre programarea în user-space folosind socket-i, consultați [[http://www.beej.us/guide/bgnet/output/html/multipage/|Beej's Guide to Network Programming Using Internet Sockets]] pentru Linux și [[http://www.sockets.com/winsock.htm|Windows Sockets]] pentru Windows. | + | Cateva din structurile importante sunt: [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L45|struct page]], [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L300|struct vm_area_struct]] şi [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L396|struct mm_struct]]. |
- | ===== Linux networking ===== | + | ==== Structura ''page'' ==== |
- | Kernel-ul Linux oferă trei structuri fundamentale pentru lucrul cu pachetele de rețea: [[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L136|struct socket]], [[http://lxr.linux.no/#linux+v2.6.35/include/net/sock.h#L155|struct sock]] și [[http://lxr.linux.no/#linux+v2.6.35/include/linux/skbuff.h#L257|struct sk_buff]]. | + | Structura [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L45|struct page]] este utilizată pentru a încorpora informaţii despre toate paginile fizice din sistem. Kernel-ul deţine o structură ''struct page'' pentru toate paginile din sistem. |
- | Primele două reprezintă abstracții ale unui socket: | + | Există numeroase funcţii care interacţionează cu această structură: |
- | *[[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L136|struct socket]] este o abstracție foarte aproape de user-space, adică de [[http://en.wikipedia.org/wiki/Berkeley_sockets|socket-ii BSD]] folosiți pentru programarea aplicațiilor de rețea. | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/arch/x86/include/asm/page.h#L68|virt_to_page]] întoarce pagina asociată unei adrese virtuale; |
- | *[[http://lxr.linux.no/#linux+v2.6.35/include/net/sock.h#L155|struct sock]] sau //INET socket// în terminologia Linux este reprezentarea la nivel rețea a unui socket. | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/asm-generic/memory_model.h#L81|pfn_to_page]] întoarce pagina asociată pentru un număr de pagină (''page frame number''); |
- | Cele doua structuri sunt corelate: ''struct socket'' conține un câmp de INET socket, iar ''struct sock'' are un BSD socket care îl deține. | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm.h#L1013|page_address]] întoarce adresa virtuală a paginii transmise ca parametru. |
- | Structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/skbuff.h#L257|struct sk_buff]] este reprezentarea unui pachet de rețea și a stării acestuia. O astfel de structură este creată la sosirea unui pachet în kernel, fie din user-space, fie de la placa de rețea. | + | ==== Structura ''vm_area_struct'' ==== |
- | ==== Structura socket ==== | + | |
- | Structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L136|struct socket]] este reprezentarea în kernel a unui socket BSD, operațiile care pot fi executate asupra acestuia fiind similare cu cele expuse de kernel aplicațiilor (prin apeluri de sistem). Operațiile comune de lucru cu socket-ii (creare, inițializare/bind, închidere, etc.), rezultă în apeluri de sistem specifice; acestea operează asupra unei structuri de tipul ''struct socket''. | + | Structura [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L300|struct vm_area_struct]] deţine informaţii despre o zonă de memorie virtuală contiguă. Zonele de memorie ale unui proces pot fi vizualizate inspectând ''procfs'': |
- | Operațiile asupra [[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L136|struct socket]] sunt descrise în [[http://lxr.linux.no/linux+v2.6.35/net/socket.c|net/socket.c]] și sunt independente de tipul de protocoale de mai jos. Structura ''struct socket'' este, astfel, o interfață generică peste implementări particulare de operații de rețea. De obicei, numele acestor operații încep cu șirul ''sock_''. | + | <code bash> |
- | + | $ cat /proc/1/maps | |
- | === Operații asupra structurii socket === | + | 08048000-0804f000 r-xp 00000000 03:01 401624 /sbin/init |
- | + | 0804f000-08050000 rw-p 00007000 03:01 401624 /sbin/init | |
- | Operații asupra unui socket sunt: | + | 08050000-08071000 rw-p 08050000 00:00 0 |
- | + | 40000000-40016000 r-xp 00000000 03:01 369654 /lib/ld-2.3.2.so | |
- | == Crearea == | + | 40016000-40017000 rw-p 00015000 03:01 369654 /lib/ld-2.3.2.so |
- | + | 40017000-40018000 rw-p 40017000 00:00 0 | |
- | Crearea este asemănătoare apelului ''socket'' din user-space, dar socket-ul creat [[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L136|struct socket]], va fi întors în parametrul ''res'': | + | 4001d000-40147000 r-xp 00000000 03:01 371432 /lib/tls/libc-2.3.2.so |
- | *''int ''[[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L1264|sock_create]]''(int family, int type, int protocol, struct socket %%**%%res)'' | + | 40147000-40150000 rw-p 00129000 03:01 371432 /lib/tls/libc-2.3.2.so |
- | * funcție apelată pentru crearea unui socket în urma unui apel de sistem ''socket''; | + | 40150000-40153000 rw-p 40150000 00:00 0 |
- | *''int ''[[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L1269|sock_create_kern]]''(int family, int type, int protocol, struct socket %%**%%res)'' | + | bffff000-c0000000 rw-p bffff000 00:00 0 |
- | * funcție apelată pentru crearea unui socket în kernel; | + | ffffe000-fffff000 ---p 00000000 00:00 0 |
- | *''int ''[[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L1022|sock_create_lite]]''(int family, int type, int protocol, struct socket %%**%%res)'' | + | |
- | * funcție 'lite' cu eliminarea verificării parametrilor. | + | |
- | + | ||
- | Parametrii acestor apeluri sunt următorii: | + | |
- | *''family'' reprezintă familia protocoalelor utilizate în transferul informației; de obicei, acestea încep cu șirul ''PF_'' (//Protocol Family//); constantele care reprezintă familia de protocoale utilizate se găsesc în [[http://lxr.linux.no/linux+v2.6.35/include/linux/socket.h|linux/socket.h]], dintre care cea mai utilizată este ''PF_INET'', pentru protocoalele TCP/IP. | + | |
- | *''type'' reprezintă tipul de socket; constantele utilizate pentru acest parametru se găsesc în [[http://lxr.linux.no/linux+v2.6.35/include/linux/net.h|linux/net.h]], dintre care cele mai utilizate sunt ''SOCK_STREAM'' pentru o comunicație bazată pe conexiune între sursă și destinație și ''SOCK_DGRAM'' pentru o comunicație fără conexiune. | + | |
- | *''protocol'' reprezintă protocolul utilizat și este în strânsă legătură cu parametrul type; constatele utilizate pentru acest parametru se găsesc în [[http://lxr.linux.no/linux+v2.6.35/include/linux/in.h|linux/in.h]], dintre care cele mai folosite sunt ''IPPROTO_TCP'' pentru TCP și ''IPPROTO_UDP'' pentru UDP. | + | |
- | + | ||
- | Pentru crearea unui socket TCP în kernel se va apela: | + | |
- | <code c> | + | |
- | struct socket *sock; | + | |
- | int err; | + | |
- | err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); | + | |
- | if(err < 0) { | + | |
- | /* handle error */ | + | |
- | } | + | |
</code> | </code> | ||
- | iar pentru crearea unui socket UDP: | + | O zonă de memorie este caracterizată printr-o adresă de start, adresă de stop, lungime, permisiuni. |
- | <code c> | + | |
- | struct socket *sock; | + | |
- | int err; | + | |
- | err = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock); | + | |
- | if(err < 0) { | + | |
- | /* error */ | + | |
- | } | + | |
- | </code> | + | |
- | Un exemplu de utilizare poate fi urmărit în codul handler-ului pentru apelul de sistem [[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L1274|sys_socket]]: | + | O structură ''vm_area_struct'' este creată la fiecare apel ''mmap'' din user-space. Un driver care are suport pentru operaţia de ''mmap'' trebuie să completeze şi să iniţializeze structura ''vm_area_struct'' asociată. Cele mai importante câmpuri ale acestei structuri sunt: |
- | <code c> | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L303|vm_start]], [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L304|vm_end]], reprezintă începutul, respectiv, sfârşitul zonei de memorie (aceste câmpuri apar şi in ''/proc/*/maps''); |
- | SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L351|vm_file]], pointer-ul la structura de fişier asociată (dacă există); |
- | { | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L349|vm_pgoff]], offset-ul zonei în cadrul fişierului; |
- | int retval; | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L324|vm_flags]], un set de indicatori; |
- | struct socket *sock; | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L346|vm_ops]], un set de funcţii de lucru asupra acestei zone. |
- | int flags; | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L308|vm_next]], [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L308|vm_prev]], ''vm_area''-urile aferente unui proces sunt înlănțuite printr-o listă |
- | /* Check the SOCK_* constants for consistency. */ | + | ==== Structura ''mm_struct'' ==== |
- | BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC); | + | |
- | BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK); | + | |
- | BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK); | + | |
- | BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK); | + | |
- | flags = type & ~SOCK_TYPE_MASK; | + | Structura [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L396|struct mm_struct]] înglobează toate zonele de memorie asociate unui proces; folosind câmpul ''mm'' al structurii [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/sched.h#L1475|task_struct]] se poate obţine structura ''mm_struct'' asociată procesului curent. |
- | if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) | + | |
- | return -EINVAL; | + | |
- | type &= SOCK_TYPE_MASK; | + | |
- | if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) | + | ==== Maparea memoriei ==== |
- | flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; | + | |
- | retval = sock_create(family, type, protocol, &sock); | + | Maparea memoriei (''memory mapping'') este una dintre cele mai interesante caracteristici ale unui sistem Unix. Din punct de vedere al unui driver, facilitatea de ''memory-mapping'' permite accesul direct al memoriei unui dispozitiv din user-space. |
- | if (retval < 0) | + | |
- | goto out; | + | |
- | retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK)); | + | Pentru a asocia unui driver o operaţie ''mmap'', trebuie utilizat câmpul [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/fs.h#L1708|mmap]] din structura [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/fs.h#L1696|struct file_operations]] asociată dispozitivului. Metoda astfel asociată este utilizată în cazul unui apel ''mmap'' din user-space. |
- | if (retval < 0) | + | |
- | goto out_release; | + | |
- | out: | + | ==== User-space ==== |
- | /* It may be already another descriptor 8) Not kernel problem. */ | + | |
- | return retval; | + | |
- | out_release: | + | Apelul ''mmap'' din user-space realizează o mapare între spaţiul de adresă al unui proces şi un fişier şi are signatura: |
- | sock_release(sock); | + | |
- | return retval; | + | |
- | } | + | |
- | </code> | + | |
- | == Închiderea == | + | <code C> |
- | + | void *mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset); | |
- | Închiderea conexiunii (pentru socket cu conexiune) și eliberarea resurselor asociate: | + | |
- | *''void ''[[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L514|sock_release]]''(struct socket *sock)'' - această funcție va apela funcția ''release'' din câmpul ''ops'' al structurii socket-ului: | + | |
- | <code c> | + | |
- | void sock_release(struct socket *sock) | + | |
- | { | + | |
- | if (sock->ops) { | + | |
- | struct module *owner = sock->ops->owner; | + | |
- | + | ||
- | sock->ops->release(sock); | + | |
- | sock->ops = NULL; | + | |
- | module_put(owner); | + | |
- | } | + | |
- | //... | + | |
- | } | + | |
</code> | </code> | ||
- | == Transmiterea/recepția unui mesaj == | + | Pentru a mapa memorie între un dispozitiv şi user-space, se va deschide dispozitivul cu un apel ''open'' şi se va pasa apelului ''mmap'' descriptorul acestuia. |
- | Transmiterea/recepția mesajelor se face cu ajutorul funcțiilor: | + | ==== Kernel-space ==== |
- | *''int ''[[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L709|sock_recvmsg]]''(struct socket *sock, struct msghdr *msg, size_t size, int flags);'' | + | |
- | *''int ''[[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L739|kernel_recvmsg]]''(struct socket *sock, struct msghdr *msg, struct kvec *vec, size_t num, size_t size, int flags);'' | + | |
- | *''int ''[[http://lxr.linux.no/linux+v2.6.35/net/socket.c#L576|sock_sendmsg]]''(struct socket *sock, struct msghdr *msg, size_t size);'' | + | |
- | *''int ''[[http://lxr.linux.no/linux+v2.6.35/net/socket.c#L590|kernel_sendmsg]]''(struct socket *sock, struct msghdr *msg, struct kvec *vec, size_t num, size_t size);'' | + | |
- | Funcțiile de transmitere/recepție de mesaj vor apela ulterior funcția ''sendmsg/recvmsg'' din câmpul ''ops'' al socket-ului. Funcțiile ce conțin ''kernel_'' ca prefix sunt folosite în cazul în care socket-ul este utilizat în cadrul kernel-ului. | + | Operaţia ''mmap'' din kernel-space se declară ca un membru al structurii ''struct file_operations'' de forma: |
- | + | <code C> | |
- | Parametrii acestor funcții sunt următorii: | + | int (*mmap) (struct file *filp, struct vm_area_struct *vma) |
- | *''msg'', o structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/socket.h#L55|struct msghdr]], ce conține mesajul de transmis/recepționat. Dintre componentele importante ale acestei structuri avem: | + | |
- | *''msg_name'', ''msg_namelen'', care pentru socket-i ''UDP'' trebuie completați cu adresa la care se transmite mesajul ([[http://lxr.linux.no/#linux+v2.6.35/include/linux/in.h#L181|struct sockaddr_in]]) | + | |
- | *''msg_iov'', ''msg_iovlen'', datele de transmis, într-un vector de structuri [[http://lxr.linux.no/#linux+v2.6.35/include/linux/uio.h#L7|struct iovec]]. O structură ''iovec'' conține un pointer către buffer-ul ce conține datele și dimensiunea acestuia. | + | |
- | *''vec'', o structură [[http://lxr.linux.no/#linux+v2.6.35/include/linux/uio.h#L31|kvec]], ce conține un pointer către buffer-ul ce conține datele și dimensiunea acestuia; după cum se poate observa, are o structură similară cu structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/uio.h#L7|struct iovec]] | + | |
- | + | ||
- | Modul de lucru cu funcțiile de transmitere poate fi urmărit în cadrul handler-ului pentru apelul de sistem [[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L1644|sys_sendto]]: | + | |
- | <code c> | + | |
- | SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len, | + | |
- | unsigned, flags, struct sockaddr __user *, addr, | + | |
- | int, addr_len) | + | |
- | { | + | |
- | struct socket *sock; | + | |
- | char address[MAX_SOCK_ADDR]; | + | |
- | int err; | + | |
- | struct msghdr msg; | + | |
- | struct iovec iov; | + | |
- | int fput_needed; | + | |
- | struct file *sock_file; | + | |
- | + | ||
- | sock_file = fget_light(fd, &fput_needed); | + | |
- | err = -EBADF; | + | |
- | if (!sock_file) | + | |
- | goto out; | + | |
- | + | ||
- | sock = sock_from_file(sock_file, &err); | + | |
- | if (!sock) | + | |
- | goto out_put; | + | |
- | iov.iov_base = buff; | + | |
- | iov.iov_len = len; | + | |
- | msg.msg_name = NULL; | + | |
- | msg.msg_iov = &iov; | + | |
- | msg.msg_iovlen = 1; | + | |
- | msg.msg_control = NULL; | + | |
- | msg.msg_controllen = 0; | + | |
- | msg.msg_namelen = 0; | + | |
- | if (addr) { | + | |
- | err = move_addr_to_kernel(addr, addr_len, address); | + | |
- | if (err < 0) | + | |
- | goto out_put; | + | |
- | msg.msg_name = address; | + | |
- | msg.msg_namelen = addr_len; | + | |
- | } | + | |
- | if (sock->file->f_flags & O_NONBLOCK) | + | |
- | flags |= MSG_DONTWAIT; | + | |
- | msg.msg_flags = flags; | + | |
- | err = sock_sendmsg(sock, &msg, len); | + | |
- | + | ||
- | out_put: | + | |
- | fput_light(sock_file, fput_needed); | + | |
- | out: | + | |
- | return err; | + | |
- | } | + | |
</code> | </code> | ||
- | === Câmpuri ale structurii socket === | + | Câmpul ''filp'' reprezintă pointer-ul la structura [[http://lxr.free-electrons.com/source/include/linux/fs.h#L866|struct file]] creată o dată cu deschiderea dispozitivului din user-space. Câmpul ''vma'' este utilizat pentru a indica spaţiul de adresă virtual folosit pentru maparea memoriei dispozitivului. Un driver va putea aloca memorie (folosind ''kmalloc'' sau ''vmalloc''), urmând ca un set de procese să poată mapa în spaţiul propriu de adresă adresa alocată în driver. |
- | Structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L136|struct socket]]: | + | ===== ''remap_pfn_range'' ===== |
- | <code c> | + | |
- | /** | + | |
- | * struct socket - general BSD socket | + | |
- | * @state: socket state (%SS_CONNECTED, etc) | + | |
- | * @type: socket type (%SOCK_STREAM, etc) | + | |
- | * @flags: socket flags (%SOCK_ASYNC_NOSPACE, etc) | + | |
- | * @ops: protocol specific socket operations | + | |
- | * @fasync_list: Asynchronous wake up list | + | |
- | * @file: File back pointer for gc | + | |
- | * @sk: internal networking protocol agnostic socket representation | + | |
- | * @wait: wait queue for several uses | + | |
- | */ | + | |
- | struct socket { | + | |
- | socket_state state; | + | |
- | short type; | + | |
- | unsigned long flags; | + | |
- | /* | + | |
- | * Please keep fasync_list & wait fields in the same cache line | + | |
- | */ | + | |
- | struct fasync_struct *fasync_list; | + | |
- | wait_queue_head_t wait; | + | |
- | struct file *file; | + | Pentru maparea unui spaţiu de memorie fizică în spaţiul virtual utilizator, reprezentat de structura ''struct vm_area_struct'', se folosește apelul [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm.h#L2193|remap_pfn_range]]. Acesta va mapa un spaţiu de adresă fizic contiguu în spaţiul virtual reprezentat de ''struct vm_area_struct'': |
- | struct sock *sk; | + | <code C> |
- | const struct proto_ops *ops; | + | int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, |
- | }; | + | unsigned long pfn, unsigned long size, pgprot_t prot); |
</code> | </code> | ||
- | Câmpuri importante sunt: | + | Parametrii funcţiei sunt: |
- | *''ops'' - structura ce conține pointeri la funcțiile specifice protocolului implementat; | + | *''vma'', spaţiul de memorie virtuală în cadrul căruia se face maparea; |
- | *''sk'' - //INET socket//-ul asociat. | + | *''addr'', spaţiul virtual de adresă de unde se începe remaparea; se vor construi tabele de pagini pentru spaţiul virtual de adresă cuprins intre ''addr'' şi ''addr + size''; |
- | + | *''pfn'', numărul paginii fizice (''page frame number'') în care se mapează adresa virtuală; de obicei acesta se obţine prin shifting-ul adresei fizice [[http://elixir.free-electrons.com/linux/v4.9/source/arch/x86/include/asm/page_types.h#L8|PAGE_SHIFT]] biţi la dreapta | |
- | === Structura proto_ops === | + | *''size'', dimensiunea (în octeţi) a spaţiului de memorie care va fi remapat; |
- | + | *''prot'', caracteristicile de protecţie pentru noul spaţiu de adresă. | |
- | Structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L159|struct proto_ops]] conține implementările operațiilor specifice protocolului implementat (''TCP'', ''UDP'', etc.); funcțiile de aici vor fi apelate din funcțiile generice de lucru cu [[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L136|struct socket]] ([[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L514|sock_release]], [[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L576|sock_sendmsg]], etc.) | + | |
- | + | ||
- | Structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L159|struct proto_ops]] conține, așadar, o serie de pointeri de funcții pentru implementări specifice de protocol: | + | |
- | <code c> | + | |
- | struct proto_ops { | + | |
- | int family; | + | |
- | struct module *owner; | + | |
- | int (*release) (struct socket *sock); | + | |
- | int (*bind) (struct socket *sock, | + | |
- | struct sockaddr *myaddr, | + | |
- | int sockaddr_len); | + | |
- | int (*connect) (struct socket *sock, | + | |
- | struct sockaddr *vaddr, | + | |
- | int sockaddr_len, int flags); | + | |
- | int (*socketpair)(struct socket *sock1, | + | |
- | struct socket *sock2); | + | |
- | int (*accept) (struct socket *sock, | + | |
- | struct socket *newsock, int flags); | + | |
- | //... | + | |
- | </code> | + | |
- | + | ||
- | Inițializarea câmpului ''ops'' din [[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L136|struct socket]] se realizează în funcția [[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L1151|__sock_create]], prin apelul funcției [[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L214|create]] specifică protocolului; un apel echivalent este următorul: | + | |
- | <code c> | + | |
- | //... | + | |
- | if ((err = net_families[family]->create(net, sock, protocol)) < 0) { | + | |
- | sock->ops = NULL; | + | |
- | goto out_module_put; | + | |
- | } | + | |
- | //... | + | |
- | </code> | + | |
- | + | ||
- | Se va realiza astfel instanțierea pointerilor de funcții cu apeluri specifice tipului de protocol asociat socket-ului. Apelurile [[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L2312|sock_register]] și [[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L2344|sock_unregister]] sunt folosite pentru completarea vectorului [[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L154|net_families]]. | + | |
- | + | ||
- | Pentru restul operațiilor cu socketi (în afară de creare, închidere și transmitere/recepție mesaj, prezentate mai sus, în secțiunea [[#Operații asupra structurii socket|Operații asupra structurii socket]]), se vor apela funcțiile date de pointerii din această structură. Spre exemplu, pentru operația ''bind'', care asociază unui socket un port pe mașina locală, vom avea următoarea secvență de cod: | + | |
- | <code c> | + | |
- | #define MY_PORT 60000 | + | |
- | + | ||
- | struct sockaddr_in addr = { | + | |
- | .sin_family = AF_INET, | + | |
- | .sin_port = htons (MY_PORT), | + | |
- | .sin_addr = { htonl (INADDR_LOOPBACK) } | + | |
- | }; | + | |
- | //... | + | Un exemplu de utilizare a acestei funcţii este: |
+ | <code C> | ||
+ | struct vm_area_struct *vma; | ||
+ | unsigned long phys_pgoff; | ||
+ | int ret; | ||
- | err = sock->ops->bind (sock, (struct sockaddr *) &addr, sizeof(addr)); | + | ret = remap_pfn_range (vma, vma->vm_start, phys_pgoff, |
- | if (err < 0) { | + | vma->vm_end - vma->vm_start, vma->vm_page_prot); |
- | /* handle error */ | + | if (ret < 0) { |
+ | printk(KERN_ERR"could not map address area\n"); | ||
+ | return -EIO; | ||
} | } | ||
- | //... | ||
</code> | </code> | ||
- | După cum se poate observa, pentru transmiterea informațiilor legate de adresa și portul care se vor asocia socket-ului, se completează o structură [[http://lxr.linux.no/#linux+v2.6.35/include/linux/in.h#L181|struct sockaddr_in]]. ((hton* -- despre funcțiile ''htons'', ''htonl'' vom discuta la secțiunea [[#Conversii|Conversii]].)) | + | În exemplul de mai sus se realizează o mapare a zonei fizice începând de la numărul de pagină ''phys_pgoff'' în întreg spaţiul virtual de adresă reprezentat de ''vma''. |
- | ==== Structura sock ==== | + | Două funcţii care sunt utile sunt cele de translatare a unei adrese virtuale din spaţiul kernel în adrese fizice. Acestea sunt [[http://elixir.free-electrons.com/linux/v4.9/source/arch/x86/include/asm/io.h#L118| virt_to_phys]] pentru adrese alocate folosind ''kmalloc'' şi [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm.h#L473|vmalloc_to_pfn]] pentru adrese alocate folosind ''vmalloc''. |
- | Structura [[http://lxr.linux.no/#linux+v2.6.35/include/net/sock.h#L155|struct sock]] descrie un //INET socket//. O astfel de structură este asociată unui socket creat în user-space și, implicit, unei structuri [[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L136|struct socket]]. Structura este folosită pentru a menține informații despre starea unei conexiuni. Câmpurile structurii și operațiile asociate încep, de obicei, cu șirul ''sk_''. Câteva câmpuri sunt prezentate mai jos: | + | Astfel, numărul de pagină ''phys_pgoff'' din exemplul de mai sus se va obţine în cazul unei adrese ''kmalloc_area'' alocate folosind ''kmalloc'' astfel: |
- | <code c> | + | <code C> |
- | struct sock { | + | static char* kmalloc_area; |
- | //... | + | |
- | unsigned char sk_protocol; | + | |
- | unsigned short sk_type; | + | |
- | //... | + | |
- | struct socket *sk_socket; | + | |
- | //... | + | |
- | struct sk_buff *sk_send_head; | + | |
- | //... | + | |
- | void (*sk_state_change)(struct sock *sk); | + | |
- | void (*sk_data_ready)(struct sock *sk, int bytes); | + | |
- | void (*sk_write_space)(struct sock *sk); | + | |
- | void (*sk_error_report)(struct sock *sk); | + | |
- | int (*sk_backlog_rcv)(struct sock *sk, | + | |
- | struct sk_buff *skb); | + | |
- | void (*sk_destruct)(struct sock *sk); | + | |
- | }; | + | |
- | </code> | + | |
- | *''sk_protocol'' este tipul de protocol utilizat de socket; | + | unsigned long phys_pgoff = virt_to_phys((void *)kmalloc_area) >> PAGE_SHIFT; |
- | *''sk_type'' este tipul de socket (''SOCK_STREAM'', ''SOCK_DGRAM'', etc.) | + | |
- | *''sk_socket'' este socket-ul BSD care îl deține; | + | |
- | *''sk_send_head'' este lista de structuri ''sk_buff'' pentru transmitere; | + | |
- | *pointerii de funcții de la sfârșit sunt callback-uri pentru diverse situații. | + | |
- | + | ||
- | Inițializarea ''struct sock'' și atașarea acesteia la un socket BSD se face cu ajutorul callback-ului [[http://lxr.linux.no/#linux+v2.6.35/include/linux/net.h#L214|create]] din [[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L154|net_families]] (apelat in [[http://lxr.linux.no/#linux+v2.6.35/net/socket.c#L1151|__sock_create]]). Mai jos este prezentat modul de | + | |
- | inițializare a structurii ''struct sock'' pentru protocolul IP, în cadrul funcției [[http://lxr.linux.no/#linux+v2.6.35/net/ipv4/af_inet.c#L262|inet_create]]: | + | |
- | <code c> | + | |
- | /* | + | |
- | * Create an inet socket. | + | |
- | */ | + | |
- | + | ||
- | static int inet_create(struct net *net, struct socket *sock, int protocol) | + | |
- | { | + | |
- | struct sock *sk; | + | |
- | + | ||
- | //... | + | |
- | err = -ENOBUFS; | + | |
- | sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot); | + | |
- | if (sk == NULL) | + | |
- | goto out; | + | |
- | + | ||
- | err = 0; | + | |
- | sk->sk_no_check = answer_no_check; | + | |
- | if (INET_PROTOSW_REUSE & answer_flags) | + | |
- | sk->sk_reuse = 1; | + | |
- | + | ||
- | //... | + | |
- | sock_init_data(sock, sk); | + | |
- | + | ||
- | sk->sk_destruct = inet_sock_destruct; | + | |
- | sk->sk_family = PF_INET; | + | |
- | sk->sk_protocol = protocol; | + | |
- | sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; | + | |
- | //... | + | |
- | } | + | |
</code> | </code> | ||
+ | iar în cazul unei adrese alocate folosind ''vmalloc'': | ||
+ | <code C> | ||
+ | static char *vmalloc_area; | ||
- | ==== Structura sk_buff ==== | + | unsigned long phys_pgoff = vmalloc_to_pfn (vmalloc_area); |
- | + | ||
- | Structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/skbuff.h#L257|struct sk_buff]] (//socket buffer//) descrie un pachet de rețea. Câmpurile structurii conțin informații atât despre antetele și conținutul pachetelor cât și protocoalele utilizate, dispozitivul de rețea utilizat, pointeri către celelalte structuri ''struct sk_buff''. O descriere sumară a conținutului structurii este prezentată mai jos: | + | |
- | <code c> | + | |
- | struct sk_buff { | + | |
- | /* These two members must be first. */ | + | |
- | struct sk_buff *next; | + | |
- | struct sk_buff *prev; | + | |
- | + | ||
- | struct sock *sk; | + | |
- | ktime_t tstamp; | + | |
- | struct net_device *dev; | + | |
- | + | ||
- | struct dst_entry *dst; | + | |
- | struct sec_path *sp; | + | |
- | + | ||
- | /* | + | |
- | * This is the control buffer. It is free to use for every | + | |
- | * layer. Please put your private variables there. If you | + | |
- | * want to keep them across layers you have to do a skb_clone() | + | |
- | * first. This is owned by whoever has the skb queued ATM. | + | |
- | */ | + | |
- | char cb[48]; | + | |
- | + | ||
- | unsigned int len, | + | |
- | data_len; | + | |
- | __u16 mac_len, | + | |
- | hdr_len; | + | |
- | + | ||
- | /* ... */ | + | |
- | + | ||
- | void (*destructor)(struct sk_buff *skb); | + | |
- | + | ||
- | /* ... */ | + | |
- | + | ||
- | sk_buff_data_t transport_header; | + | |
- | sk_buff_data_t network_header; | + | |
- | sk_buff_data_t mac_header; | + | |
- | + | ||
- | /* These elements must be at the end, see alloc_skb() for details. */ | + | |
- | sk_buff_data_t tail; | + | |
- | sk_buff_data_t end; | + | |
- | unsigned char *head, | + | |
- | *data; | + | |
- | unsigned int truesize; | + | |
- | atomic_t users; | + | |
- | }; | + | |
</code> | </code> | ||
- | unde: | + | De cele mai multe ori, implementarea funcţiei ''mmap'' descrisă mai sus se reduce la un apel al funcţiei ''remap_pfn_range''. Paginile alocate folosind ''vmalloc'' nu sunt fizic contigue, astfel încât va trebui mapată fiecare pagină în parte (în cazul în care zona de memorie are mai multe pagini), apelând ''remap_pfn_range'' pentru fiecare pagină virtuală. |
- | *''next'' și ''prev'' sunt pointeri către următorul, respectiv precedentul element din lista de buffer-e; | + | |
- | *''dev'' este device-ul care transmite sau primește buffer-ul; | + | |
- | *''sk'' este socket-ul asociat buffer-ului; | + | |
- | *''destructor'' este apelul callback de dealocare a buffer-ului; | + | |
- | *''transport_header'', ''network header'' și ''mac_header'' sunt offset-uri între începutul pachetului si începutul diverselor headere din pachet. Ele sunt menținute intern de diversele niveluri de procesare prin care trece pachetul. Pentru a obține pointeri către headere, folosiți una din următoarele funcții: [[http://lxr.linux.no/#linux+v2.6.35/include/linux/tcp.h#L213|tcp_hdr]], [[http://lxr.linux.no/linux+v2.6.35/include/linux/udp.h#L43|udp_hdr]], [[http://lxr.linux.no/linux+v2.6.35/include/linux/ip.h#L110|ip_hdr]], etc. În principiu, fiecare protocol oferă o funcție de a obține o referință la header-ul respectivului protocol din cadrul unui pachet primit. De reținut: câmpul ''network_header'' nu este setat decât după ce pachetul ajunge la nivelul rețea, iar câmpul ''transport_header'' nu este setat decât după ce pachetul ajunge la nivelul transport. | + | |
- | Structura unui [[http://www.networksorcery.com/enp/protocol/ip.htm|antet IP]] ([[http://lxr.linux.no/linux+v2.6.35/include/linux/ip.h#L85|struct iphdr]]) are următoarele câmpuri: | + | ===== ''SetPageReserved''/''ClearPageReserved'' ===== |
- | <code c> | + | |
- | 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; | + | |
- | __be16 tot_len; | + | |
- | __be16 id; | + | |
- | __be16 frag_off; | + | |
- | __u8 ttl; | + | |
- | __u8 protocol; | + | |
- | __sum16 check; | + | |
- | __be32 saddr; | + | |
- | __be32 daddr; | + | |
- | /*The options start here. */ | + | |
- | }; | + | |
- | </code> | + | |
- | unde: | + | Înainte de a fi utilizată, unei pagini îi va trebui activat bitul [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/page-flags.h#L85|PG_reserved]]. Acest bit înseamnă că pagina nu poate fi evacuată pe disk (''swap''). Activarea se realizează cu ajutorul macroului [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/page-flags.h#L194|SetPageReserved]]. Macrodefiniţia primeşte ca parametru un pointer către structura de pagină, ''struct page'', care se obţine din adresa virtuală din kernel cu ajutorul funcţiei ''virt_to_page'', pentru adrese alocate folosind ''kmalloc'': |
- | *''protocol'' reprezintă protocolul de nivel transport utilizat; | + | <code C> |
- | *''saddr'' reprezintă adresa IP a nodului sursă; | + | #define NPAGES 16 |
- | *''daddr'' reprezintă adresa IP a nodului destinație. | + | |
- | + | ||
- | Structura unui [[http://www.networksorcery.com/enp/protocol/tcp.htm|antet TCP]] ([[http://lxr.linux.no/linux+v2.6.35/include/linux/tcp.h#L24|struct tcphdr]]) are următoarele câmpuri: | + | |
- | <code c> | + | |
- | struct tcphdr { | + | |
- | __be16 source; | + | |
- | __be16 dest; | + | |
- | __be32 seq; | + | |
- | __be32 ack_seq; | + | |
- | #if defined(__LITTLE_ENDIAN_BITFIELD) | + | |
- | __u16 res1:4, | + | |
- | doff:4, | + | |
- | fin:1, | + | |
- | syn:1, | + | |
- | rst:1, | + | |
- | psh:1, | + | |
- | ack:1, | + | |
- | urg:1, | + | |
- | ece:1, | + | |
- | cwr:1; | + | |
- | #elif defined(__BIG_ENDIAN_BITFIELD) | + | |
- | __u16 doff:4, | + | |
- | res1:4, | + | |
- | cwr:1, | + | |
- | ece:1, | + | |
- | urg:1, | + | |
- | ack:1, | + | |
- | psh:1, | + | |
- | rst:1, | + | |
- | syn:1, | + | |
- | fin:1; | + | |
- | #else | + | |
- | #error "Adjust your <asm/byteorder.h> defines" | + | |
- | #endif | + | |
- | __be16 window; | + | |
- | __sum16 check; | + | |
- | __be16 urg_ptr; | + | |
- | }; | + | |
- | </code> | + | |
- | *''source'' reprezintă portul sursă | + | |
- | *''dest'' reprezintă portul destinație | + | |
- | *''syn'', ''ack'', ''fin'' sunt flag-uri folosite în protocolul TCP; pentru detalii studiați această [[http://www.eventhelix.com/Realtimemantra/Networking/Tcp.pdf|diagramă]]. | + | |
- | + | ||
- | Structura unui [[http://www.networksorcery.com/enp/protocol/udp.htm|antet UDP]] ([[http://lxr.linux.no/linux+v2.6.35/include/linux/udp.h#L22|struct udphdr]]) are următoarele câmpuri: | + | |
- | <code c> | + | |
- | struct udphdr { | + | |
- | __be16 source; | + | |
- | __be16 dest; | + | |
- | __be16 len; | + | |
- | __sum16 check; | + | |
- | }; | + | |
- | </code> | + | |
- | *''source'' reprezintă portul sursă | + | |
- | *''dest'' reprezintă portul destinație | + | |
- | Un exemplu de accesare a informațiilor prezente în antetele unui pachet de rețea este următorul: | + | static char* kmalloc_area; |
- | <code c> | + | int i; |
- | struct sk_buff *skb; | + | |
- | struct iphdr *iph = ip_hdr(skb); /* IP header */ | + | for (i = 0; i < NPAGES * PAGE_SIZE; i += PAGE_SIZE) { |
- | /* iph->saddr - source IP address */ | + | SetPageReserved(virt_to_page(((unsigned long)kmalloc_area) + i)); |
- | /* iph->daddr - destination IP address */ | + | |
- | if (iph->protocol == IPPROTO_TCP) { /* TCP protocol */ | + | |
- | struct tcphdr *tcph = tcp_hdr(skb); /* TCP header */ | + | |
- | /* tcph->source - source TCP port */ | + | |
- | /* tcph->dest - destination TCP port */ | + | |
- | } else if (iph->protocol == IPPROTO_UDP) { /* UDP protocol */ | + | |
- | struct udphdr *udph = udp_hdr(skb); /* UDP header */ | + | |
- | /* udph->source - source UDP port */ | + | |
- | /* udph->dest - destination UDP port */ | + | |
} | } | ||
</code> | </code> | ||
+ | şi cu ajutorul funcţiei [[http://elixir.free-electrons.com/linux/v4.9/source/mm/vmalloc.c#L235|vmalloc_to_page]] pentru adrese alocate folosind ''vmalloc'': | ||
+ | <code C> | ||
+ | #define NPAGES 16 | ||
- | ==== Conversii ==== | + | static char* vmalloc_area; |
+ | int i; | ||
- | În sisteme diferite, există mai multe variante pentru ordonarea octeților într-un cuvânt ([[http://en.wikipedia.org/wiki/Endianness|Endianness]]), printre care: [[http://en.wikipedia.org/wiki/Endianness#Big-endian|Big Endian]] (cel mai semnificativ octet primul) și [[http://en.wikipedia.org/wiki/Endianness#Little-endian|Little Endian]] (cel mai puțin semnificativ octet primul). Având în vedere că o rețea interconectează sisteme cu platforme diferite, Internet-ul a impus o secvență standard pentru stocarea datelor numerice, numită [[http://en.wikipedia.org/wiki/Endianness#Endianness_in_networking|network byte-order]]. Spre deosebire, secvența octeților pentru reprezentarea datelor numerice pe calculatorul gazdă se numește host byte-order. Datele primite/trimise din/în rețea sunt în formatul network byte-order și trebuie facută conversia între acest format și host byte-order. | + | for (i = 0; i < NPAGES * PAGE_SIZE; i += PAGE_SIZE) { |
- | + | SetPageReserved(vmalloc_to_page((void *)(((unsigned long)vmalloc_area) + i))); | |
- | Pentru conversie există urmatoarele macrodefiniții: | + | |
- | *''u16 ''[[http://lxr.linux.no/linux+v2.6.35/include/linux/byteorder/generic.h#L140|htons]]''(u16 x)'' | + | |
- | *convertește un întreg pe 16 biți din host byte-order în network byte-order (//host to network short//) | + | |
- | *''u32 ''[[http://lxr.linux.no/linux+v2.6.35/include/linux/byteorder/generic.h#L138|htonl]]''(u32 x)'' | + | |
- | *convertește un întreg pe 32 de biți din host byte-order în network byte-order (//host to network long//) | + | |
- | *''u16 ''[[http://lxr.linux.no/linux+v2.6.35/include/linux/byteorder/generic.h#L141|ntohs]]''(u16 x)'' | + | |
- | *convertește un întreg pe 16 biți din network byte-order în host byte-order (//network to host short//) | + | |
- | *''u32 ''[[http://lxr.linux.no/linux+v2.6.35/include/linux/byteorder/generic.h#L139|ntohl]]''(u32 x)'' | + | |
- | *convertește un întreg pe 32 de biți din network byte-order în host byte-order (//network to host long//) | + | |
- | ==== netfilter ==== | + | |
- | + | ||
- | [[http://www.netfilter.org/|Netfilter]] este denumirea interfeței de kernel pentru captura pachetelor de rețea cu scopul de modificare/analiză a acestora (pentru filtrare, NAT, etc.). Interfața //netfilter// este utilizată în user-space de [[http://www.frozentux.net/documents/iptables-tutorial/|iptables]]. | + | |
- | + | ||
- | În kernel-ul Linux, captura de pachete folosind netfilter se realizează prin atașarea unor hook-uri. Hook-urile pot fi precizate în diferite locații din traseul urmat de un pachet de rețea în kernel, în funcție de necesitate. O organigramă cu traseul urmat de un pachet și zonele posibile de plasare a unui hook găsiți [[http://linux-ip.net/nf/nfk-traversal.png|aici]]. | + | |
- | + | ||
- | Header-ul inclus atunci când se folosește netfilter este [[http://lxr.linux.no/linux+v2.6.35/include/linux/netfilter.h|linux/netfilter.h]]. | + | |
- | + | ||
- | Un hook se definește prin intermediul structurii [[http://lxr.linux.no/linux+v2.6.35/include/linux/netfilter.h#L96|struct nf_hook_ops]]: <code c> | + | |
- | struct nf_hook_ops { | + | |
- | struct list_head list; | + | |
- | + | ||
- | /* User fills in from here down. */ | + | |
- | nf_hookfn *hook; | + | |
- | struct module *owner; | + | |
- | int pf; | + | |
- | int hooknum; | + | |
- | /* Hooks are ordered in ascending priority. */ | + | |
- | int priority; | + | |
- | }; | + | |
- | </code> | + | |
- | + | ||
- | unde: | + | |
- | *''pf'' este tipul pachetului ([[http://lxr.linux.no/#linux+v2.6.35/include/linux/socket.h#L199|PF_INET]], etc.); | + | |
- | *''hooknum'' este tipul de hook utilizat; pentru IP, acestea sunt definite în [[http://lxr.linux.no/linux+v2.6.35/include/linux/netfilter_ipv4.h|linux/netfilter_ipv4.h]]: <code c> | + | |
- | /* IP Hooks */ | + | |
- | /* After promisc drops, checksum checks. */ | + | |
- | #define NF_INET_PRE_ROUTING 0 | + | |
- | /* If the packet is destined for this box. */ | + | |
- | #define NF_INET_LOCAL_IN 1 | + | |
- | /* If the packet is destined for another interface. */ | + | |
- | #define NF_INET_FORWARD 2 | + | |
- | /* Packets coming from a local process. */ | + | |
- | #define NF_INET_LOCAL_OUT 3 | + | |
- | /* Packets about to hit the wire. */ | + | |
- | #define NF_INET_POST_ROUTING 4 | + | |
- | #define NF_INET_NUMHOOKS 5 | + | |
- | </code> | + | |
- | *''priority'' este prioritatea; prioritățile sunt definite în [[http://lxr.linux.no/linux+v2.6.35/include/linux/netfilter_ipv4.h|linux/netfilter_ipv4.h]]: <code c> | + | |
- | enum nf_ip_hook_priorities { | + | |
- | NF_IP_PRI_FIRST = INT_MIN, | + | |
- | NF_IP_PRI_CONNTRACK_DEFRAG = -400, | + | |
- | NF_IP_PRI_RAW = -300, | + | |
- | NF_IP_PRI_SELINUX_FIRST = -225, | + | |
- | NF_IP_PRI_CONNTRACK = -200, | + | |
- | NF_IP_PRI_MANGLE = -150, | + | |
- | NF_IP_PRI_NAT_DST = -100, | + | |
- | NF_IP_PRI_FILTER = 0, | + | |
- | NF_IP_PRI_NAT_SRC = 100, | + | |
- | NF_IP_PRI_SELINUX_LAST = 225, | + | |
- | NF_IP_PRI_CONNTRACK_HELPER = INT_MAX - 2, | + | |
- | NF_IP_PRI_NAT_SEQ_ADJUST = INT_MAX - 1, | + | |
- | NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX, | + | |
- | NF_IP_PRI_LAST = INT_MAX, | + | |
- | }; | + | |
- | </code> | + | |
- | *''nhook_fn'' este handler-ul apelat in momentul capturării unui pachet de rețea (în forma unei structuri ''struct sk_buff''); prototipul este definit în [[http://lxr.linux.no/linux+v2.6.35/include/linux/netfilter.h|linux/netfilter.h]]: <code c> | + | |
- | typedef unsigned int nf_hookfn(unsigned int hooknum, | + | |
- | struct sk_buff *skb, | + | |
- | const struct net_device *in, | + | |
- | const struct net_device *out, | + | |
- | int (*okfn)(struct sk_buff *)); | + | |
- | </code> | + | |
- | *''skb'' este pointer la pachet-ul de rețea capturat. | + | |
- | *Se observă că se poate folosi același handler pentru mai multe hook-uri, deosebirea între acestea realizându-se cu ajutorul parametrului ''hooknum''. | + | |
- | *Un handler de captură poate întoarce una din [[http://lxr.linux.no/linux+v2.6.35/include/linux/netfilter.h#L17|constantele NF_*]]: <code c> | + | |
- | /* Responses from hook functions. */ | + | |
- | #define NF_DROP 0 | + | |
- | #define NF_ACCEPT 1 | + | |
- | #define NF_STOLEN 2 | + | |
- | #define NF_QUEUE 3 | + | |
- | #define NF_REPEAT 4 | + | |
- | #define NF_STOP 5 | + | |
- | </code> | + | |
- | *''NF_DROP'' este folosit pentru a filtra (ignora) un pachet, iar ''NF_ACCEPT'' este folosit pentru a accepta un pachet și a-l transmite mai departe. | + | |
- | + | ||
- | Înregistrarea/deînregistrarea unui hook se realizează cu ajutorul funcțiilor definite în [[http://lxr.linux.no/linux+v2.6.35/include/linux/netfilter.h|linux/netfilter.h]]: <code c> | + | |
- | /* Function to register/unregister hook points. */ | + | |
- | int nf_register_hook(struct nf_hook_ops *reg); | + | |
- | void nf_unregister_hook(struct nf_hook_ops *reg); | + | |
- | int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n); | + | |
- | void nf_unregister_hooks(struct nf_hook_ops *reg, unsigned int n); | + | |
- | </code> | + | |
- | + | ||
- | * **Atenție!** Există anumite restricții legate de utilizarea funcțiilor de extragere a antetelor dintr-un ''struct sk_buff'' dat ca parametru într-un hook netfilter. În timp ce antetul IP poate fi obținut de fiecare dată folosind ''ip_hdr()'', antetele TCP și UDP pot fi obținute cu ''tcp_hdr()'', respectiv ''udp_hdr()'' numai pentru pachete care pornesc dinspre sistem, și nu pentru cele care intră. În cazul din urmă, trebuie calculat manual offset-ul antetelor în pachet: <code c> | + | |
- | // Pentru pachete TCP (iph->protocol == IPPROTO_TCP) | + | |
- | tcph = (struct tcphdr*)((__u32*)iph + iph->ihl); | + | |
- | // Pentru pachete UDP (iph->protocol == IPPROTO_UDP) | + | |
- | udph = (struct udphdr*)((__u32*)iph + iph->ihl); | + | |
- | </code> | + | |
- | + | ||
- | Acest cod funcționează în toate situațiile de filtrare, și astfel este recomandată folosirea lui în locul funcțiilor de acces la antete. | + | |
- | + | ||
- | Un exemplu de utilizare a unui hook netfilter este prezentat mai jos: | + | |
- | <code c> | + | |
- | #include <linux/netfilter.h> | + | |
- | #include <linux/netfilter_ipv4.h> | + | |
- | + | ||
- | static unsigned int my_nf_hookfn(unsigned int hooknum, | + | |
- | struct sk_buff *skb, | + | |
- | const struct net_device *in, | + | |
- | const struct net_device *out, | + | |
- | int (*okfn)(struct sk_buff *)) | + | |
- | { | + | |
- | /* process packet */ | + | |
- | //... | + | |
- | + | ||
- | return NF_ACCEPT; | + | |
} | } | ||
- | |||
- | static struct nf_hook_ops my_nfho = { | ||
- | .owner = THIS_MODULE, | ||
- | .hook = my_nf_hookfn, | ||
- | .hooknum = NF_INET_LOCAL_IN, | ||
- | .pf = PF_INET, | ||
- | .priority = NF_IP_PRI_FIRST | ||
- | }; | ||
- | |||
- | int __init my_hook_init(void) | ||
- | { | ||
- | return nf_register_hook(&my_nfho); | ||
- | } | ||
- | |||
- | void __exit my_hook_exit(void) | ||
- | { | ||
- | nf_unregister_hook(&my_nfho); | ||
- | } | ||
- | |||
- | module_init(my_hook_init); | ||
- | module_exit(my_hook_exit); | ||
</code> | </code> | ||
- | ===== Windows networking ===== | ||
- | În kernel-ul Windows lucrul cu rețeaua se realizează prin intermediul NPI ([[http://msdn.microsoft.com/en-us/library/ff568373.aspx|Network Programming Interface]]). NPI definește interfața între module de networking care doresc să comunice între ele (clienți și provideri). | + | Înainte de eliberarea paginii (''kfree''/''vfree''), bitul de pagină rezervată trebuie dezactivat cu ajutorul macroului [[http://lxr.free-electrons.com/source/include/linux/page-flags.h?v=4.9#L185|ClearPageReserved]]. Acesta primeşte ca parametru acelaşi pointer către o structura de pagină care a fost dat la ''SetPageReserved''. |
- | ==== TDI ==== | ||
- | |||
- | În Windows 2000/XP/2003, NPI este reprezentată de TDI ([[http://msdn.microsoft.com/en-us/library/ff565685.aspx|Transport Driver Interface]]). TDI expune o interfață peste care rulează așa numiții //clienți TDI// care implementează operațiile specifice. Socket-ii Windows (winsockets) sunt implementați peste clienți TDI. | ||
- | |||
- | TDI folosește obiecte fișier pentru comunicație și pentru controlul acesteia. Astfel, TDI folosește obiecte fișier pentru a descrie adrese de transport deschise, conexiuni asociate unei adrese de transport și canale de control cu scopul de stabilire și consultare a parametrilor de comunicație. | ||
- | |||
- | === NDIS === | ||
- | |||
- | Arhitectura de rețea pentru Windows este una modulară. TDI este construit peste NDIS ([[http://msdn.microsoft.com/en-us/library/ff556938.aspx|Network Driver Interface Specification]]). NDIS este o bibliotecă de funcții kernel care abstractizează diferitele niveluri ale rețelei; fiecărui astfel de nivel i se pot asocia drivere specifice. Folosind interfețele NDIS se pot construi: | ||
- | *[[http://msdn.microsoft.com/en-us/library/ff557065.aspx|NDIS Miniport Drivers]], drivere pentru NIC (Network Interface Card); | ||
- | *[[http://msdn.microsoft.com/en-us/library/ff557149.aspx|NDIS Protocol Drivers]], drivere utilizate pentru implementarea unui protocol; acestea transmit și primesc pachete de rețea prin intermediul unui driver tip miniport; | ||
- | *[[http://msdn.microsoft.com/en-us/library/ff557012.aspx|NDIS Intermediate Drivers]], drivere utilizate în filtrarea de pachete, load balancing, colectarea de statistici de rețea. | ||
- | |||
- | === Winsock Kernel === | ||
- | |||
- | Începând cu Windows Vista, se recomandă utilizarea WSK ([[http://msdn.microsoft.com/en-us/library/ff556958.aspx|Winsock Kernel]]) în locul TDI. WSK vine cu un API nou pentru o performanță mai bună și programarea mai ușoara a driver-elor. WSK NPI suportă operații pe socket-i asemănătoare cu cele din user-space. | ||
- | |||
- | === Operații TDI === | ||
- | |||
- | [[http://msdn.microsoft.com/en-us/library/ff565119.aspx|Operațiile TDI]] sunt apropiate operațiilor disponibile în user-space pentru lucrul cu socket-i: | ||
- | *deschiderea unei adrese de transport (echivalent cu un socket local și un apel bind asociat); | ||
- | *deschiderea unui punct de conexiune; | ||
- | *cererea unei conexiuni; | ||
- | *acceptarea unei conexiuni; | ||
- | *transmiterea unui segment de date pe o conexiune; | ||
- | *primirea unui segment de date pe o conexiune; | ||
- | *deconectarea; | ||
- | *închiderea unui punct de conexiune; | ||
- | *transmiterea unei datagrame; | ||
- | *primirea unei datagrame; | ||
- | *diverse operații de control. | ||
- | |||
- | [[http://msdn.microsoft.com/en-us/library/ff565091.aspx|API-ul]] furnizat de TDI pentru implementarea acestor operații este unul destul de stufos iar [[http://msdn.microsoft.com/en-us/library/ff565119.aspx|documentația asociată]] poate fi pe alocuri confuză și greu de urmărit; drept urmare, nu vom insista. | ||
- | |||
- | O descriere foarte bună a modului de utilizare a API-ului TDI (împreună cu un exemplu cod sursa) găsiți la [[http://www.codeproject.com/kb/system/driverdev5asp.aspx|The Code Project]]. | ||
- | ==== Captura de pachete în kernel-ul Windows ==== | ||
- | |||
- | Captura de pachete în kernel-ul Windows se realizează prin intermediul [[http://msdn.microsoft.com/en-us/library/ff565094.aspx|TDI Drivers]], [[http://msdn.microsoft.com/en-us/library/ff557012.aspx|NDIS Intermediate Drivers]], sau așa numitelor [[http://msdn.microsoft.com/en-us/library/ff546594.aspx|Hook Drivers]]. Deși primele doua tipuri oferă mai multă flexibilitate și sunt recomandate în locul hook drivers, nu vom insista pe acestea deoarece au un API complex. | ||
- | |||
- | Kernel-ul de Windows 2000/XP/2003 oferă API pentru două tipuri de hook drivers: | ||
- | *[[http://msdn.microsoft.com/en-us/library/ff546489.aspx|Filter-Hook Drivers]], adăugă funcționalitate driver-ului de filtrare IP (IP filter driver) existent în sistem; dezavantajul este că nu poate exista decât un singur filter-hook driver în sistem pentru a fi folosit de IP filter driver. | ||
- | *[[http://msdn.microsoft.com/en-us/library/ff546499.aspx|Firewall-Hook Drivers]], este folosit pentru a filtra pachete primite/transmise de-a lungul unui firewall în contextul protocolului TCP/IP. | ||
- | |||
- | === Filter-Hook Driver === | ||
- | |||
- | Un [[http://msdn.microsoft.com/en-us/library/ff546489.aspx|filter-hook driver]] înregistrează un callback (denumit filter hook) în cadrul IP filter driver. Funcția de callback este apoi utilizată pentru a prelucra pachetul. | ||
- | |||
- | == Callback == | ||
- | |||
- | La fel cum în Linux, tipul funcției de filtrare era ''nf_hook_fn'', în cazul filter-hook, funcția de callback trebuie să fie [[http://msdn.microsoft.com/en-us/library/ff562312.aspx|PacketFilterExtensionPtr]]: | ||
- | <code c> | ||
- | typedef PF_FORWARD_ACTION | ||
- | (*PacketFilterExtensionPtr)( | ||
- | IN unsigned char *PacketHeader, | ||
- | IN unsigned char *Packet, | ||
- | IN unsigned int PacketLength, | ||
- | IN unsigned int RecvInterfaceIndex, | ||
- | IN unsigned int SendInterfaceIndex, | ||
- | IN IPAddr RecvLinkNextHop, | ||
- | IN IPAddr SendLinkNextHop | ||
- | ); | ||
- | </code> | ||
- | |||
- | unde: | ||
- | *''PacketHeader'' este pointer la antetul IP al pachetului; de obicei se va face cast la un pointer de tip [[http://msdn.microsoft.com/en-us/library/ff562312.aspx#ddk_ipheader_nr|IPHeader]]: <code c> | ||
- | typedef struct IPHeader { | ||
- | UCHAR iph_verlen; | ||
- | UCHAR iph_tos; | ||
- | USHORT iph_length; | ||
- | USHORT iph_id; | ||
- | USHORT iph_offset; | ||
- | UCHAR iph_ttl; | ||
- | UCHAR iph_protocol; | ||
- | USHORT iph_xsum; | ||
- | ULONG iph_src; | ||
- | ULONG iph_dest; | ||
- | } IPHeader; | ||
- | </code> | ||
- | *''Packet'' este un pointer la informația conținută de pachetul IP fără antetul acestuia; de obicei, se vor crea structuri ''UDPHeader'' și ''TCPHeader'' și se va face un cast corespunzător; | ||
- | *''PacketLength'' este lungimea, în octeți, a conținutului pachetului (fără antet). | ||
- | |||
- | Funcția de callback (hook-ul) poate întoarce trei valori: | ||
- | *''PF_FORWARD'': IP filter driver va transmite imediat pachetul următorului nivel din stiva de rețea; | ||
- | *''PF_DROP'': IP filter driver va filtra (ignora) pachetul; | ||
- | *''PF_PASS'': IP filter driver va procesa pachetul și apoi îl va transmite următorului nivel din stiva de rețea. | ||
- | |||
- | Un exemplu de astfel de funcție: | ||
- | <code c> | ||
- | static PF_FORWARD_ACTION cbFilterFunction ( | ||
- | unsigned char *PacketHeader, | ||
- | unsigned char *Packet, | ||
- | unsigned int PacketLength, | ||
- | unsigned int RecvInterfaceIndex, | ||
- | unsigned int SendInterfaceIndex, | ||
- | unsigned long RecvLinkNextHop, | ||
- | unsigned long SendLinkNextHop) { | ||
- | |||
- | IPHeader *iph = (IPHeader *) PacketHeader; | ||
- | |||
- | if (iph->iph_protocol == IPPROTO_TCP) { | ||
- | TCPHeader *tcph = (TCPHeader*) Packet; | ||
- | /* process tcp packet from IP address iph->iph_src */ | ||
- | } else if (iph->protocol == IPPROTO_UDP) { | ||
- | UDPHeader *udph = (UDPHeader*) Packet; | ||
- | /* process tcp packet from IP address iph->iph_src */ | ||
- | } | ||
- | |||
- | return PF_FORWARD; | ||
- | } | ||
- | </code> | ||
- | |||
- | == Înregistrare/deînregistrare == | ||
- | |||
- | Hook-ul se înregistrează în cadrul IP filter driver prin intermediul rutinei ioctl [[http://msdn.microsoft.com/en-us/library/ff548976.aspx|IOCTL_PF_SET_EXTENSION_POINTER]]; se creează un IRP care apoi este transmis către IP filter driver. Pașii urmați sunt: | ||
- | |||
- | * Obținerea obiectului dispozitiv (device object) asociat IP filter driver folosind [[http://msdn.microsoft.com/en-us/library/ff549198.aspx|IoGetDeviceObjectPointer]]: <code c> | ||
- | NTSTATUS status = STATUS_SUCCESS; | ||
- | UNICODE_STRING filterName; | ||
- | PDEVICE_OBJECT ipDeviceObject = NULL; | ||
- | PFILE_OBJECT ipFileObject = NULL; | ||
- | |||
- | RtlInitUnicodeString(&filterName, DD_IPFLTRDRVR_DEVICE_NAME); | ||
- | status = IoGetDeviceObjectPointer(&filterName, STANDARD_RIGHTS_ALL, | ||
- | &ipFileObject, &ipDeviceObject); | ||
- | </code> | ||
- | * Funcția primește ca argumente numele IP filter driver și pointeri la buffer-ele care rețin obiectul dispozitiv și obiectul fișier asociat. | ||
- | * Construirea IRP-ului de înregistrare folosind [[http://msdn.microsoft.com/en-us/library/ff548318.aspx|IoBuildDeviceIoControlRequest]]: <code c> | ||
- | PDEVICE_OBJECT ipDeviceObject = NULL; | ||
- | PF_SET_EXTENSION_HOOK_INFO filterData; | ||
- | KEVENT event; | ||
- | IO_STATUS_BLOCK ioStatus; | ||
- | PIRP irp; | ||
- | |||
- | /* get pointer to IP filter driver device object */ | ||
- | //... | ||
- | |||
- | /* specify the callback filter function */ | ||
- | filterData.ExtensionPointer = cbFilterFunction; | ||
- | |||
- | /* init event that will notify us of the completion of the request */ | ||
- | KeInitializeEvent(&event, NotificationEvent, FALSE); | ||
- | irp = IoBuildDeviceIoControlRequest( | ||
- | IOCTL_PF_SET_EXTENSION_POINTER, | ||
- | ipDeviceObject, | ||
- | (PVOID) &filterData, | ||
- | sizeof (PF_SET_EXTENSION_HOOK_INFO), | ||
- | NULL, | ||
- | 0, | ||
- | FALSE, | ||
- | &event, | ||
- | &ioStatus); | ||
- | </code> | ||
- | * Printre parametrii transmiși se număra: | ||
- | * valoarea [[http://msdn.microsoft.com/en-us/library/ff548976(VS.85).aspx|IOCTL_PF_SET_EXTENSION_POINTER]] prin care se specifică hook-ul; | ||
- | * pointerul la obiectul dispozitiv asociat IP filter driver; | ||
- | * buffer ce conține o structură [[http://msdn.microsoft.com/en-us/library/ff562370(VS.85).aspx|PF_SET_EXTENSION_HOOK_INFO]]; câmpul ''ExtensionPointer'' al acestei structuri specifică callback-ul; pentru a elibera hook-ul din sistem, aici se poate transmite ''NULL''). | ||
- | * Apelul întoarce IRP-ul de înregistare a hook-ului. | ||
- | * Transmiterea IRP-ului către IP filter driver folosind [[http://msdn.microsoft.com/en-us/library/ff548336.aspx|IoCallDriver]]: <code c> | ||
- | status = IoCallDriver (ipDeviceObject, irp); | ||
- | </code> | ||
- | |||
- | == Deînregistrare == | ||
- | |||
- | Pentru deînregistare trebuie construit un IRP pentru care pointerul la structura ''PF_SET_EXTENSION_HOOK_INFO'' să fie ''NULL''. | ||
- | |||
- | == IpFilterDriver == | ||
- | |||
- | Înainte de încărcarea în kernel a unui filter-hook driver trebuie pornit serviciul ''IpFilterDriver'': | ||
- | <code> | ||
- | net start IpFilterDriver | ||
- | </code> | ||
- | |||
- | O implementare de Filter-Hook driver găsiți la The Code Project: [[http://www.codeproject.com/KB/IP/drvfltip.aspx|Developing Firewalls for Windows 2000/XP]]. | ||
- | |||
- | === Firewall-Hook Driver === | ||
- | |||
- | Avantajul utilizării [[http://msdn.microsoft.com/en-us/library/ff546499.aspx|Firewall-Hook driver]] este posibilitatea de inserare a mai multor funcții de filtrare. Fiecare funcție are asociată o anumită prioritate. Sistemul va apela funcțiile în ordinea priorității pâna în momentul în care una din ele întoarce ''DROP_PACKET''. | ||
- | |||
- | == Callback == | ||
- | |||
- | La fel ca la filter-hook, filtrarea se realizează prin intermediul unui callback. Tipul acestuia este ''IPPacketFirewallPtr'': | ||
- | <code c> | ||
- | FORWARD_ACTION cbFilterFunction(VOID **pData, | ||
- | UINT RecvInterfaceIndex, | ||
- | UINT *pSendInterfaceIndex, | ||
- | UCHAR *pDestinationType, | ||
- | VOID *pContext, | ||
- | UINT ContextLength, | ||
- | struct IPRcvBuf **pRcvBuf); | ||
- | </code> | ||
- | |||
- | unde: | ||
- | *''pData'' este un pointer la un buffer de pachet de tipul [[http://msdn.microsoft.com/en-us/library/aa915349.aspx|struct IPRcvBuf]]; | ||
- | *''pContext'' este un pointer la o structură ''FIREWALL_CONTEXT_T'' de unde se pot afla informații despre pachet. | ||
- | |||
- | Funcția poate întoarce trei valori: | ||
- | *''FORWARD'': pachetul este admis și este transmis următorului callback; | ||
- | *''DROP'': pachetul este filtrat (ignorat); | ||
- | *''ICMP_ON_DROP'': pachetul este filtrat și se transmite un pachet ''ICMP'' sistemului de la distanță. | ||
- | |||
- | Structura [[http://msdn.microsoft.com/en-us/library/aa915349.aspx|struct IPRcvBuf]] are următorul conținut: | ||
- | <code c> | ||
- | struct IPRcvBuf { | ||
- | /* point to the next buffer in the chain */ | ||
- | struct IPRcvBuf *ipr_next; | ||
- | |||
- | /* always 0 */ | ||
- | UINT ipr_owner; | ||
- | |||
- | /* buffer data */ | ||
- | UCHAR *ipr_buffer; | ||
- | |||
- | /* buffer data size */ | ||
- | UINT ipr_size; | ||
- | |||
- | //... | ||
- | }; | ||
- | </code> | ||
- | |||
- | Spre deosebire de filter-hook, pachetul nu mai este complet asamblat. El va trebui reasamblat urmând pointerul ''ipr_next'' la următorul buffer: | ||
- | <code c> | ||
- | FORWARD_ACTION cbFilterFunction(VOID **pData, | ||
- | UINT RecvInterfaceIndex, | ||
- | UINT *pSendInterfaceIndex, | ||
- | UCHAR *pDestinationType, | ||
- | VOID *pContext, | ||
- | UINT ContextLength, | ||
- | struct IPRcvBuf **pRcvBuf) | ||
- | { | ||
- | char *pPacket = NULL; | ||
- | int iBufferSize; | ||
- | struct IPRcvBuf *pBuffer = (struct IPRcvBuf *) *pData; | ||
- | |||
- | /* total packet size */ | ||
- | iBufferSize = buffer->ipr_size; | ||
- | while (pBuffer->ipr_next != NULL) { | ||
- | pBuffer = pBuffer->ipr_next; | ||
- | iBufferSize += pBuffer->ipr_size; | ||
- | } | ||
- | |||
- | /* allocate memory for entire packet */ | ||
- | pPacket = (char *) ExAllocatePool(NonPagedPool, iBufferSize); | ||
- | if (pPacket != NULL) { | ||
- | unsigned int iOffset = 0; | ||
- | pBuffer = (struct IPRcvBuf *) *pData; | ||
- | |||
- | /* copy all the buffers in the entire packet buffer */ | ||
- | memcpy(pPacket, pBuffer->ipr_buffer, pBuffer->ipr_size); | ||
- | while(pBuffer->ipr_next != NULL) { | ||
- | iOffset += pBuffer->ipr_size; | ||
- | pBuffer = pBbuffer->ipr_next; | ||
- | memcpy(pPacket + iOffset, pBuffer->ipr_buffer, | ||
- | pBbuffer->ipr_size); | ||
- | } | ||
- | } | ||
- | //... | ||
- | } | ||
- | </code> | ||
- | |||
- | == Înregistrare/deînregistrare == | ||
- | |||
- | Înregistrarea/deînregistrarea unui callback se realizează la fel ca la filter-driver. Acum, însa, codul ioctl folosit la crearea IRP-ului este ''IOCTL_IP_SET_FIREWALL_HOOK'', iar structura utilizată este de tip ''IP_SET_FIREWALL_HOOK_INFO'': | ||
- | <code c> | ||
- | typedef struct _IP_SET_FIREWALL_HOOK_INFO { | ||
- | /* filter callback */ | ||
- | IPPacketFirewallPtr FirewallPtr; | ||
- | |||
- | /* priority of the hook */ | ||
- | UINT Priority; | ||
- | |||
- | /* if TRUE then ADD else DELETE */ | ||
- | BOOLEAN Add; | ||
- | } IP_SET_FIREWALL_HOOK_INFO, *PIP_SET_FIREWALL_HOOK_INFO; | ||
- | </code> | ||
- | |||
- | Pentru înregistarea unui callback câmpul ''Add'' al structurii va fi inițializat la ''TRUE'', iar pentru deînregistrare la ''FALSE'': | ||
- | <code c> | ||
- | PDEVICE_OBJECT ipDeviceObject = NULL; | ||
- | IP_SET_FIREWALL_HOOK_INFO filterData; | ||
- | KEVENT event; | ||
- | IO_STATUS_BLOCK ioStatus; | ||
- | PIRP irp; | ||
- | |||
- | /* get pointer to IP filter driver device object */ | ||
- | //... | ||
- | |||
- | /* specify the callback filter function */ | ||
- | filterData.FirewallPtr = filterFunction; | ||
- | filterData.Priority = 1; | ||
- | filterData.Add = load; | ||
- | |||
- | KeInitializeEvent(&event, NotificationEvent, FALSE); | ||
- | |||
- | /* build IRP */ | ||
- | irp = IoBuildDeviceIoControlRequest( | ||
- | IOCTL_IP_SET_FIREWALL_HOOK, | ||
- | ipDeviceObject, | ||
- | (PVOID) &filterData, | ||
- | sizeof (IP_SET_FIREWALL_HOOK_INFO), | ||
- | NULL, | ||
- | 0, | ||
- | FALSE, | ||
- | &event, | ||
- | &ioStatus); | ||
- | </code> | ||
- | |||
- | O implementare de Firewall-Hook driver găsiți la The Code Project: [[http://www.codeproject.com/KB/IP/FwHookDrv.aspx|An Adventure: How to implement a Firewall-Hook Driver?]] | ||
- | |||
- | === Windows Filtering Platform Callout Drivers === | ||
- | |||
- | Începând cu Windows Vista, implementarea de drivere de filtrare de pachete se va realiza utilizând [[http://msdn.microsoft.com/en-us/library/ff571068.aspx|Windows Filtering Platform Callout Drivers]]. Aceste drivere sunt construite în cadrul WFP ([[http://msdn.microsoft.com/en-us/library/ff571066.aspx|Windows Filtering Platform]]) pentru a prelucra pachetele de rețea. | ||
- | |||
- | ==== Conversii ==== | ||
- | |||
- | La fel ca și pentru Linux, pentru datele primite/transmise din/în rețea trebuie facută conversia între network byte-order și host byte-order. | ||
- | |||
- | Pentru conversie există urmatoarele funcții: | ||
- | *''USHORT ''[[http://msdn.microsoft.com/en-us/library/ff563016.aspx|RtlUshortByteSwap]]''(USHORT Source)'' – convertește un întreg pe 16 biți între formatul host byte-order și network byte-order (echivalent pentru ''ntohs'' și ''htons'') | ||
- | *''ULONG ''[[http://msdn.microsoft.com/en-us/library/ff562886.aspx|RtlUlongByteSwap]]''(ULONG Source)'' – convertește un întreg pe 32 de biți între formatul host byte-order și network byte-order order (echivalent pentru ''ntohl'' și ''htonl'') | ||
- | ===== netcat ===== | ||
- | |||
- | Cand se dezvolta aplicații care includ o parte de networking, una din cele mai folosite unelte este netcat. Supranumit și "Swiss-army knife for TCP/IP", netcat permite printre altele: | ||
- | *Inițierea de conexiuni TCP; | ||
- | *Așteptarea unei conexiuni TCP; | ||
- | *Trimiterea și primirea de pachete UDP; | ||
- | *Afișarea traficului sub forma de hexdump; | ||
- | *Execuția unui program la stabilirea conexiunii (de exemplu, un shell); | ||
- | *Setarea unor opțiuni speciale în pachetele trimise. | ||
- | |||
- | Pentru a iniția o conexiune TCP: | ||
- | <code> | ||
- | nc hostname port | ||
- | </code> | ||
- | |||
- | Pentru a asculta pe un port TCP: | ||
- | <code> | ||
- | nc -l -p port | ||
- | </code> | ||
- | |||
- | Primirea și trimiterea pachetelor UDP se realizează adăugând opțiunea ''-u'' în linia de comandă. | ||
- | |||
- | **Observație:** numele comenzii este ''nc''; de multe ori ''netcat'' este un alias pentru această comandă. Există și alte implementări ale comenzii netcat, unele având parametrii puțin diferiți față de implementarea clasică. Consultați ''man nc'' sau rulați ''nc -h'' pentru a vedea modul de utilizare. | ||
- | |||
- | Pentru mai multe informații despre netcat, citiți acest [[http://team5150.com/~random/apps/netcat/Netcat_Tutorial.pdf|tutorial]]. | ||
- | |||
- | ===== Exerciții ===== | ||
- | |||
- | <note important> | ||
- | * Toate exercițiile de Linux vor fi realizate în cadrul **mașinii virtuale** de Linux (dacă nu este specificat altfel), iar cele de Windows în cadrul **mașinii virtuale** de Windows. | ||
- | * Recomandăm să porniți VMware într-un workspace separat. | ||
- | * Excepție face folosirea LXR, puteți folosi mașina fizică pentru căutări. În cazul folosirii cscope, folosiți mașina virtuală, mai precis directorul ''/usr/src/linux''. | ||
- | </note> | ||
- | |||
- | * Mașinile virtuale pot fi accesate, respectiv, prin Multicast DNS, folosind numele ''spook.local'' (Linux) și ''chooch.local'' Windows. | ||
- | * Pentru accesarea mașinilor virtuale puteți folosi SSH. Conturile mașinilor virtuale sunt: | ||
- | * Linux: root/student (adică utilizatorul ''root'' cu parola ''student''; conectare folosind comanda ''ssh root@spook.local''), student/student (''ssh student@spook.local'') | ||
- | * Windows: Administrator/student (''ssh Administrator@spook.local''), student/student (''ssh student@chooch.local'') | ||
- | * Fiind vorba de kernel programming/driver development veți folosi preponderent conturile privilegiate (''root'' respectiv ''Administrator''). | ||
- | * Există create două alias-uri SSH pentru conectare rapidă la mașinile virtuale: | ||
- | * Linux (root): ''ssh linux'' | ||
- | * Windows (Administrator): ''ssh windows'' | ||
- | * Pentru accesarea locală a sistemului de fișiere de pe mașinile virtuale puteți folosi Samba/CIFS, prin intermediul a două scripturi: | ||
- | * Montarea ''/root/share/'' de pe mașina virtuală Linux în ''/home/student/linux-share/'' pe sistemul local se face folosind comanda ''~/bin/mount-linux''. | ||
- | * Montarea ''/home/Administrator/share/'' (Cygwin) de pe mașina virtuală Windows în ''/home/student/windows-share/'' pe sistemul local se face folosind comanda ''~/bin/mount-windows''. | ||
- | |||
- | <note warning> | ||
- | Înainte de a rezolva un exercițiu, citiți cu **atenție** toate bullet-urile acestuia. | ||
- | </note> | ||
- | |||
- | <note> | ||
- | Punctajul maxim obținut este de **10 puncte**. Bonusul poate recupera lipsa de activitate de la alte laboratoare. | ||
- | </note> | ||
- | ==== Linux ==== | ||
- | |||
- | * Folosiți directorul ''lin/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab11-tasks.zip|arhiva de sarcini]] a laboratorului. | ||
- | * Punctaj total: **6 puncte** | ||
- | |||
- | <note important> | ||
- | * Pe mașina virtuală de Linux recomandăm: | ||
- | * Conectarea prin SSH de pe sistemul fizic folosind comanda ''ssh linux''. | ||
- | * Folosirea ''wget'' pentru descărcarea [[http://elf.cs.pub.ro/so2/res/laboratoare/lab11-tasks.zip | arhivei de sarcini]] a laboratorului. | ||
- | * În cazul apariției unui oops, reporniți mașina virtuală (sau faceți revert la un snapshot realizat anterior de voi). | ||
- | </note> | ||
- | |||
- | - (**2 puncte**) Afișare pachete în kernel space | ||
- | * Creați un modul de kernel care afișează adresa și portul sursă pentru pachetele TCP care inițiază o conexiune spre exterior. | ||
- | * Porniți de la scheletul din ''lin/1-2-netfilter/''. | ||
- | * Completați zonele marcate cu ''TODO 1''. | ||
- | * Înregistrați hook-ul netfilter. | ||
- | * Parcurgeți secțiunea [[#netfilter|netfilter]]. | ||
- | * Va trebui sa folosiți un hook ''NF_INET_LOCAL_OUT''. | ||
- | * Obțineți antetul IP folosind funcția [[http://lxr.linux.no/linux+v3.7.8/include/linux/ip.h#L23|ip_hdr]], iar antetul TCP folosind funcția [[http://lxr.linux.no/linux+v3.7.8/include/linux/tcp.h#L28|tcp_hdr]]. | ||
- | * Parcurgeți secțiunea [[#structura_sk_buff|Structura sk_buff]]. | ||
- | * Pentru a identifica pachetul de inițiere a unei conexiuni TCP studiați această [[http://www.eventhelix.com/Realtimemantra/Networking/Tcp.pdf|diagramă]]. | ||
- | * Pachetul are flagul SYN din antetul TCP activ, iar flagul ACK inactiv. | ||
- | * Pentru afișarea adresei IP sursă folosiți formatul ''%pI4'' al funcției [[https://www.kernel.org/doc/Documentation/printk-formats.txt|printk]]. | ||
- | * Portul TCP sursă este, în antentul TCP, în formatul [[http://en.wikipedia.org/wiki/Byte_order#Endianness_in_networking|network byte-order]]. | ||
- | * Folosiți [[http://lxr.free-electrons.com/source/include/linux/byteorder/generic.h?v=3.7#L141|ntohs]] pentru conversie. | ||
- | * Parcurgeți secțiunea [[#conversii|Conversii]]. | ||
- | * Pentru testare folosiți ''lin/1-2-netfilter/test-1.sh''. | ||
- | - (**1 punct**) Precizare adresă destinație. | ||
- | * Extindeți modulul de la exercițiul 1 astfel încât să permită precizarea unei adrese destinație dorite prin intermediul unei rutine ioctl ''MY_IOCTL_FILTER_ADDRESS''. | ||
- | * Afișați numai acele pachete care conțin adresa destinație precizată. | ||
- | * Completați zonele marcate cu ''TODO 2''. | ||
- | * Completați funcția ''my_ioctl''. | ||
- | * Revedeți [[:so2:laboratoare:lab04#ioctl|Laboratorul 4]]. | ||
- | * Adresa transmisă din user-space este în [[http://en.wikipedia.org/wiki/Endianness#Endianness_in_networking|network byte-order]]. | ||
- | * Adresa IP estre trimisă prin adresă, nu prin valoare. | ||
- | * Stocați adresa IP în variabila ''ioctl_set_addr''. | ||
- | * Completați funcția ''test_daddr'' | ||
- | * Pentru comparație, folosiți adresele în network byte-order. | ||
- | * Apelați funcția ''test_addr'' în handler-ul netfilter. | ||
- | * Afișați doar pachetele de inițiere de conexiune pentru care adresa destinație este cea transmisă prin rutina ioctl. | ||
- | * Pentru testare folosiți [[http://www.securitydocs.com/library/3376|netcat]] sau scriptul ''lin/1-2-netfilter/test-2.sh''. | ||
- | * Scriptul face toți pașii necesari, inclusiv compilarea utilitarului de testare, inserarea și scoaterea modulului. | ||
- | * (opțional) Pentru setarea adresei din user-space folosiți utilitarul ''lin/1-2-netfilter/test.c''. | ||
- | * (opțional) Îl puteți compila cu comanda: ''make -f Makefile.test''. | ||
- | * După rularea testului, în outputul dmesg trebuie să apară **o singură linie** de forma<code> | ||
- | Connection from: 127.0.0.1:60001 | ||
- | </code> | ||
- | * **Nu** trebuie să apară o linie de forma<code> | ||
- | Connection from: 127.0.0.2:60001 | ||
- | </code> | ||
- | - (**2 puncte**) 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 ''lin/3-4-tcp-sock/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab11-tasks.zip|arhiva de sarcini]] a laboratorului. | ||
- | * Completați zonele marcate cu ''TODO 1''. | ||
- | * Creați socketul de tip server și adăugați-l în starea listening. | ||
- | * Parcurgeți secțiunile [[#operatii_asupra_structurii_socket|Operații asupra structurii socket]] și [[#structura_proto_ops|Structura proto_ops]] din laborator. | ||
- | * Pentru echivalentul ''bind'', ''listen'' în kernel-space, consultați handler-ele de apel de sistem [[http://lxr.free-electrons.com/source/net/socket.c#L1519|sys_bind]] și [[http://lxr.free-electrons.com/source/net/socket.c#L1548|sys_listen]]. | ||
- | * Este vorba de apeluri de forma ''sock->ops->...''. | ||
- | * Eliberați socket-ul în funcția de tip exit a modulului și în zona cu label-uri de eroare. | ||
- | * Pentru testare, rulați scriptul ''lin/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. | ||
- | - (**1 punct**) Acceptarea unei conexiuni în kernel space | ||
- | * 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). | ||
- | * Completați zonele marcate cu ''TODO 2''. | ||
- | * Parcurgeți secțiunile [[#operatii_asupra_structurii_socket|Operații asupra structurii socket]] și [[#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#L1662|sys_accept]] (care este implementat folosind [[http://lxr.free-electrons.com/source/net/socket.c#L1581|sys_accept4]]). | ||
- | * Folosiți ''0'' ca valoarea pentru ultimul argument (''flags''). | ||
- | * 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#L1738|sys_getpeername]]. | ||
- | * 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 ''lin/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, deblocarea modulului și extragerea modulului din kernel. | ||
- | ==== Bonus ==== | ||
- | |||
- | - Socket UDP sender | ||
- | * Creați un modul de kernel care creează un socket UDP și transmite mesajul "kernelsocket" pe socket către adresa loopback pe portul ''60001''. | ||
- | * Porniți de la scheletul ''lin/5-udp-sock/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab11-tasks.zip|arhiva de sarcini]] a laboratorului.. | ||
- | * 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#L1818|sys_send]] sau secțiunea [[#transmitereareceptia_unui_mesaj|Transmiterea/recepția unui mesaj]]. | ||
- | * Pentru transmiterea efectiva folosiți [[http://lxr.free-electrons.com/source/net/socket.c#L666|kernel_sendmsg]]. | ||
- | * Parametrii de transmitere a mesajului sunt preluați din kernel-space. | ||
- | * Pentru deschiderea unui socket UDP pe portul 60001 folosiți [[http://www.securitydocs.com/library/3376|netcat]]; | ||
- | * Citiți secțiunile [[#operatii_asupra_structurii_socket|Operații asupra structurii socket]] și [[#structura_proto_ops|Structura proto_ops]] din laborator. | ||
- | ==== Windows ==== | ||
- | |||
- | * Folosiți directorul ''win/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab11-tasks.zip|arhiva de sarcini]] a laboratorului. | ||
- | * Punctaj total: **5 puncte** | ||
- | |||
- | <note warning> | ||
- | Nu lucrați în directoare care conțin spații (blank). | ||
- | </note> | ||
- | | ||
- | <note tip> | ||
- | * Pe mașina virtuală de Windows recomandăm: | ||
- | * Folosirea mașinii virtuale în modul full screen. | ||
- | * Accesarea tuturor resurselor (browser, LXR) din cadrul mașinii virtuale, fără interacțiune cu sistemul fizic. | ||
- | * Lucrul cu module de kernel în Windows se realizează din consola DDK (''Windows Server 2003 Checked x86 Build Environment''). | ||
- | * Folosiți icon-ul ''x86 Checked Build Environment'' din partea dreaptă a Desktop-ului pentru a deschide consola DDK. | ||
- | * Recomandăm să lucrați în ''C:\Cygwin\home\Administrator\so2'' sau ''C:\Cygwin\home\Administrator\share''. | ||
- | * Pentru a folosi Vim în consolele din Windows, folosiți comanda ''vim-nox''. | ||
- | * În cazul apariției unui //Blue Screen of Death// (BSOD), așteptați repornirea mașinii virtuale și apoi porniți ''WinDbg'' și din acesta dump-ul de memorie ''C:\Windows\MEMORY.DMP''. | ||
- | * Folosiți comanda ''!analyze -v'' pentru depanare. | ||
- | * Parcurgeți indicațiile din [[:so2:laboratoare:lab02#analiza_folosind_memory_dump|laboratorul 2]]. | ||
- | </note> | ||
- | |||
- | - (**3 puncte**) Afișare adreselor de pachet în kernel space | ||
- | * Creați un modul de kernel care afișează adresa și portul sursă pentru pachetele TCP de inițiere de conexiune. | ||
- | * Puteți folosi [[http://msdn.microsoft.com/en-us/library/ff546489.aspx|Filter-Hook Driver]] sau [[http://msdn.microsoft.com/en-us/library/ff546499.aspx|Firewall-Hook Driver]]. | ||
- | * În cazul Filter-Hook Driver, nu uitați să porniți serviciul ''IpFilterDriver'' (''net start IpFilterDriver''). | ||
- | * Folosiți scheletul din ''win/1-2-filter/'', respectiv ''win/1-2-firewall/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab11-tasks.zip|arhiva de sarcini]] a laboratorului. | ||
- | * Parcurgeți secțiunile [[#captura_de_pachete_in_kernel-ul_windows|Captura de pachete în kernel-ul Windows]] și [[#conversii1|Conversii]] din laborator. | ||
- | * Completați funcțiile ''cbFilterFunction'', respectiv ''FilterPacket''. | ||
- | * Cele două funcții vor afișa adresa și portul sursă pentru pachetele de inițiere de conexiune. | ||
- | * Pachetele de inițiere de conexiune sunt cele care au activat flag-ul ''SYN'' în antetul TCP și au dezactivat flag-ul ''ACK''. | ||
- | * **Structurile folosite se găsesc în fișierul** ''win/include/NetHeaders.h''. | ||
- | * Folosiți pointer-ul ''iph'' pentru a referi antetul IP. | ||
- | * Antetul TCP este dat de argumentul ''Packet''. Antetul TCP este reprezentat de tipul de date ''TCPHeader'' descris în ''include/NetHeaders.h''. | ||
- | * Folosiți ''DbgPrint'' pentru afișarea adresei sursă și a portului sursă. | ||
- | * Folosiți macro-urile ''NIPQUAD'' și ''NIPQUAD_FMT'' pentru afișarea adresei IP/ | ||
- | * Folosiți o construcție de forma ''"..." NIPQUAD_FMT "...", NIPQUAD(address)'' unde ''address'' este în [[http://en.wikipedia.org/wiki/Endianness#Endianness_in_networking|network byte-order]]. | ||
- | * ''NIPQUAD_FMT'' este un șir de caractere, nu se pun virgule la concatenarea cu alte siruri de caractere. | ||
- | * Pentru testare folosiți ''nc''. | ||
- | * Pentru crearea unui port (partea de server) folosiți comanda ''nc -l -p 5000''. | ||
- | * Pentru deschiderea unei conexiuni folosiți (partea de client) comanda ''nc localhost 5000''. | ||
- | * În momentul realizării conexiunii modulul vostru va trebui să afișeze adresa sursă și portul aferent. | ||
- | - (**2 puncte**) Afișarea unor anumite adrese de pachet în kernel space | ||
- | * Extindeți modulul de la exercițiul anterior astfel încât să permită precizarea unei adrese destinație dorite prin intermediul unei rutine ioctl ''MY_IOCTL_FILTER_ADDRESS''. | ||
- | * Se afișează numai acele pachete care conțin adresa destinație precizată. | ||
- | * Parcurgeți secțiunile [[#captura_de_pachete_in_kernel-ul_windows|Captura de pachete în kernel-ul Windows]] și [[#conversii1|Conversii]] din laborator. | ||
- | * Completați funcția ''testDestinationAddress'' și apelați-o în cadrul funcțiilor ''cbFilterFunction'', respectiv ''FilterPacket'' pentru testarea adresei destinație a unui pachet. | ||
- | * Completați funcția ''DeviceIoControl'' pentru a permite stocarea adresei destinație de test (în cadrul variabilei ''ioctlSetAddresss''. | ||
- | * Pentru testarea modulului folosiți testul din ''win/test/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab11-tasks.zip|arhiva de sarcini]] a laboratorului. | ||
- | * Fișierul de test permite specificarea adresei care să fie filtrate. Recomandăm specificarea adresei IP aferentă mașinii virtuale. | ||
- | * Adresa transmisă din user-space este în [[http://en.wikipedia.org/wiki/Endianness#Endianness_in_networking|network byte-order]]. | ||
- | * Pentru crearea unui port (partea de server) folosiți comanda ''nc -l -p 5000''. | ||
- | * Pentru deschiderea unei conexiuni care să rezulte în afișarea unui mesaj din cadrul modulului de kernel rulați comanda ''nc $IP_ADDRESS 5000'' (unde ''$IP_ADDRESS'' este adresa IP a mașinii virtuale). | ||
- | * Pentru deschiderea unei conexiuni care să **nu** rezulte în afișarea unui mesaj din cadrul modulului de kernel rulați comanda ''nc localhost 5000''. | ||
- | ==== Soluții ==== | ||
- | |||
- | *[[http://elf.cs.pub.ro/so2/res/laboratoare/lab11-sol.zip | Soluții exerciții laborator 11]] | ||
===== Resurse utile ===== | ===== Resurse utile ===== | ||
Line 1199: | Line 180: | ||
==== Linux ==== | ==== Linux ==== | ||
- | * Understanding Linux Network Internals | + | - Linux Device Drivers 3rd Edition – [[http://lwn.net/images/pdf/LDD3/ch15.pdf|Chapter 15. Memory Mapping and DMA]] |
- | * [[http://www.cs.unh.edu/cnrg/gherrin/|Linux IP networking]] | + | - [[http://www.scs.ch/~frey/linux/memorymap.html|Linux - mapping driver memory in user-space]] |
- | * [[http://www.ecsl.cs.sunysb.edu/elibrary/linux/network/LinuxKernel.pdf|Linux Networking Kernel]] | + | - [[http://www.xml.com/ldd/chapter/book/ch13.html|Linux Device Driver mmap Skeleton]] |
- | * [[http://www.stllinux.org/meeting_notes/2001/0719/myTUX/|The TUX Web Server]] | + | - [[http://www.linuxhq.com/guides/TLK/mm/memory.html|The Linux Kernel - Memory Management]] |
- | * [[http://www.beej.us/guide/bgnet/output/html/multipage/|Beej's Guide to Network Programming Using Internet Sockets]] | + | - [[http://lwn.net/Articles/28746/|Driver porting: supporting mmap()]] |
- | * [[http://www.linuxjournal.com/article/7660|Kernel Korner - Network Programming in the Kernel]] | + | - [[http://www.linuxjournal.com/article/1287|Device Drivers Concluded]] |
- | * [[http://www.phrack.org/issues.html?issue=61&id=13&mode=txt|Hacking the Linux Kernel Network Stack]] | + | - [[http://www.ecst.csuchico.edu/~beej/guide/ipc/mmap.html|Memory Mapped Files]] |
- | * [[http://www.netfilter.org/|The netfilter.org project]] | + | - [[http://en.wikipedia.org/wiki/Mmap|mmap]] |
- | * [[http://www.topsight.net/article.php?story=2003050621055083|Using Netfilter hooks]] | + | |
- | * [[http://www.6test.edu.cn/~lujx/linux_networking/0131777203_ch19lev1sec3.html|The Netfilter Architecture of Linux 2.4]] | + | |
- | * [[http://www.linuxfoundation.org/en/Net:Main_Page|Linux Foundation Networking Page]] | + | |
- | + | ||
- | ==== Windows ==== | + | |
- | + | ||
- | * [[http://msdn2.microsoft.com/en-us/library/aa973518.aspx|Windows and Windows 2000 Networking]] | + | |
- | * [[http://www.codeproject.com/kb/system/driverdev5asp.aspx|Transport Device Interface]] | + | |
- | * [[http://www.codeproject.com/KB/IP/drvfltip.aspx|Developing Firewalls for Windows 2000/XP]] (Filter-Hook Driver example) | + | |
- | * [[http://www.codeproject.com/KB/IP/FwHookDrv.aspx|An Adventure: How to implement a Firewall-Hook Driver?]] | + | |
- | * [[http://download.microsoft.com/download/9/8/f/98f3fe47-dfc3-4e74-92a3-088782200fe7/TWNE05008_WinHEC05.ppt|Windows Filtering Platform and Winsock Kernel]] | + | |
- | * [[http://www.acc.umu.se/~bosse/High%20performance%20kernel%20mode%20web%20server%20for%20Windows.pdf| High performance kernel mode web server for Windows]] | + | |
- | * [[http://www.sockets.com/winsock.htm|Windows Sockets]] | + |