• Non ci sono risultati.

La funzione tcp_hc_get

2.3 Le funzioni di gestione della TCP Host Cache

2.3.4 La funzione tcp_hc_get

Funzione di accesso: cerca una entrata nella host cache e ne copia, in una struttura hc_metric_lite, le metriche ad essa associate. Se al chiamante ritorna qualche campo di hc_metric_lite nullo, significa che l’entrata non è stata trovata o che il valore non è settato.

netinet/tcp_hostcache.c void

tcp_hc_get(struct in_conninfo *inc,

struct hc_metrics_lite *hc_metrics_lite)

netinet/tcp_hostcache.c

Figura 2.10: Dichiarazione di tcp_hc_get.

L’implementazione di questa funzione è semplice: richiama tcp_hc_lookup per cer- care la entry voluta e se la trova, la copia nella struttura hc_metrics_lite precedente- mente inizializzata a zero. Contestualmente aggiorna il tempo di vita dell’entrata.

2.3.5

La funzione tcp_hc_getmtu

Funzione di accesso: cerca una entrata nella host cache e ne ritorna il path MTU relativo. Ritorna 0 se la entry non è stata trovata o il valore non è settato.

netinet/tcp_hostcache.c u_long

tcp_hc_getmtu(struct in_conninfo *inc)

netinet/tcp_hostcache.c

Figura 2.11: Dichiarazione di tcp_hc_getmtu.

Anche in questo caso, invoca semplicemente tcp_hc_lookup per trovare l’entrata voluta e ne ritorna il path MTU. Contestualmente aggiorna il tempo di vita dell’entrata.

2.3.6

La funzione tcp_hc_gettao

netinet/tcp_hostcache.c void

tcp_hc_gettao(struct in_conninfo *inc, struct rmxp_tao *tao)

netinet/tcp_hostcache.c

Figura 2.12: Struttura tcp_hc_gettao.

Funzione di accesso: cerca una entry nella host cache e riempie la struttura rmxp_tao passata dal chiamante. Resetta *rmxp_tao se l’entrata non è stata trovata o il valore non è settato. Contestualmente aggiorna il tempo di vita dell’entrata.

2.3.7

La funzione tcp_hc_updatemtu

Funzione di acesso: aggiorna il valore dell’ MTU di una entry nella host cache. Se non trova quella cercata ne crea una.

netinet/tcp_hostcache.c void

tcp_hc_updatemtu(struct in_conninfo *inc, u_long mtu)

netinet/tcp_hostcache.c

Figura 2.13: Dichiarazione di tcp_hc_updatemtu.

Richiama la funzione tcp_hc_lookup per cercare la entry e se non la trova invoca la tcp_hc_insert per inserirla. A questo punto aggiorna il valore del membro rmx_mtu con quello passato dal chiamante e inserisce l’entrata in testa alla lista. Rilascia il blocco che aveva richiesto tcp_hc_lookup. Contestualmente aggiorna il tempo di vita dell’entrata.

2.3.8

La funzione tcp_hc_update

Funzione di acesso: aggiorna le metriche tcp di una entry della tcp host cache. Se non la trova ne crea una nuova.

netinet/tcp_hostcache.c void

tcp_hc_update(struct in_conninfo *inc, struct hc_metrics_lite *hcml)

netinet/tcp_hostcache.c

Figura 2.14: Dichiarazione di tcp_hc_update.

Richiama la tcp_hc_lookup per cercare la entrata voluta e se non la trova ne inserisce una nuova nella cache. A questo punto per tutti i campi della struttura hc_metrics_lite passata dal chiamante, non nulli aggiorna la metrica corrispondente nell’entrata. Tale aggiornamento viene eseguito considerando due casi:

• la metrica nell’entrata è nulla - copia il valore specificato dal chiamante nella

metrica corrispondente;

• la metrica nell’entrata ha già un valore - fa una media tra quel valore e quello

specificato dal chiamante.

2.3.9

La funzione tcp_hc_updatetao

Funzione di accesso: aggiorna un campo della t/tcp tao cache di una entrata nella host cache. Se non trova l’entrata voluta ne crea una nuova.

netinet/tcp_hostcache.c void

tcp_hc_updatetao(struct in_conninfo *inc, int field, tcp_cc ccount, u_short mss)

netinet/tcp_hostcache.c

Figura 2.15: Dichiarazione di tcp_hc_updatetao.

Richiama tcp_hc_lookup per cercare la entrata voluta e, se non la trova, ne inserisce una nuova nella cache. Esamina il parametro field per capire quale sia il campo della tao cache da aggiornare e lo aggiorna. Contestualmente aggiorna il tempo di vita dell’entrata e la inserisce in testa alla lista.

2.3.10

La funzione tcp_hc_list

Funzione utilizzata per il meccanismo di sysctl: stampa la lista e i valori di tutte le entrate della host cache in maniera disordinata.

netinet/tcp_hostcache.c static int

sysctl_tcp_hc_list(SYSCTL_HANDLER_ARGS)

netinet/tcp_hostcache.c

Figura 2.16: Dichiarazione di sysctl_tcp_hc_list.

Pone a 128 caratteri la lunghezza massima di una riga di stampa e alloca un’area di memoria di dimensioni tali da contenere una riga di intestazione più una riga per ciascuna entrata. L’intestazione indica le informazioni che verranno riportate per cia- scuna entrata e cioè: IP address, MTU, SSTRESH, RTT (round trip time), RTTVAR (round trip time variance), BANDWIDTH, CWND (congestion window), SENDPIPE, RECVPIPE, HITS, UPD (updates), EXP (expire time).

Tutto viene memorizzato nel buffer allocato all’inizio, senza stampare niente e solo alla fine il riferimento a tale buffer viene passato alla macro SYSCTL_OUT per la stampa vera e propria.

2.3.11

La funzione tcp_hc_get

Invocata periodicamente, analizza la tcp host cache, ne elimina le entrate scadute oppure tutte quelle esistenti al momento della loro invocazione se è settato il membro purgeall.

netinet/tcp_hostcache.c static void

tcp_hc_purge(void *arg)

netinet/tcp_hostcache.c

Figura 2.17: Dichiarazione di tcp_hc_purge.

Per ogni bucket row scorre la lista delle entrate e ne decrementa il tempo di vita di TCP_HOSTCACHE_PRUNE secondi. Quindi le elimina se il tempo a loro dispo- sizione è scaduto o se il membro purgeall di tcp_hostcache è settato a 1. Infine, schedu- la nuovamente l’invocazione di se stessa dopo un tempo pari a TCP_HOSTCACHE_PRUNE secondi.

Reimplementazione del protocollo

ARP

3.1

Introduzione

Quando un pacchetto viene inviato verso un host, il meccanismo di routing packet determina l’indirizzo IP del next hop (ossia l’indirizzo del nodo successivo a cui sarà necessario inviare il pacchetto per farlo giungere a destinazione), e l’interfaccia sulla quale ci si aspetta di trovare tale next hop.

Nel caso di Ethernet, si rende necessario il meccanismo di risoluzione degli indirizzi per convertire la coppia <tipo di protocollo, indirizzo del next hop> nel corrispondente Ethernet address a 48 bit (detto MAC address). Il modulo di risoluzione degli indirizzi tenta di trovare la suddetta coppia in una tabella detta ARP cache. Se ha successo, restituisce il corrispondente indirizzo a 48 bit al chiamante (l’hardware driver) che, quindi, lo copia nell’header Ethernet e aggiunge il datagramma alla coda di uscita dell’interfaccia appropriata, per inviare il pacchetto. Se invece, l’entrata non viene trovata, informa il chiamante del fallimento, prende in consegna il datagramma e in- via in broadcast una richiesta di ADDRESS_RESOLUTION (ARP request) indicando l’indirizzo che non è stato in grado di mappare.

La richiesta viene presa in considerazione soltanto dal l’host corrispondente al target address espresso nella ARP request, che, alla ricezione del messaggio, risponde al mittente con un pacchetto detto di ARP reply, in cui specifica il proprio MAC address. Contestualmente alla risposta, il target node può anche aggiornare la propria ARP

cache. Questo viene fatto nell’assunzione che la comunicazione sia bidirezionale: se A ha qualche motivo per inviare informazioni a B, allora B, probabilmente, avrà presto bisogno di inviarne ad A.

Alla ricezione dell’ARP reply, il meccanismo di risoluzione degli indirizzi diven- ta capace di completare il mapping. In seguito all’aggiornamento della ARP cache, viene dunque inviato l’eventuale pacchetto precedentemente preso in consegna e tutti i successivi pacchetti che il sistema vorrà inviare in futuro.

È da notare che lo scambio di informazioni circa il mapping IP address - MAC ad- dress, avviene soltanto in caso di effettiva necessità. Un broadcasting periodico, infatti, non è desiderbile. Il protocollo ARP stabilisce specificatamente che i nodi non avver- tano periodicamente della loro presenza per due ragioni: primo, verrebbe generato troppo overhead, sia per quanto riguarda il traffico sulla rete, che per la manutenzione della ARP cache sui nodi; secondo, è altamente improbabile che tutti i nodi avranno bisogno di comunicare con tutti gli altri nodi.

Poichè un nodo non avverte della sua esistenza, tantomeno deve avvertire una sua prossima disconnessione dalla rete. Ciò non rappresenta un problema poichè ARP mantiene dei timers in modo da essere in grado di decidere se una entrata è scaduta oppure no. Un primo timer conterrà il tempo di vita dell’entrata, mentre un altro timer farà eseguire periodicamente il meccanismo di pulizia.

3.2

Implementazione originale

3.2.1

Struttura interna

Nella RELENG4 le funzioni ARP mantengono una lista di strutture llinfo_arp per immagazzinare le informazioni necessarie al mapping tra livello 2 e livello 3. La testa di questa lista è implementata da una struttura definita dalla macro LIST_HEAD:

netinet/if_ether.c static LIST_HEAD(, llinfo_arp) llinfo_arp;

netinet/if_ether.c

Figura 3.1: Testa della ARP cache.

La variabile globale llinfo_arp contiene il puntatore al primo elemento della lista e uno all’ultimo. Gli elementi sono collegati tra di loro tramite una lista doppia e ad essi

si accede utilizzando le macro della famiglia LIST_XXX per aggiungere, scorrere, can- cellare, ecc. . . La struttura di ciascun elemento è dichiarata nel file netinet/if_ether.c.

netinet/if_ether.c struct llinfo_arp {

LIST_ENTRY(llinfo_arp) la_le; struct rtentry *la_rt;

struct mbuf *la_hold; /* last packet until resolved/timeout */

u_short la_preempt; /* #times we QUERIED before entry expiration */ u_short la_asked; /* #times we QUERIED following expiration */ #define la_timer la_rt->rt_rmx.rmx_expire /* deletion time in seconds */ };

netinet/if_ether.c

Figura 3.2: Struttura llinfo_arp.

I membri della struct llinfo_arp sono:

la_le - è essenzialmente un puntatore al succesivo elemento. Viene utilizzato per scorrere la lista.

la_rt - punta alla rtentry corrispondente al destinatario. Rappresenta il legame forte che esisteva nella implementazione originale tra routing table e ARP cache. Ciò è reso necessario dal fatto che non tutte le informazioni che, almeno concettual- mente, dovrebbero essere nella ARP cache, si trovano effettivamente nella strut- tura llinfo_arp. In essa manca infatti l’indirizzo IP da mappare, e il corrispon- dente MAC address. Manca persino il time to expire, cioè il tempo di vita dall’entrata, utile per la pulizia della ARP cache.

hold - punta a un datagramma che deve essere inviato all’indirizzo IP a cui fa rife- rimento l’entrata e che non è ancora stato risolto. In attesa del corrispondente MAC address, il datagramma viene temporaneamente parcheggiato qui e solo quando verrà completato il mapping, sarà spedito correttamente a destinazione. preempt - definisce l’intervallo di tempo che serve per capire se una entrata è prossi-

ma alla scadenza oppure no. Viene utilizzato in fase di address resolution per rinfrescare l’entrata relativa all’IP da risolvere, prima che questa muoia (vedi paragrafo 3.2.9 relativo alla funzione arpresolve).

asked - quando il sistema che ha inviato l’ARP request, non riceve il corrispondente ARP reply, tenta nuovamente inviandone altri a piccoli intervalli di tempo. Que- sto membro contiene il numero di tali ARP request inviati per completare l’ARP resolve. Ovviamente esiste un massimo oltre il quale il meccanismo di address

resolution resta down per un tempo superiore e schedula l’invio del successi- vo ARP request a quando tornerà di nuovo UP (vedi la funzione arpresolve nel paragrafo 3.2.9).

la_timer - è definito come il membro rt_rmx.rmx_expire della rtentry relativa e rap- presenta il tempo di vita dell’entrata ARP. Grazie alla direttiva define il time to live viene utilizzato come un membro della llinfo_arp, anche se, in realtà non lo è. Tale implementazione (come si vedrà in seguito) porta notevoli compli- cazioni perchè prima di accedere a questa informazione, il codice dovrà, ogni volta, verificare che esista la rtentry corrispondente.

3.2.2

Variabili globali

static int arpt_prune = (5*60*1);

Rappresenta il periodo di tempo che intercorre tra una pulizia della cache e l’altra static int arpt_keep = (20*60);

una volta che l’indirizzo è stato risolto, il mapping appena eseguito, viene con- siderato valido per aprt_keep secondi

static int arp_maxtries = 5;

numero di volte che il sistema proverà a inviare un ARP request per ottenere risposta dal target host

static int arpt_down = 20;

tempo (in secondi) durante il quale il sistema considererà down l’entrata reltiva ad un host dal quale non ha ancora ricevuto risposta

static int arp_inuse;

rappresenta il numero di entrate della ARP cache attualmente in uso static int arp_allocated;

numero totale di entrate che sono state allocate dal sistema fin dall’inizio static int arpinit_done;

static int useloopback = 1;

attiva/disattiva l’utilizzo dell’interfaccia di loopback

3.2.3

La funzione arp_init

Inizializza la ARP cache.

netinet/if_ether.c static void

arp_init(void)

netinet/if_ether.c

Figura 3.3: Dichiarazione di arp_init.

Oltre ad utilizzare la macro LIST_INIT per inizializzare la testa della lista di llinfo_arp, registra la funzione arpintr nel database di netisr, richiamando la fun- zione register_netisr. Il meccanismo netisr provvede a fare il demultiplex di un sin- golo interrupt software, usato per lo scheduling del codice network, per richiamare le routine di ciascun protocollo. In questo modo, ogni volta che arriva un pacchetto ARP, netisr intercetta l’unico interrupt software generato per l’arrivo di qualsiasi pacchetto, riconosce che il datagramma è relativo al protocollo ARP e quindi richiama la funzione arpintr.

3.2.4

La funzione arpintr

È la funzione eseguita per gestire i pacchetti arp che arrivano nel sistema. Tramite il sistema di switching netisr (vedi paragrafo 3.2.3 relativo alla funzione arp_init) viene definita una coda dei pacchetti e richiamata questa funzione ogni volta che un nuovo pacchetto entra nel sistema oppure che ci sono dei pacchetti ancora non processati in coda.

netinet/if_ether.c static void

arpintr()

netinet/if_ether.c

Figura 3.4: Dichiarazione di arpintr.

Ogni volta che viene invocata la arpintr esegue un ciclo while sulla coda arpintrq per processare tutti i pacchetti indirizzati al protocollo ARP che sono arrivati fino ad ora. In realtà, questa funzione, si limita a fare dei controlli sul pacchetto da processare per verificarne la validità prima che questo venga effettivamente processato. Testa,

ad esempio, se il formato di indirizzo hardware è tra quelli conosciuti, verifica che la lunghezza effettiva del pacchetto sia consistente con quanto espresso dai valori trovati nell’header del pacchetto stesso, ecc. . . In caso di insuccesso di almeno uno dei test, genera un log di errore, scarta il pacchetto e passa al successivo. Altrimenti, se tutto va bene, richiama la funzione in_arpinput per la gestione vera e propria del pacchetto.

3.2.5

La funzione in_arpinput

netinet/if_ether.c static void in_arpinput(m) struct mbuf *m; netinet/if_ether.c

Figura 3.5: Dichiarazione di in_arpinput.

Dopo un controllo sulla lunghezza del pacchetto, estrae il tipo di arp (request, re- sponse, ecc. . . ), il sender protocol address (spa) e il target protocol address (tpa) del pacchetto.

Viene cercato un indirizzo locale associato alla stessa interfaccia (ifp) da cui è stato ricevuto il pacchetto ARP. Tale indirizzo deve coincidere con uno tra spa e tpa. Per velocizzare la ricerca, vengono usati gli hash buckets di indirizzi associati a ifp. In particolare si ricerca solo nel bucket relativo al sender protocol address e in quello relativo al target protocol address. Se non si ha successo, viene utilizzato il primo indirizzo di famiglia AF_INET associato a ifp. Se neppure questa scelta ha successo (perchè non esistono indirzzi del genere su ifp), il pacchetto viene scartato. Seguono altri controlli:

1. se l’indirizzo hardware del sender è lo stesso di ifp significa che il pacchetto ARP è stato inviato dal sistema stesso e quindi viene scartato.

2. se l’indirizzo hardware del sender coincide con l’indirizzo di broadcast di livel- lo 2 per l’interfaccia ifp, viene generato un log di errore e il pacchetto viene scartato.

3. se il sender protocol address coincide con l’indirizzo di sistema scelto, signifi- ca che qualche altro host sulla stessa rete locale sta usando lo stesso indirizzo. Viene generato un log di errore e si risponde al sender con un arp reply nor-

male, come se il sistema fosse effettivamente il target. In questo caso non viene aggiornata nessuna entrata nella arp table.

4. se ifp ha settato il flag IFF_STATICARP, significa che per quella interfaccia vengono utilizzate solo entrate della arp table statiche e quindi risponde senza modificare nessuna antrata nella arp table.

Se nessuno dei check precedenti viene verificato, richiama arplookup per cercare l’entrata nella arp table relativa al sender del pacchetto. Nel caso in cui il sistema è il target del pacchetto arp, richiede la creazione dell’entrata qualora non ci fosse ancora. Se non è ottenuta nessuna entrata, si passa direttamente alla fase di REPLY, altrimenti la si analizza. In particolare:

• Se il sistema non fa da bridge e l’interfaccia da cui a ricevuto il pacchetto è

diverso rispetto a quella su cui invierebbe la eventuale risposta, genera un log di errore e salta a REPLY

• Se l’indirizzo di livello 2 specificato nell’header del pacchetto ARP è diverso

da quello contenuto nella rtentry relativa all’entrata ARP ottenuta, significa che si dovrà modificare l’informazione memorizzata; questa operazione può essere effettuata solo se la entry non è permanente (in questo caso viene generato un log di notifica), altrimenti genera un log di errore e salta a REPLY.

Prima di effettuare la vera e propria modifica dell’indirizzo hardware, svolge ulte- riori controlli sulla lunghezza di tale indirizzo. In particolare:

• se la lunghezza del vecchio e del nuovo indirizzo sono diverse, genera un log di

WARNING per avvisare l’amministratore di sistema;

• se la lunghezza del nuovo indirizzo è diversa da quella relativa ad un indirizzo

hardware corrispondente all’interfaccia da cui ha ricevuto il pacchetto ARP, allo- ra genera un log di WARNING per notificare che il cambiamento verrà ignorato e salta a REPLY.

La precondizione di ciò che segue è che l’entrata della ARP cache fino ad ora analizzata deve essere aggiornata. In particolare in_arpinput:

2. rinnova il tempo di vita sommando al vecchio arpt_keep secondi 3. resetta il flag RTF_REJECT della rtentry relativa alla antrata ARP 4. resetta i membri la_preempt e la_asked

5. se era stato congelato un pacchetto indirizzato verso l’host relativo a questa antrata, lo invia richiamando la if_output associata al’interfaccia

REPLY:

Se il pacchetto non è relativo ad un ARP request viene scartato. Se il sistema è il target dell’arp request, copia nel sender protocol address e nel sender hardware ad- dress dell’arp reply, rispettivamente l’indirizzo IP e il MAC address relativi a ifp. Al- trimenti esegue una ricerca nella arp table dell’entrata relativa al target con comando LLE_PROXY e se la trova aggiorna l’indirizzo di livello 2. Se l’entrata cercata non esi- ste nell’arp table si suppone che non sia relativa ad un host locale ma che corrisponda ad un’altra rete fisica. In questo caso, se il sistema non funziona da proxy arp, non deve fare niente e scarta il pacchetto. Altrimenti richiama rtalloc1 per vedere se il target è effettivamente attivo su un’altra interfaccia, nel qual caso invia un arp reply al sender con l’indirizzo hardware relativo a ifp, altrimenti scarta il pacchetto.

3.2.6

La funzione arptimer

netinet/if_ether.c static void arptimer(ignored_arg) void *ignored_arg; netinet/if_ether.c

Figura 3.6: Dichiarazione di arptimer.

Questa funzione viene richiamata periodicamente, ogni arpt_prune secondi, da un timer impostato la prima volta che viene invocata la arp_rtrequest, e aggiornato auto- maticamente da arptimer stessa. Esso serve a ripulire la ARP table: utilizza la variabile globale llinfo_arp per ottenere la testa della lista e la scorre alla ricerca di entrate che verificano le seguenti condizioni:

1. non sono permanenti (expire!=0);

Per ognuna di tali entrate richiama la funzione arptfree per invalidarle ed eventual- mente eliminarle (vedi paragrafo 3.2.7).

3.2.7

La funzione arptfree

Documenti correlati