Peer-to-Peer Un approccio pratico Topologie, protocolli, sicurezza Orlando Selenu Partiamo dalle origini “Il problema fondamentale della comunicazione è quello di riprodurre in un punto, esattamente o meno, un messaggio proveniente da un altro punto.“ Claude Elwood Shannon Un problema vecchio quanto l’informatica Turing, Shannon, e Dijkstra, i padri dell’informatica, hanno sempre cercato nuove risposte al bisogno crescente di comunicazione. Abbiamo la maggior parte degli strumenti informatici che ci servono per realizzare una rete paritaria sin dagli anni ’70, ma solo negli ultimi anni si è registrata un’effettiva e diffusa tendenza all’aggregazione delle risorse (Napster ha avuto un picco di traffico di 7 TB in un giorno). Tendenza classica Storicamente si registra una tendenza all'aggregazione delle potenze di calcolo I motivi sono da ricercarsi essenzialmente nella riduzione dei costi E’ passata l’era dei mainframe, o del glorioso CEP E poi? Cosa sarà oltre Internet? (interrogativo sempre aperto) Un approccio pratico al problema Perché? un approccio pratico per una mia personale visione dell'informatica un'analisi ergonomica dell'informatica Ovvero: Quale impatto nella società può avere una scoperta/invenzione informatica? La moglie di Nobel Quanto approfondire il discorso La resistenza di una catena è data dall'anello più debole Pertanto: E’ necessario capire tutti i passaggi di un applicazione o tecnologia (P2P in questo caso) per comprendere i punti deboli del sistema Un po’ di terminologia Peer (o Nodo) = qualcuno o qualcosa che è allo stesso livello Da qui: Rete paritaria = rete formata da calcolatori gerarchicamente uguali Lo strato fisico Per una trasmissione da un nodo a un altro nodo ho bisogno di: un'infrastruttura sottostante (toh! Internet!) [metà anni '60, quindi precedente a Metcalfe] una lingua comune, un protocollo, per scambiare informazioni [sempre dalla fine degli anni '60 in poi i primi sviluppi] Le tipologie di P2P (1/2) Prima ancora di interrogarsi sui dettagli, è intuitivo pensare che esistano due grandi macroaree: Distribuzione di potenza di calcolo (cpu) Distribuzione di spazio (dati) (esisterebbe una terza via, il caching, ma la comprendo nella distribuzione di dati) Le tipologie di P2P (2/2) A dirla tutta esiste anche un’altra distinzione tra le reti paritarie: Reti strutturate e non strutturate (reti che hanno un server centrale, o meno) Un primo problema, comune ad ogni rete: Indicizzazione, hashing (impronta digitale del file) Algoritmi usati solitamente [md5, sha-1, merkle’s tree] Comprimere una quantità di dati n in uno spazio arbitrario n‘ (limiti ragionevoli dell’hashing) Il P2P più semplice possibile Due nodi Ogni nodo è alternativamente client e server Almeno uno dei due deve ottenere dei dati dall’altro nodo [interrogazione, ack, replica] Nessuna policy di saturazione di banda (flooding) [“vince il più forte”] Per fortuna non è tutto qui Pertanto: Il primo problema nell’utilizzo di un sistema peer-to-peer è localizzare in modo efficiente il nodo che mantiene l’informazione o la risorsa che si sta cercando. Il secondo problema è bilanciare tutto il carico. Bene.. E adesso? E adesso viene il bello! Le 4 fasi di una rete P2P Boot (è un limite) Lookup (vero nodo cruciale) Join (dipendente da anonimità e sicurezza) Leave (non sempre presente) Boot Si tratta di ottenere un elenco, perlomeno parziale, degli altri nodi della rete Non è detto che esista, ad esempio si potrebbe fare un ip scanning, sperando di trovare un altro nodo, con lo stesso protocollo. (un tentativo molto poco ragionevole) Praticamente tutti i protocolli hanno la fase di boot implementata come un elenco degli ultimi nodi/server attivi Lookup Si tratta di cercare il gestore di un dato sulla rete Lookup centralizzato (Napster) Flooded queries (Gnutella) Architettura ibrida: più server centrali risolvono le ricerche Peer to Peer puro non strutturato: Multicast della richiesta: i vicini inviano ricorsivamente la richiesta ai loro vicini. Routed Queries Architettura pura strutturata: Ogni peer attua politiche di routing per instradare la richiesta Join Per ora non ce ne occupiamo, ogni rete è estremamente singolare Leave Non è banale, perché è bene che i protocolli siano tolleranti verso i nodi che si scollegano inaspettatamente Le buone topologie, infatti, devono bilanciarsi automaticamente Richieste di una buona rete (1) Il lavoro richiesto a un determinato nodo nel sistema non deve crescere troppo in funzione del numero di nodi nel sistema La scalabilità di un protocollo dipende: dalla topologia della rete dall’algoritmo di routing Obiettivi: Minimizzare il numero di messaggi necessari per fare lookup (minimizzare il flooding) Minimizzare, per ogni nodo, le informazioni relative agli altri nodi (minimizzare la tabella di routing) Richieste di una buona rete (2) “Conoscere i luoghi, vicini o lontani non vale la pena, non è che teoria; saper dove meglio si spini la birra è pratica vera, è geografia” Goethe P2P di prima generazione (1/2) Reti strutturate: ed2k, Fasttrack, Gnutella e Gnutella2 (“piccola” diatriba sul nome con la GNU project) trovare tutti i nodi che hanno un dato trovarne almeno uno nel minor tempo possibile P2P di prima generazione (2/2) Sistemi proprietari o meno (distinzione e primo approccio alla sicurezza) Topologia tipica: alcuni server centrali, molti client Problemi tipici: http://katapekkia.altervista.org/?q=aumentano_i_ server_spia_sulla_rete_ed2k.htm Rapida comparativa DirectConnect (DC++) Usa una serie di hub (server) che mantengono le informazioni relative a un gruppo di utenti Una volta connessi ad un hub si condivide file solo con i nodi connessi a tale hub E’ possibile connettersi a più hub nello stesso tempo Gli hub sono connessi tra loro ma non si scambiano informazioni relative alla ricerca di una determinata chiave (tanti piccoli Napster) La lista degli hub attivi viene mantenuta da tutti gli hub e aggiornata periodicamente dagli altri hub mediante messaggi del tipo “I am here” WinMX Basato su una rete di server (circa 50) chiamata OpenNap nata subito dopo che è stato chiuso il server di Napster Viene usata anche da NapMx In WinMx viene fatta una distinzione fra nodi di connessione primaria: direttamente connessi ai server sono usati anche per il Routing e nodi di connessione secondaria: connessi solo ai nodi di connessione primaria non si occupano di Routing KaZaA Viene usata una rete proprietaria In KaZaA viene fatta una distinzione fra nodo e Supernodo(server): Ogni nodo semplice collabora con il proprio Supernodo I Supernodi collaborano tra loro e con i propri sottonodi Come sono connessi i Supernodi? Gnutella La ricerca usa il flooding Il messaggio di ricerca viene inoltrato a tutti i nodi della rete Gnutella, ogni volta La ricerca risulta inefficiente e non scalabile Il peso dei messaggi è rilevante rispetto al traffico Gnutella2 (1/2) Struttura simile a KaZaA In Gnutella2 il numero massimo di nodi affidati a un hub è basso(150) Il costo di comunicazione fra nodi e hub è basso (ed è giusto che sia così) Il problema principale è rappresentato dalla comunicazione fra gli hub: Gli hub sono raggruppati in cluster Quando un nodo richiede la ricerca di un proprio elemento al proprio hub: Viene ricercato l’elemento nel proprio cluster Viene ricercato l’elemento nei cluster vicini(a distanza 1) più qualche cluster lontano scelto a caso Viene ricercato l’elemento nei cluster vicini dei vicini (a distanza 2) più qualche cluster lontano scelto a caso Gnutella2 (2/2) La lista degli hub a distanza i+1 è ottenuta in risposta alla ricerca effettuata sugli hub a distanza i Ogni hub mantiene la lista degli hub vicini più una cache di hub lontani aggiornata di tanto in tanto Viene mantenuta una done list Le ricerche all’interno del cluster sono fatte usando TCP Le ricerche fra cluster sono fatte usando UDP Gli strumenti software Sun, con grande lungimiranza, ha realizzato una piattaforma open source JXTA (sta per juxtapose) per creare in modo facile applicazioni P2P JXTA non è una libreria di codice; piuttosto, è un insieme di protocolli che può essere implementato in ogni linguaggio e su ogni rete per costruire applicazioni P2P JXTA fornisce tutte le funzionalità di base richieste in una applicazione P2P fra queste: peer discovery; peer communication; JXTA è 100% OPEN SOURCE (http://www.jxta.org) Ancora su JXTA, InstantP2P InstantP2P è un esempio opensource di applicazione JXTA. E’ progettato per fornire un mezzo semplice per capire come un’applicazione può usare la tecnologia JXTA. Implementa alcune applicazioni classiche: peer-to-peer messaging (crittografato e in chiaro) peer group chat condivisione file Assistiamo ad un gap generazionale Reti non strutturate: Kademlia (varie implementazioni) Chord Tapestry Gnunet BitTorrent Quasi tutte non sono proprietarie! Topologia tipica: ogni client può anche fungere da server centrale Problemi tipici: lentezza e scarsa esaustività della ricerca Distributed Hash Tables (1/2) Le tabelle di hash distribuite (DHT) sono una classe di metodi distribuiti decentrallizzati che partizionano la proprietà di un insieme di chiavi tra i nodi partecipanti, e possono efficentemente indirizzare i messaggi all’unico proprietario di una data chiave. Le DHT sono pensate per scalare su grandi numeri di nodi e per gestire un continuo va e vieni di nodi (eventuali problemi potrebbero mandare in crash una macchina). Primo sviluppo: MIT Distributed Hash Tables (2/4) A ogni file e ad ogni nodo è associata una chiave La chiave viene di solito creata facendo l’hash (tipicamente 128 o 160 bit) del nome del file o dell’IP del nodo Ogni nodo del sistema è responsabile di un insieme di file/chiavi, e tutti insieme realizzano una DHT L’unica operazione che un sistema DHT deve fornire è lookup(key), la quale restituisce l’identità del responsabile di una determinata chiave Distributed Hash Tables (3/4) Tutti i nodi del sistema condividono una tabella hash Conoscono la struttura della tabella Ma non conoscono il responsabile di una determinata entry Distributed Hash Tables (4/4) Se una chiave è richiesta più spesso, il responsabile della chiave e anche i suoi vicini potrebbero sovraccaricarsi Per ovviare al problema si possono usare meccanismi di caching e di duplicazione Diverso è il problema relativo ai nodi che si sovraccaricano per il traffico generato dalle lookup Questo tipo di traffico è abbastanza difficile da individuare e da gestire P2P di seconda generazione (1/3) Problema: i protocolli usati da Napster e Gnutella non sono scalabili Per migliorare la scalabilità sono nati i cosiddetti protocolli P2P di seconda generazione che utilizzano le DHT (Distributed Hash Table) Alcuni esempi di questi protocolli sono: Tapestry, Pastry, Chord, Can, Viceroy, Butterfly P2P di seconda generazione (2/3) A ogni file e ad ogni nodo è associata una chiave; La chiave viene di solito creata facendo l’hash del nome del file; Ogni nodo del sistema è responsabile di un insieme di file(o chiavi) e tutti realizzano una DHT; L’unica operazione che un sistema DHT deve fornire è lookup(key), la quale restituisce l’identità del responsabile di una determinata chiave. P2P di seconda generazione (3/3) La scalabilità di un protocollo è direttamente legata all’efficienza dell’algoritmo usato per il routing I vari DHT conosciuti differiscono proprio nel routing Ricordo che per ora stiamo parlando di protocolli pensati per reti fisiche wired Kademlia (1/2) Ogni nodo è rappresentato da un identificatore di 160 bit Dati due identificatori x e y la distanza fra x e y (d(x,y)) è definita come x ⊕(xor) y XOR è una metrica: d(x,x)=0 d(x,y)>0 se x≠y d(x,y)=d(y,x) (simmetrico) d(x,y)+d(y,z)≥d(x,z) fissato x e una distanza d esiste un solo y tale che d(x,y)=d Per ogni 0 ≤ i ≤ 160, ogni nodo mantiene una lista di k (costante) nodi (detta k-bucket) a distanza compresa fra 2i e 2i+1 da se stesso Kademlia (2/2) I k-bucket vengono aggiornati e ordinati ad ogni operazione con una politica detta least-recently seen node Ovviamente a ogni passo l’algoritmo di routing dimezza la distanza fra il nodo che fa la richiesta e la destinazione (O(log n) passi) La dimensione delle tabelle di routing è klog n Poiché è simmetrico il sistema si stabilizza da solo. In pratica durante le lookup le tabelle di routing vengono aggiornate Tapestry Realizzazione dinamica dell’algoritmo di Plaxton (che non si adattava a sistemi dinamici) Supponendo che le chiave sia costituita da un intero positivo l’algoritmo di routing corregge a ogni passo un singolo digit alla volta Per fare ciò un nodo deve avere informazioni sui nodi responsabili dei prefissi della sua chiave; (O(log N) nodi) Il numero di messaggi necessari per fare lookup è O(log N) L’algoritmo in pratica simula un Ipercubo Chord Le chiavi sono mappate su un array circolare (ring, o anello) Il nodo responsabile di una determinata chiave è il primo nodo che la succede in senso orario Ogni nodo x di Chord mantiene due insiemi di vicini: I log N successori del nodo x più il predecessore. Questo insieme viene usato per dimostrare la correttezza del Routing Un insieme log N nodi distanziati esponenzialmente dal nodo x, vale a dire l’insieme dei nodi che si trovano a distanza 2i da x per i che va da 0 a log N – 1. Questo insieme viene usato per dimostrare l’efficienza del Routing Le informazioni che il nodo deve mantenere sugli altri nodi sono log N + log N + 1 = O(log N) Il numero di messaggi necessari per fare lookup è O(log N) Il costo che si paga quando un nodo lascia o si connette alla rete è di O(log2N) messaggi L’algoritmo in pratica simula un Ipercubo, inoltre si comporta molto bene in un sistema dinamico Svantaggi: una sola dimensione una sola strada CAN I nodi sono mappati su un toro a d dimensioni A ogni nodo è associato un sottoinsieme di questo spazio d-dimensionale Ogni nodo mantiene la lista dei nodi responsabili dei sottospazi che confinano con il proprio sottospazio Ogni nodo ha O(d) vicini (due per ogni dimensione) Sicurezza nelle DHT E’ possibile realizzare un protocollo P2P che resista ad attacchi di tipo denial of service E’ necessario replicare i dati E’ importante usare funzioni hash “One Way” (per i dati e per i nodi) E’ importante osservare che tutte le dimostrazioni relative alla sicurezza dei vari algoritmi incontrati finora si basano sul fatto che le chiavi vengono associate ai files in modo casuale (quasi tutti gli algoritmi usano SHA) P2P di terza generazione Freenet Mute Netsukuku (progetto italiano!) ANtsP2P (progetto italiano!) Devono offrire piena anonimità, e alcune accortezze per resistere agli attacchi di denial of service (l’ipotesi dei generali bizantini, ad esempio) Freenet Ogni nodo mette a disposizione un po’ di spazio Le operazioni possibili sono get e put di un file Per aggiugere un nuovo file si invia un send message nella rete e un identificatore GUID (Global Unique Identifier) in base al quale il file viene memorizzato in un insieme di nodi (Data Partition) Per recuperare un file basta inviare un messaggio di richiesta contenente il GUID del file Servizi aggiuntivi: Persistenza Anonimato Purtroppo è tutt’altro che efficiente… Idee per reti atipiche Small world network (6 gradi di separazione) Bloom filters (una tipica funzione hash a senso unico) Bloom Filters Sono una soluzione probabilistica al problema dell’appartenenza da parte di un membro “Ovunque sia usata una lista o un insieme, e lo spazio sia una variabile da tenere in considerazione, un filtro di Bloom dovrebb’essere considerato anch’esso. Quando si usa un filtro simile è bene mettere in conto i falsi positivi” B. Bloom [1970] Come testare una rete P2P ns2, molto avanzato p2psim, estremamente specializzato, anche se in fase di beta Simulatore di reti: p2psim (1/3) p2psim fa parte del progetto IRIS (MIT): Il progetto IRIS si propone come fine lo sviluppo di una nuova infrastruttura decentralizzata, basata su DHT Il progetto IRIS comprende tra l’altro: Chord CFS (Cooperativa File System): è un sistema di memorizzazione basato su Chord permette la pubblicazione dei propri file e la possibilità di accedere ai file pubblicati da altri (solo lettura) permette di distribuire il carico fra i nodi permette di creare repliche dei dati, onde evitare la perdita degli stessi a seguito di fallimento dei nodi del sistema Simulatore di reti: p2psim (2/3) L’applicazione prende in input 3 file di testo, per rappresentare rispettivamente: La topologia della rete Il protocollo da utilizzare Gli eventi da generare C’è di che sbizzarrirsi, ma non esiste uno standard per la visualizzazione dei risultati di una simulazione In realtà i risultati vengono generati dal distruttore della classe che rappresenta il protocollo testato Nella versione attuale di p2psim ogni protocollo si basa su parametri diversi, e questo complica la comparazione dei protocolli Simulatore di reti: p2psim (3/3) Simulare un protocollo con p2psim è abbastanza semplice perchè i file sillyprotocol.h e silliprotocol.C contengono una bozza di protocollo non implementato E’ sufficiente implementare le operazioni di join e lookup E’ possibile farsi un’idea del codice da scrivere analizzando il codice sviluppato per gli altri protocolli Infine bisogna editare i 3 file di testo (protocollo, topologia, generatore di eventi) da passare all’applicazione E’ possibile inoltre creare il proprio generatore di eventi utilizzando tra l’altro SillyEventGenerator Prove di concetto Tutto è iniziato da: tinyp2p, 15 righe di Python Seguito da: molester, 6 righe di Perl E da: uP2P, 6 righe di bash script uP2P, il codice Altri usi del P2P Ambienti virtuali distribuiti: Grid Network e distribuzione della potenza di calcolo Update di un file, replicazione sugli slave, e consistenza Concludiamo in bellezza “Possiamo vedere poco di ciò che c’è all’orizzonte, ma ciò che vediamo ci fa capire che c’è molto da fare.“ Alan Turing