{% extends "_layout_de.html" %} {% block title %}To Do Liste{% endblock %} {% block content %}
Die Webseite wird gerade überholt und dieses Dokument kann alte Informationen enthalten
Hier ist eine detaillietere (und dennoch unvollständige) Diskussion der wichigsten Gebiete zur zukünftigen Entwicklung des grundlegenden I2P Netzwerkes, übergreifend über die zukünftigen geplanten Versionen. Dieses beinhaltet nicht steganographische Transporte, anpassen auf kabellose Geräte oder Werkzeuge zum absichern der eigenen Computer gegen Angriffe. Ebenfalls sind die Klientenanwendungen nicht dabei, die unverzichtbar für den Erfolg von I2P sind. Es werden mehr Themen folgen, besonders alsbald I2P mehr Überprüfungen bekommt, aber hier sind zumindest die "grossen Themen" aufgelistet. Schaue ebenfalls in den Zeitplan. Möchtest du helfen? Beteilige Dich!
Um Knoten hinter nicht kontrollierten NATs oder Firewalls dennoch mit voller Funktionalität am Netzwerk teilnehmen zu lassen brauchen wir ein wenig der Funktionalität der Beachränkten Routen (da diese Knoten keine eigehenden Verbindungen empfangen können). Um dieses erfolgreich zu machen, teilt man die Knoten in 2 Gruppen auf:
Hierfür verbinden sich die nicht erreichbaren Knoten einfach mit ein paar anderen Knoten, bauen einen Tunnel durch sie auf und veröffentlichen eine Referenz auf diese Tunnel in Ihrer RouterInfo Struktur in der Netzwerk Datenbank.
Falls jemand einen bestimmten Router erreichen will, muss dieser erst dessen Routerinfo aus der Netzwerk Datenbank bekommen. Diese teilt ihm mit, ob der Router direkt (z.B. hat der Knoten ein öffentlich erreichbares Interface) erreichbar ist oder indirekt erreicht werden muss. Direkte Verbindungen passieren wie sonst normal auch, während indirekte durch einen der publizierten Tunnel aufgebaut werden müssen.
Falls ein Router nur ein oder zwei Nachrichten an einen bestimmten versteckten Knoten senden will, kann dieser einfach diese indirekten Tunnel zum Senden der Nutzdaten benutzen. Falls jedoch der Router öfters zu dem versteckten Knoten sprechen möchte (z.B. als Teil eines Tunnels), sendet er eine garlic geroutete Nachricht durch den indirekten Tunnel zu dem verstecktem Knoten, welcher den Inhalt, der eine Nachricht zum zurückschicken an den Ausgangsrouter enthält, auspackt. Dieser verteckte Knoten baut dann eine ausgehende Verbindung zu dem Ausgangsrouter auf und über diese direkte Verbindung können die beiden Router dann miteinander kommunizieren.
Natürlich funktioniert dieses nur, wenn der Ausgnagsrouter Verbindungen empfangen kann (nicht auch versteckt ist). Falls das der Fall ist, kann er einfach die garlic geroutete Nachricht über den Eingangstunnel des Ausgangsrouter zurückrouten..
Hierbei geht es nicht darum, die IP eines Knotens zu verstecken, es ist eher eine Möglichkeit um Nutzern hinter NATs und Firewalls ganz am Netzwerk teilnehmen zu lassen. IPs zu verstecken braucht etwas mehr Arbeit, wie weiter unten beschrieben.
Mit dieser Technik kann jeder Router in einem Tunnel jede Aufgabe annehmen. Aus Effizienzgründen ist ein versteckter Knoten eine schlechte Wahl als Eingangsgateway. Innerhalb eines Tunnels sollten zwei benachbarte Knoten nicht versteckt sein, dennoch wird diese Regel technisch nicht benötigt.
Standard TCP Kommunikation in Java braucht normalerweise blockierende Socket Aufrufe. Damit diese Aufrufe nicht das gesamte System blockieren, sind sie in eigenen Threads implementiert. Unser alter TCP Transport war recht einfach implementiert - für jeden Knoten mit dem wir kommunizierten hatten wir einen lesenden und einen schreibenden Thread. Der lesende Thread ruft einfach in einer Schleife eine Reihe vo read()-Aufrufen auf, baut I2NP Nachrichten auf und fügt sie einer internen Warteschlange für eingehende Nachrichten hinzu. Der schreibende Thread holt die Nachrichten aus der Ausgangs Nachrichten Queue (je Verbindung) und schiebt diese durch die write()-Aufrufe.
Wir machen das aus CPU-Sicht recht effizient - die meiste Zeit warten fast alle Threads untätig. Jedoch braucht jeder Thread auch reale Ressourcen (auf älteren Linux Kernels war oft z.B. jeder Thread als ein abgeleiter Prozess implementiert). Sobald das Netzwerk wächst, wächst auch die Anzahl Knoten, mit der ein jeder Router kommuizieren möchte (man bedenke, dass I2P voll verbunden ist, was bedeuted, das jeder Knoten wissen sollte, wie er eine Nachricht zu einem anderen Knoten bekommt. Eine Einschränung der Routen sollten nicht wirklich die Anzahl der benötigten Verbindungen reduzieren). Somit wird in einem Netzwerk mit 100.000 Routern jeder einzelne bis zu 199.998 Threads NUR zum Abarbeiten der TCP Verbindungen haben!
Offensichtlich wird das nicht funktionieren. Wir brauchen eine skalierbare Transportschicht. In Java haben wir da 2 grosse Möglichkeiten:
Das Senden und Empfangen von UDP Datagrammen geschieht ohne stehende Verbindung. Auch bei 100.000 anderen Routern benötigt UDP nur einen einzelnen Thread, der die ausgehenden Pakete aus einem Stack ausliest und versendet (und um zu Empfangen, gibt es einen Thread, der alle eingehenden UDP Pakete einsammelt und diese an die Eingehende Queue weiterleitet).
UDP zu nutzen bedeuted aber auch, die Vorteile von TCP zu verlieren, wie z.B. die Reihenfolge der Pakete, Auslastungskontrolle, MTU Erkennung, usw. Dieses alles zu implementieren wird viel Arbeit sein, aber I2P braucht nicht alle Featuers von TCP. Insbesondere habe ich beim Messen im Simulator und im Livenetz festgestellt, dass der Grossteil der transportierten Nachrichten problemlos in ein einzelnes unfragmentierten UDP Paket passt, wogegen die grössten in 20-30 Pakete passen. Wie mule angemerkt hat, fügt TCP einen erheblichen Overhead bei den vielen kleinen Paketen hinzu, da für jedes Paket ein ACK verschickt werden muss. Mit UDP können wir den Transport sowohl auf Effizienz als auch auf Belastbarkeit optimieren in Hinblick auf die speziellen Bedürfnisse von I2P.
Es wird eine Menge an Arbeit sein.
In Java 1.4 wurde ein ganzes Paket von sogenannten "New I/O" eingebaut, die den Entwicklern erlaubt, die Vorteile der nicht-blockierenden IO-Fähigkeiten des Systems zu Nutzen - dadurch kann eine grosse Menge von gleichzeitigen IO-Operationen verwaltet werden, ohne die Notwendigkeit eines seperaten Threads für jede einzelne IO-Operation. Dieser Ansatz ist vielversprechend, da wir auf diese Weise viele Verbindungen gleichzeitig verarbeiten können und keinen selbstgeschriebenen Mini-TCP Stack in UDP benötigen. Dennoch sind die NIO Pakete nicht ganz fehlerfrei, wie die Freenetentwickler feststellen mussten. Hinzu kommt, das wir mit NIO-Support nicht die Open Source JVMs wie z.B. Kaffe unterstützen, da GNU/Classpath NIO nur im geringen Umfang unterstützt. (Hinweis: das ist womöglich schon/bald der Fall, da es Verbesserungen bei den Classpaths NIO gegeben hat, aber es ist unbekannt, wie weit diese verbessert sind.)
Eine weitere Alternative in diese Richtung ist das Non Blocking I/O Paket - eine grundsätzliche saubere NIO-Implementierung (geschrieben bevor NIO da war). Es verwendet einige BEtriebssystem-Routinen des nichtblockierenden IO und reicht Events an Java weiter. Es scheint mit Kaffee zu funktionieren, obwohl es letztens nicht viel Entwicklung in dem Projekt gab (wohl durch die Java 1.4 NIO Entwicklung bedingt).
In der derzeitigen Netzwerkdatenbank und Profilemanagement Implementation haben wir uns die Freiheit genommen, ein paar praktische Vereinfachungen zu nutzen. Zum Beispiel haben wir keinen Code um die Knoten Referenzen aus der K-Gruppe zu entfernen, falls wir nicht genug Knoten haben um nur eine Gruppe hinreichend zu füllen, stattdessen behalten wir den Peer in der jeweiligen passenden Gruppe. Ein weiteres Beispiel dreht sich um die Knoten Profile - der benötigte Speicherbedarf für jedes Knoten Profile ist klein genug damit wir tausende voll ausgefüllte Profile ohne Probleme im Speicher halten können. Während wir die Möglichkeit zum Nutzen von gekürzten Profilen (von denen wir 100 Tausende im Speicher halten können) haben, haben wir keinen Code um ein gekürztes Profil in ein volles Profil zu wandeln, anders herum oder gar ganz zu entfernen. Es ist einfach nicht praktikabel genug, den Code jetzt zu schreiben, da wir diesen noch eine lange Zeit nicht brauchen werden.
Somit sollten wir dieses im Hinterkopf behalten, sobald das Netzwerk weiter wächst haben wir ein wenig Arbeit damit, aber für jetzt lassen wir das für später.
Zur Zeit wissen, wenn Alice einen 4-Hop Eingangstunnel aufbaut, der bei Elvis beginnt und über Dave,Charlie und Bob zu Alice geht (A<--B<--C<--D<--E), alle 5 beteiligten Knoten die Tunnel ID "123", da die Nachrichten damit bezeichnet sind. Wir möchten jedem Hop seine eigene Tunnel-Hop ID geben - Charlie bekommt Nachrichten über Tunnel 234 and reicht diese zum Tunnel 876 an Bob weiter. Beabsichtigt wird damit, dass Bob oder Charlie nicht wissen, dass sie im Tunnel von Alice teilnehmen. Unterschiedliche Tunnel IDs erschweren "Collusion-Attacken".
Eine einzigartige TunnelID pro Hop hinzuzufügen ist nicht schwer, aber für sich alleine unzureichend. Falls Dave und Bob unter der Kontrolle des selben Angreifers stehen, könnten sie nicht anhand der Tunnel ID sagen, das sie am selben Tunnel teilnehmen, jedoch können sie es anhand der Nachrichtentexte und Verifikationsstrukturen durch einen simplen Vergleich herausfinden. Um das zu verhindern muss ein Tunnel eine schichtweise Verschlüsselung auf dem Wege nutzen, sowohl auf den Inhalt der getunnelten Nachricht als auch auf der Verifikationsstruktur (die zum verhindern einfacher Attacken genutzt wird). Die TunnelMessage Struktur muss hierfür geriongfügig geändert werden, als auch die Einbindung von pro-Hop geheimen Schlüsseln, die beim Tunnelaufbau transportiert und zum Tunnel Gateway durchgereicht werden. Wir müssen eine maximale Tunnellänge setzen (z.B. 16 Hops) und dem Gateway sagen, das dieser die Nachricht mit jedem der 16 empfangenen geheimen Schlüsseln verschlüsseln muss, in umgekehrter Reihenfolge, und auch die Signatur der Prüfsumme der (verschlüsselten) Daten bei jedem Schritt verschlüsseln muss. Das Gateway senden dann diese 16-fach verschlüsselte Nachricht mitsamt den 16-fachem, 16 Felder weiten, verschlüsselten Mapping an den ersten Hop. Dieser entschlüsselt die Nachricht und die Nutzdaten mit seinem geheimen Schlüssel, schaut in der 16 Felder weiten Mapping nach dem Eintrag, der zu seinem eigenem Hop passt (mit der Pro- Hop Tunnel ID gekennzeichnet) und prüft die Nutzdaten gegen die assoziierte signierte Prüfsumme.
Das Tunnel Gateway hat immer noch mehr Informationen als die anderen Knoten im Tunnel und die Übernahme des Gateways und eines Tunnel Teilnehmers erlaubt Angreifern, diesen Knoten zu vergleichen und festzustellen, dass beide im selben Tunnel sind. Zusätzlich wissen benachbarte Knoten sowieso, das sie im selben Tunnel sind, da sie wissen, an wen sie die Nachricht gesendet haben. Mit IP basierten Transporten ohne beschränktes Routing wissen sie auch von wem die Nachricht kam. Dennoch erhöhen die oben genannten Techniken erheblich die Kosten um bei längeren Tuuneln aussagekräftige Daten zu erlangen.
Wie Connelly vorschlug, der Predecessor Attacke (2008 update) zu begegnen, in dem wir die Reihenfolge der Knoten in unseren Tunneln konsistent halten (das meint, wann immer Alice einen Tunnel mit Bob und Charlie aufbaut, wird Charlie immer der nächste Hop von Bob sein), setzen wir es um, damit Bob nicht Alice Auswahlgruppe an Knoten herausfinden kann. Möglicherweise gehen wir einen Schritt weiter und erlauben Bob nur, nur in einer Richtung in Alices Tunnel teilzunehmen - von Dave eine Nachricht empfangen und an Charlie weiterreichen - und falls diese Knoten nicht zur Teilnahme im Tunnel zur Verfügung stehen (z.B. wegen Überlastung, Disconnect oder so) wird Bob nicht mehr zur Teilnahme im Tunnel angefragt, bis die Knoten wieder zur Verfügung stehen.
Es sind noch mehr Analysen notwendig um den Prozess der Tunnelherstellung zu überprüfen - zur Zeit suchen wir einfach aus und sortieren zufällig in der "Top Tier" Knotengruppe des Knotens (die Gruppe mit "fast + high capacity).
Das Anwenden des strengen Sortierens der Knoten im Tunnel erhöht auch die Anonymität der Knoten mit 0-Hop Tunnels, da sonst die Tatsache, das das Gateway eines Knoten immer gleich bliebe, eine verräterische Aussage ist. Dennoch sollten Knoten mit 0-Hop Tunnel hin und wieder 1-Hop Tunnel nutzen um den Ausfall eines normalen, zuverlässlichen Gateways zu simulieren (z.B. alle MTBF*(Tunnel Lebensdauer) Minuten einen 1-Hop Tunnel nutzen).
Ohne Tunnellängen Variationen kann jemand, der irgendwie die Tunnellänge einer Destination mitbekommt, diese Information nutzen, um die IP Adresse eines Zieles mittels einer "Predecessor Atacke" herauszubekommen. Wenn z.B. jeder 2-Hop Tunnel nutzt und Bob eine Tunnelnachricht von charlie bekommt und an Alice weiterreicht, weiss Bob, das Alice der letzte Knoten im Tunnels ist. Falls Bob identifizieren kann, welche Destination den Tunnel erstellt hat (das meint durch Kollisionen mit dem Gateway und die Netzwerk Datenbank nach all den Leasesets durchsuchen), dann kennt er den Router, auf dem die Destination liegt (und ohne beschränkte Routen auch die IP).
Um dem Verhalten der Benutzer zu begegnen sollten Tunnellängen variiert werden, realisiert mit einem Algorhytmus der auf der angefragten Tunnellänge basiert (z.B. die 1/MTBF Tunnellänge für 0-Hop Tunnels, wie oben beschrieben).
Die Funktionalität der oben beschriebenen beschränkten Routen ist eine einfache funktionale Angelegenheit - wie man Knoten, die sonst nicht teilnehmen können, am Netz teilnehmen lassen kann. Doch bietet dieses Konzept noch weitere Möglichkeiten. Z.B. wenn ein Router es nicht riskieren kann, mit nicht vertrauten Knoten direkt zu kommunizieren, bauen sie vertrauenswürdige Verbindungen durch diese Knoten auf, die es ihnen erlaubt, all ihre Nachrichten zu senden und zu empfangen. Diese versteckten Knoten, die absolut isoliert sein möchten, verneinen auch alle Verbindungsversuche anderer Knoten (wie sie in den "Garlic Routing" Techniken zuvor dargestellt wurden) - sie nehmen einfach den Garlicmantel, der den Befehl zum Transport zu einem bestimmten Knoten enthält, und routen diese Nachricht mitsamt den Befehlen zum Weiterreichen an den ursprünglich geforderten Zielknoten über einen der vertrauenswürdigen Verbindungen des versteckten Knoten hinaus.
Im Netzwerk werden wir wollen, das einige Personen nicht zu viele Ressourcen verbrauchen oder so viele Knoten erstellen, das sie einen Sybil Angriff starten können. Traditionelle Techniken wie einen Knoten sehen lassen, wer eine Ressource anfordert oder einen Knoten beteibt, funktionieren in I2P nicht, da sie die Anonymität des Systems herabsetzen. Stattdessen möchten wir einige Anforderungen "teuer" machen.
Hashcash ist eine technik, die wir anonym nutzen können um bestimmte Aktivitäten "teuer" zu machen, so zum Beispiel das Erstellen einer neuen Router ID (nur einmal bei der Installation nötig), erstellen einer neuen Destination (nur einmal beim Erstellen des Services nötig) oder beim Anfordern von Knoten zum Teilnehmen in Tunneln (oft, etwa 2-300 mal die Stunde). Wir kennen nicht die "korrekten" Kosten für diese Aktivitäten bis jetzt, aber mit etwas forschen und experimentieren sollten wir einen Basislevel finden, der ausreichend teuer ist und dennoch keine unnötige Belastung für die Leute mit wenig Ressourcen darstellt.
Es gibt ein paar weitere Methoden die wir erkunden können um die Anfragen an Ressourcen "nicht kostenlos" zu machen und mehr forschen an dieser Front ist gerne gesehen.
Für grosse passive externe Beobachter und grosse interne, Kollusionen hervorrufende Beobachter ist das standard Tunnel Routing angreifbar über Traffic Analysen - einfache die grösse und Frequenz der Nachrichten zwischen Routern beobachten. Um sich dagegen zu schützen, möchten wir einige der Tunnel in ihre eigene Mixing Kaskade verwandeln - Nachrichten am Gateway sammeln, verzögern und in Paketen aussenden, falls nötig in anderer Reihenfolge und auch das einfügen von sinnlosen Nachrichten (für die Knoten im Pfad nicht von den "echten" Nachrichten unterscheidbar). Es gab schon eine Menge an Nachforschungen an diesen algorhytmen, an denen wir zuerst lernen können bevor wir die verschiedenen Tunnel Mix Strategien einbauen.
Zu den Aspekten der Anonymität der mehr variierenden Tunnel Operationen gibt es auch funktionale Aspekte. Jeder Knoten kann nur eine bestimmte Menge an Daten für das Netzwerk transportieren. Und um einen bestimmten Tunnel daran zu hindern, eine viel zu grosse Menge der Bandbreite zu nutzen, sollten die Knoten einige Bremsen für die Tunnel eingebaut haben. Zum Beispiel kann ein Tunnel so eingestellt werden, das er nach 600 transportierten Nachrichten (1 jede Sekunde), 2,4MB (4KBps) oder überschreiten eines Durchschnitts (8KBps für die letzte Minute) gebremst wird. Zuviele Nachrichten werden verzögert oder einfach fallen gelassen. Mit dieser Art des Beschränkens können Knoten eine Art ATM-QoS für bereitstellen, welches verhindert, das verhindert, das mehr Bandbreite alloziert wird, als der Knoten zur Verfügung hat.
Dazu wollen wir möglicherweise Code zum dynamischen Umrouten von Tunneln einbauen, um ausfallende Knoten zu umgehen oder zusätzliche Knoten in den Weg einzufügen. Dies kann durchs garlic routen einer Nachricht an einen bestimmtel Knoten im Tunnel geschehen, die Informationen zum umdefinieren des nächsten Hops im Tunnel..
Über das pro-Tunnel Sammeln und Mixen hinweg gibt es weitere Möglichkeiten um sich gegen grosse Angreifer zu schützen, so wie zum Beispiel für jeden Schritt im garlic gerouteten Pfad eine Verzögerung oder Zeitfenster zum Weiterrouten der Nachrichten zu defenieren. Dieses gibt Schutz gegen einen langzeit "Intersection" Angriff, da ein Knoten Nachrichten verschicken kann, die für die meisen Knoten auf dem Weg wie perfekte Standard Nachrichten aussehen, nur für den Knoten nicht, für den die Ummantelung die Verzögerungsanweisung preisgibt.
Die zufällige Auswahl von Tunneln und Leases für jede Nachricht erzeugt eine grosse Anzahl von Nachrichten, die nicht in korrekter Reihenfolge transportiert werden. Dieses hindert die Streaming Bibliothek daran, die Grösse der Nutzdaten auf die maximal mögliche zu erweitern. Durchs stabile Halten der Auswahl für eine Verbindung wird die Übertragungsrate viel höher.
I2P fügte ein Antwort-Leaseset (typischweise 1056 bytes) an jede ausgehende Klient Nachricht an, was ein massiver Overhead war. Korrigiert in 0.6.2.
Zur Zeit funktioniert unser ElGamal/AES+SessinTag Algorythmus so, das er jede verschlüsselte Nachricht mit einer zufälligen 32byte Einmalmarkierung markiert (ein "Session Tag"), die die Nachricht als mit dem assozierenden AES Session Schlüssel verschlüsselt identifiziert. Dies verhindert, das Knoten Nachrichten als zur selben Session identifizieren können, da jede Nachricht eine komplett neue zufällige Markierung haben. Um dieses zu erfüllen bündeln wir alle paar Nachrichten ein komplett neues Set an Session Tags in die verschlüsselte Nachricht und haben so einen transparenten Weg um zukünftige Nachrichten zu indentifizieren. Wir müssen dann nur noch die erfolgrich transportierten Nachrichten verfolgen um zu wissen, welche Tags wir nutzen sollten.
Dieses arbeitet gut und robust, ist aber aus Sicht des Bandbreitenverbrauchs ineffizient, da es das Vorschicken der Session Tags erfordert (und nicht alle Tags werden gebraucht, manche können verfallen, da ihre Lebenszeit ausgelaufen ist). Im Schnitt kostet das Vorraussenden der Session Tags 32 Bytes für jede Nachricht (die Grösse eines Tags). Wie taral jedoch vorgeschlagen hat, kann das vermieden werden durch Nutzung einer synchronisierten PRNG - sobald eine neue Session aufgebaut ist (durch den ElGamal verschlüsselen Block) seeden beide Seiten eine PRNG und generieren die Session Tags nach Bedarf (wobei der Empfänger die nächsten paar möglichen Tags vorrausberechnet, um Nachrichten in falscher Reihenfolge händeln zu können).
Seit I2P Version 0.4.2 haben wir eine "full sliding window" Streaming Bibliothek, die die alte feste "Window"-Grösse Streaming Bibliothek stark erweitert und die Verzögerung beim nochmal senden stark herabsetzt. Wie auch immer, gibt es auch hier noch Platz für weitere Optimierungen: