<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <HTML> <HEAD> <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9"> <TITLE>KernelAnalysis-HOWTO: La Rete su Linux</TITLE> <LINK HREF="KernelAnalysis-HOWTO-9.html" REL=next> <LINK HREF="KernelAnalysis-HOWTO-7.html" REL=previous> <LINK HREF="KernelAnalysis-HOWTO.html#toc8" REL=contents> </HEAD> <BODY> <A HREF="KernelAnalysis-HOWTO-9.html">Next</A> <A HREF="KernelAnalysis-HOWTO-7.html">Previous</A> <A HREF="KernelAnalysis-HOWTO.html#toc8">Contents</A> <HR> <H2><A NAME="s8">8. La Rete su Linux</A></H2> <H2><A NAME="ss8.1">8.1 Come viene gestita la Rete su Linux?</A> </H2> <P>Per ogni tipo di NIC vi e' un device driver che lo gestisce (ad esempio per la il device NE2000 compatibile oppure per la periferica 3COM 3C59X, ecc.). <P> <P>Dopo il device a basso livello, Linux chiama SEMPRE la routine di routing ad alto livello "netif_rx [net/core/dev.c]", che controlla: <P> <P> <UL> <LI>A quale protocollo di 3 livello appartiene il pacchetto in questione</LI> <LI>Quale chiamata (tramite puntatori virtuali) eseguire per gestirlo </LI> </UL> <H2><A NAME="ss8.2">8.2 Esempio pratico: TCP</A> </H2> <P>Vedremo un esempio di quello che accade quando mandamo dobbiamo ricevere un pacchetto TCP, partendo dalla ''netif_rx [net/core/dev.c]'' (in sostanza analizziamo ''a grandi linee'' lo stack TCP/IP di Linux). <P> <H3>Gestione Interrupt: "netif_rx"</H3> <P> <PRE> |netif_rx |__skb_queue_tail |qlen++ |* Inserimento tramite puntatori nella coda pacchetti* |cpu_raise_softirq |softirq_active(cpu) |= (1 << NET_RX_SOFTIRQ) // settiamo il bit NET_RX_SOFTIRQ nel vettore BH </PRE> <P> <UL> <LI>__skb_queue_tail [include/linux/skbuff.h]</LI> <LI>cpu_raise_softirq [kernel/softirq.c] </LI> </UL> <H3>Gestione Post Interrupt: "net_rx_action"</H3> <P>Una volta che l'interazione IRQ e' terminata, seguiamo cosa accade in fase di scheduling quando si esegue il BH relativo alla rete che abbiamo attivato tramite NET_RX_SOFTIRQ: in pratica andiamo a chiamare la ''net_rx_action [net/core/dev.c]'' come specificato da "net_dev_init [net/core/dev.c]". <P> <P> <PRE> |net_rx_action |skb = __skb_dequeue (operazione inversa della __skb_queue_tail) |for (ptype = first_protocol; ptype < max_protocol; ptype++) // Determiniamo |if (skb->protocol == ptype) // qual'e' il protocollo di rete |ptype->func -> ip_rcv // come specificato sulla ''struct ip_packet_type [net/ipv4/ip_output.c]'' **** ADESSO SAPPIAMO CHE IL PACCHETTO E' DI TIPO IP **** |ip_rcv |NF_HOOK (ip_rcv_finish) |ip_route_input // accediamo alla tabella di routing per capire qual e' la funzione da chiamare (qual e' cioe' l'interfaccia) |skb->dst->input -> ip_local_deliver // come da controllo della tabella di routing la destinazione e' la macchina locale |ip_defrag // riassembliamo i frammenti IP |NF_HOOK (ip_local_deliver_finish) |ipprot->handler -> tcp_v4_rcv // come da ''tcp_protocol [include/net/protocol.c]'' **** ADESSO SAPPIAMO CHE IL PACCHETTO E' TCP **** |tcp_v4_rcv |sk = __tcp_v4_lookup |tcp_v4_do_rcv |switch(sk->state) *** Il pacchetto puo' essere mandato al Task tramite il socket aperto *** |case TCP_ESTABLISHED: |tcp_rcv_established |__skb_queue_tail // accoda il pacchetto sul socket |sk->data_ready -> sock_def_readable |wake_up_interruptible *** Dobbiamo gestire il 3-way TCP handshake *** |case TCP_LISTEN: |tcp_v4_hnd_req |tcp_v4_search_req |tcp_check_req |syn_recv_sock -> tcp_v4_syn_recv_sock |__tcp_v4_lookup_established |tcp_rcv_state_process *** 3-Way TCP Handshake *** |switch(sk->state) |case TCP_LISTEN: // Riceviamo il SYN |conn_request -> tcp_v4_conn_request |tcp_v4_send_synack // Mandiamo SYN + ACK |tcp_v4_synq_add // settiamo lo stato SYN |case TCP_SYN_SENT: // riceviamo SYN + ACK |tcp_rcv_synsent_state_process tcp_set_state(TCP_ESTABLISHED) |tcp_send_ack |tcp_transmit_skb |queue_xmit -> ip_queue_xmit |ip_queue_xmit2 |skb->dst->output |case TCP_SYN_RECV: // Riceviamo ACK |if (ACK) |tcp_set_state(TCP_ESTABLISHED) </PRE> <P> <UL> <LI>net_rx_action [net/core/dev.c]</LI> <LI>__skb_dequeue [include/linux/skbuff.h]</LI> <LI>ip_rcv [net/ipv4/ip_input.c]</LI> <LI>NF_HOOK -> nf_hook_slow [net/core/netfilter.c]</LI> <LI>ip_rcv_finish [net/ipv4/ip_input.c]</LI> <LI>ip_route_input [net/ipv4/route.c]</LI> <LI>ip_local_deliver [net/ipv4/ip_input.c]</LI> <LI>ip_defrag [net/ipv4/ip_fragment.c]</LI> <LI>ip_local_deliver_finish [net/ipv4/ip_input.c]</LI> <LI>tcp_v4_rcv [net/ipv4/tcp_ipv4.c]</LI> <LI>__tcp_v4_lookup</LI> <LI>tcp_v4_do_rcv</LI> <LI>tcp_rcv_established [net/ipv4/tcp_input.c]</LI> <LI>__skb_queue_tail [include/linux/skbuff.h]</LI> <LI>sock_def_readable [net/core/sock.c]</LI> <LI>wake_up_interruptible [include/linux/sched.h]</LI> <LI>tcp_v4_hnd_req [net/ipv4/tcp_ipv4.c]</LI> <LI>tcp_v4_search_req</LI> <LI>tcp_check_req</LI> <LI>tcp_v4_syn_recv_sock</LI> <LI>__tcp_v4_lookup_established</LI> <LI>tcp_rcv_state_process [net/ipv4/tcp_input.c]</LI> <LI>tcp_v4_conn_request [net/ipv4/tcp_ipv4.c]</LI> <LI>tcp_v4_send_synack</LI> <LI>tcp_v4_synq_add</LI> <LI>tcp_rcv_synsent_state_process [net/ipv4/tcp_input.c]</LI> <LI>tcp_set_state [include/net/tcp.h]</LI> <LI>tcp_send_ack [net/ipv4/tcp_output.c] </LI> </UL> <P>Descrizione: <P> <P> <UL> <LI>Prima determiniamo il tipo di protocollo (IP, poi TCP)</LI> <LI>NF_HOOK (funzione) e' una routine di incapsulazione che prima gestisce il filtro di rete (firewall), eppoi chiama la ''funzione''.</LI> <LI>Dopo gestiamo il 3-way TCP Handshake: </LI> </UL> <P> <PRE> SERVER (LISTENING) CLIENT (CONNECTING) SYN <------------------- SYN + ACK -------------------> ACK <------------------- 3-Way TCP handshake </PRE> <P> <UL> <LI>Alla fine dobbiamo solo piu' lanciare "tcp_rcv_established [net/ipv4/tcp_input.c]" che manda il pacchetto al socket e sveglia il processo in attesa dello stesso </LI> </UL> <HR> <A HREF="KernelAnalysis-HOWTO-9.html">Next</A> <A HREF="KernelAnalysis-HOWTO-7.html">Previous</A> <A HREF="KernelAnalysis-HOWTO.html#toc8">Contents</A> </BODY> </HTML>