From bb8736f2cdd67e92333aa26cd74e5159310cdb49 Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 27 Feb 2011 16:24:11 +0000 Subject: [PATCH 1/6] drop obsolete docs --- router/doc/net.png | Bin 24115 -> 0 bytes router/doc/techintro.html | 994 ---------------------------- router/doc/tunnel-alt-creation.html | 163 ----- router/doc/tunnel-alt.html | 467 ------------- router/doc/tunnel.html | 529 --------------- router/doc/udp.html | 759 --------------------- router/doc/udp.png | Bin 34329 -> 0 bytes 7 files changed, 2912 deletions(-) delete mode 100644 router/doc/net.png delete mode 100644 router/doc/techintro.html delete mode 100644 router/doc/tunnel-alt-creation.html delete mode 100644 router/doc/tunnel-alt.html delete mode 100644 router/doc/tunnel.html delete mode 100644 router/doc/udp.html delete mode 100644 router/doc/udp.png diff --git a/router/doc/net.png b/router/doc/net.png deleted file mode 100644 index 6eac3784245e13c794795634b797ec2ea0a2eab1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24115 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 D6zp&p diff --git a/router/doc/techintro.html b/router/doc/techintro.html deleted file mode 100644 index 1574f5fa2..000000000 --- a/router/doc/techintro.html +++ /dev/null @@ -1,994 +0,0 @@ - - - Introducing I2P - a scalable framework for anonymous communication - - - - -
-Introducing I2P
-a scalable framework for anonymous communication
-$Id: techintro.html,v 1.8.2.1 2006/02/13 07:13:35 jrandom Exp $ -
-
- - - - - -
-
-* Introduction
-* Operation
-  * Overview
-  * Tunnels
-  * Network Database
-  * Transport protocols
-  * Cryptography
-
-
-
-* Future
-  * Restricted routes
-  * Variable latency
-  * Open questions
-
-
-
-* Similar systems
-  * Tor
-  * Freenet
-* Appendix A: Application layer
-
-
-
- -
- -

Introduction

-

-I2P is a scalable, self organizing, resilient packet switched anonymous network layer, -upon which any number of different anonymity or security conscious applications -can operate. Each of these applications may make their own anonymity, latency, and -throughput tradeoffs without worrying about the proper implementation of a free -route mixnet, allowing them to blend their activity with the larger anonymity set of -users already running on top of I2P. Applications available already provide the full -range of typical Internet activities - anonymous web browsing, anonymous web hosting, -anonymous blogging and content syndication (with Syndie), -anonymous chat (via IRC or Jabber), anonymous swarming file transfers (with i2p-bt, I2PSnark, and -Azureus), anonymous file sharing (with -I2Phex), anonymous email (with I2Pmail -and susimail), anonymous newsgroups, as well as several -other applications under development. Unlike web sites hosted within content -distribution networks like Freenet or -GNUnet, the services hosted on I2P are fully -interactive - there are traditional web-style search engines, bulletin boards, blogs -you can comment on, database driven sites, and bridges to query static systems like -Freenet without needing to install it locally. -

- -

-With all of these anonymity enabled applications, I2P takes on the role of the message -oriented middleware - applications say that they want to send some data to a cryptographic -identifier (a "destination") and I2P takes care of making sure it gets there securely -and anonymously. I2P also bundles a simple streaming library -to allow I2P's anonymous best-effort messages to transfer as reliable, in-order streams, -transparently offering a TCP based congestion control algorithm tuned for the high -bandwidth delay product of the network. While there have been several simple SOCKS -proxies available to tie existing applications into the network, their value has been -limited as nearly every application routinely exposes what, in an anonymous context, -is sensitive information. The only safe way to go is to fully audit an application to -ensure proper operation, and to assist in that we provide a series of APIs in various -languages which can be used to make the most out of the network. -

- - - - -

-I2P is not a research project - academic, commercial, or governmental, but is instead -an engineering effort aimed at doing whatever is necessary to provide a sufficient -level of anonymity to those who need it. It has been in active development since -early 2003 with one full time developer and a dedicated group of part time contributors -from all over the world. All of the work done on I2P is open source and -freely available on the website, with the majority -of the code released outright into the public domain, though making use of a few -cryptographic routines under BSD-style licenses. The people working on I2P do not -control what people release client applications under, and there are several GPL'ed -applications available (I2PTunnel, -susimail, I2PSnark, Azureus, -I2Phex). Funding -for I2P comes entirely from donations, and does not receive any tax breaks in any -jurisdiction at this time, as many of the developers are themselves anonymous. -

- -

Operation

-

Overview

- -

-To understand I2P's operation, it is essential to understand a few key concepts. -First, I2P makes a strict separation between the software participating -in the network (a "router") and the anonymous endpoints ("destinations") associated -with individual applications. The fact that someone is running I2P is not usually -a secret. What is hidden is information on what the user is doing, if anything at -all, as well as what router a particular destination is connected to. End users -will typically have several local destinations on their router - for instance, one -proxying in to IRC servers, another supporting the user's anonymous webserver ("eepsite"), -another for an I2Phex instance, another for torrents, etc. -

- -

-Another critical concept to understand is the "tunnel" - a directed path through -an explicitly selected set of routers, making use of layered encryption so that -the messages sent in the tunnel's "gateway" appear entirely random at each hop -along the path until it reaches the tunnel's "endpoint". These unidirectional -tunnels can be seen as either "inbound" tunnels or "outbound" tunnels, referring -to whether they are bringing messages to the tunnel's creator or away from them, -respectively. The gateway of an inbound tunnel can receive messages from any -peer and will forward them down through the tunnel until it reaches the (anonymous) -endpoint (the creator). On the other hand, the gateway of an outbound tunnel is -the tunnel's creator, and messages sent through that tunnel are encoded so that -when they reach the outbound tunnel's endpoint, that router has the instructions -necessary to forward the message on to the appropriate location. -

- -

-A third critical concept to understand is I2P's "network database" (or "netDb") -- a pair of algorithms used to share network metadata. The two types of metadata -carried are "routerInfo" and "leaseSets" - the routerInfo gives routers the data -necessary for contacting a particular router (their public keys, transport -addresses, etc), while the leaseSet gives routers the information necessary for -contacting a particular destination. Within each leaseSet, there are any number -of "leases", each of which specifies the gateway for one of that destination's -inbound tunnels as well as when that tunnel will expire. The leaseSet also -contains a pair of public keys which can be used for layered garlic encryption. -

- - - -

-When Alice wants to send a message to Bob, she first does a lookup in the -netDb to find Bob's leaseSet, giving her his current inbound tunnel gateways. -She then picks one of her outbound tunnels and sends the message -down it with instructions for the outbound tunnel's endpoint to forward the -message on to one of Bob's inbound tunnel gateways. When the outbound -tunnel endpoint receives those instructions, it forwards the message as -requested, and when Bob's inbound tunnel gateway receives it, it is -forwarded down the tunnel to Bob's router. If Alice wants Bob to be able -to reply to the message, she needs to transmit her own destination explicitly -as part of the message itself (taken care of transparently in the -streaming library). Alice may also cut down on -the response time by bundling her most recent leaseSet with the message so -that Bob doesn't need to do a netDb lookup for it when he wants to reply, but this -is optional. -

- -

-While the tunnels themselves have layered encryption to prevent unauthorized -disclosure to peers inside the network (as the transport layer itself does to -prevent unauthorized disclosure to peers outside the network), it is necessary -to add an additional end to end layer of encryption to hide the message from the -outbound tunnel endpoint and the inbound tunnel gateway. This -"garlic encryption" lets Alice's router wrap up multiple -messages into a single "garlic message", encrypted to a particular public key -so that intermediary peers cannot determine either how many messages are within -the garlic, what those messages say, or where those individual cloves are -destined. For typical end to end communication between Alice and Bob, the -garlic will be encrypted to the public key published in Bob's leaseSet, -allowing the message to be encrypted without giving out the public key to Bob's -own router. -

- -

-Another important fact to keep in mind is that I2P is entirely message based -and that some messages may be lost along the way. Applications using I2P -can use the message oriented interfaces and take care of their own congestion -control and reliability needs, but most would be best served by reusing the -provided streaming library to view I2P as a streams -based network. -

- -

Tunnels

- -

-Both inbound and outbound tunnels work along similar principles - the tunnel -gateway accumulates a number of tunnel messages, eventually preprocessing them -into something for tunnel delivery. Next, the gateway encrypts that preprocessed -data and forwards it to the first hop. That peer and subsequent tunnel -participants add on a layer of encryption after verifying that it isn't a -duplicate before forward it on to the next peer. Eventually, the -message arrives at the endpoint where the messages are split out again and -forwarded on as requested. The difference arises in what -the tunnel's creator does - for inbound tunnels, the creator is the endpoint -and they simply decrypt all of the layers added, while for outbound tunnels, -the creator is the gateway and they pre-decrypt all of the layers so that after -all of the layers of per-hop encryption are added, the message arrives in the -clear at the tunnel endpoint. -

- -

-The choice of specific peers to pass on messages as well as their particular -ordering is important to understanding both I2P's anonymity and performance -characteristics. While the network database (below) has its own criteria for -picking what peers to query and store entries on, tunnels may use any peers in -the network in any order (and even any number of times) in a single tunnel. If -perfect latency and capacity data were globally known, selection and ordering -would be driven by the particular needs of the client in tandem with their threat -model. Unfortunately, latency and capacity data is not trivial to gather -anonymously, and depending upon untrusted peers to provide this information has -its own serious anonymity implications. -

- -

-From an anonymity perspective, the simplest technique would be to pick peers -randomly from the entire network, order them randomly, and use those peers -in that order for all eternity. From a performance perspective, the simplest -technique would be to pick the fastest peers with the necessary spare capacity, -spreading the load across different peers to handle transparent failover, and -to rebuild the tunnel whenever capacity information changes. While the former -is both brittle and inefficient, the later requires inaccessible information -and offers insufficient anonymity. I2P is instead working on offering a range -of peer selection strategies, coupled with anonymity aware measurement code to -organize the peers by their profiles. -

- -

-As a base, I2P is constantly profiling the peers with which it interacts with -by measuring their indirect behavior - for instance, when a peer responds to -a netDb lookup in 1.3 seconds, that round trip latency is recorded in the -profiles for all of the routers involved in the two tunnels (inbound and -outbound) through which the request and response passed, as well as the queried -peer's profile. Direct measurement, such as transport layer latency or -congestion, is not used as part of the profile, as it can be manipulated and -associated with the measuring router, exposing them to trivial attacks. While -gathering these profiles, a series of calculations are run on each to summarize -its performance - its latency, capacity to handle lots of activity, whether they -are currently overloaded, and how well integrated into the network they seem to -be. These calculations are then compared for active peers to organize the routers -into four tiers - fast and high capacity, high capacity, not failing, and failing. -The thresholds for those tiers are determined dynamically, and while they -currently use fairly simple algorithms, alternatives exist. -

- -

-Using this profile data, the simplest reasonable peer selection strategy is to -pick peers randomly from the top tier (fast and high capacity), and this is -currently deployed for client tunnels. Exploratory tunnels (used for netDb -and tunnel management) pick peers randomly from the not failing tier (which -includes routers in 'better' tiers as well), allowing the peer to sample -routers more widely, in effect optimizing the peer selection through randomized -hill climbing. These strategies alone do however leak information regarding the -peers in the router's tip tier through predecessor and netDb harvesting attacks. -In turn, several alternatives exist which, while not balancing the load as evenly, -will address the attacks mounted by particular classes of adversaries. -

- -

-By picking a random key and ordering the peers according to their XOR distance -from it, the information leaked is reduced in predecessor and harvesting attacks -according to the peers' failure rate and the tier's churn. Another simple strategy -for dealing with netDb harvesting attacks is to simply fix the inbound tunnel -gateway(s) yet randomize the peers further on in the tunnels. To deal with -predecessor attacks for adversaries which the client contacts, the outbound tunnel -endpoints would also remain fixed. The selection of which peer to fix on the most -exposed point would of course need to have a limit to the duration, as all peers -fail eventually, so it could either be reactively adjusted or proactively avoided -to mimic a measured mean time between failures of other routers. These two strategies -can in turn be combined, using a fixed exposed peer and an XOR based ordering within -the tunnels themselves. A more rigid strategy would fix the exact peers and ordering -of a potential tunnel, only using individual peers if all of them agree to participate -in the same way each time. This varies from the XOR based ordering in that the -predecessor and successor of each peer is always the same, while the XOR only makes -sure their order doesn't change. -

- -

-As mentioned before, I2P currently (release 0.6.1.1) includes the tiered random -strategy above, but the others are planned for the 0.6.2 release. A more detailed -discussion of the mechanics involved in tunnel operation, management, and peer -selection can be found in the -tunnel spec. -

- -

Network Database

- -

-As mentioned earlier, I2P's netDb works to share the network's metadata. Two -algorithms are used to accomplish this - primarily, a small set of routers are -designated as "floodfill peers", while the rest of the routers participate in -the Kademlia derived -distributed hash table for redundancy. To integrate the two algorithms, each -router always uses the Kademlia style store and fetch, but acts as if the -floodfill peers are 'closest' to the key in question. Additionally, when a -peer publishes a key into the netDb, after a brief delay they query another -random floodfill peer, asking them for the key, and if that peer does not have -it, they move on and republish the key again. Behind the scenes, when one of -the floodfill peers receives a new valid key, they republish it to the other -floodfill peers who then cache it locally. -

- -

-Each piece of data in the netDb is self authenticating - signed by the -appropriate party and verified by anyone who uses or stores it. In addition, -the data has liveliness information within it, allowing irrelevant entries to be -dropped, newer entries to replace older ones, and, for the paranoid, protection -against certain classes of attack. This is also why I2P bundles the necessary -code for maintaining the correct time, occasionally querying some SNTP servers -(the pool.ntp.org round robin by default) -and detecting skew between routers at the transport layer. -

- -

-The routerInfo structure itself contains all of the information that one router -needs to know to securely send messages to another router. This includes their -identity (made up of a 2048bit ElGamal public key, a 1024bit DSA public key, and -a certificate), the transport addresses which they can be reached on, such as -an IP address and port, when the structure was published, and a set of arbitrary -uninterpreted text options. In addition, there is a signature against all of -that data as generated by the included DSA public key. The key for this routerInfo -structure in the netDb is the SHA256 hash of the router's identity. The options -published are often filled with information helpful in debugging I2P's operation, -but when I2P reaches the 1.0 release, the options will be disabled and kept blank. -

- -

-The leaseSet structure is similar, in that it includes the I2P destination -(comprised of a 2048bit ElGamal public key, a 1024bit DSA public key, and a -certificate), a list of "leases", and a pair of public keys for garlic encrypting -messages to the destination. Each of the leases specify one of the destination's -inbound tunnel gateways by including the SHA256 of the gateway's identity, a 4 -byte tunnel id on that gateway, and when that tunnel will expire. The key for -the leaseSet in the netDb is the SHA256 of the destination itself. -

- -

-As the router currently automatically bundles the leaseSet for the sender inside -a garlic message to the recipient, the leaseSet for destinations which will not -receive unsolicited messages do not need to be published in the netDb at all. If -the destination itself is sensitive, the leaseSet could instead be transmitted -through other means without ever going into the netDb. -

- -

-Bootstrapping the netDb itself is simple - once a router has at least one routerInfo -of a reachable peer, they query that router for references to other routers in the -network with the Kademlia healing algorithm. Each routerInfo reference is stored in -an individual file in the router's netDb subdirectory, allowing people to easily -share their references to bootstrap new users. -

- -

-Unlike traditional DHTs, the very act of conducting a search distributes the data -as well, since rather passing Kademlia's standard IP+port pairs, references are given -to the routers that the peer should query next (namely, the SHA256 of those routers' -identities). As such, iteratively searching for a particular destination's leaseSet -or router's routerInfo will also provide you with the routerInfo of the peers along -the way. In addition, due to the time sensitivity of the data published, the information -doesn't often need to migrate between peers - since a tunnel is only valid for 10 -minutes, the leaseSet can be dropped after that time has passed. To take into -account Sybil attacks on the netDb, the Kademlia routing location used for any given -key varies over time. For instance, rather than storing a routerInfo on the peers -closest to SHA256(routerInfo.identity), they are stored on the peers closest to -SHA256(routerInfo.identity + YYYYMMDD), requiring an adversary to remount the attack -again daily so as to maintain their closeness to the current routing key. As the -very fact that a router is making a lookup for a given key may expose sensitive data -(and the fact that a router is publishing a given key even more so), all netDb -messages are transmitted through the router's exploratory tunnels. -

- -

-The netDb plays a very specific role in the I2P network, and the algorithms have -been tuned towards our needs. This also means that it hasn't been tuned to address the -needs we have yet to run into. As the network grows, the primary floodfill algorithm -will need to be refined to exploit the capacity available, or perhaps replaced with -another technique for securely distributing the network metadata. -

- -

Transport protocols

- -

-Communication between routers needs to provide confidentiality and integrity -against external adversaries while authenticating that the router contacted -is the one who should receive a given message. The particulars of how routers -communicate with other routers aren't critical - three separate protocols have -been used at different points to provide those bare necessities. To accommodate -the need for high degree communication (as a number of routers will end up -speaking with many others), I2P moved from a TCP based transport -to a UDP based one - "Secure Semireliable UDP", or "SSU". As described in the -SSU spec:

- -
-The goal of this protocol is to provide secure, authenticated, -semireliable, and unordered message delivery, exposing only a minimal amount of -data easily discernible to third parties. It should support high degree -communication as well as TCP-friendly congestion control, and may include -PMTU detection. It should be capable of efficiently moving bulk data at rates -sufficient for home users. In addition, it should support techniques for -addressing network obstacles, like most NATs or firewalls. -
- -

Cryptography

- -

-A bare minimum set of cryptographic primitives are combined together to provide I2P's -layered defenses against a variety of adversaries. At the lowest level, interrouter -communication is protected by the transport layer security - SSU -encrypts each packet with AES256/CBC with both an explicit IV and MAC (HMAC-MD5-128) -after agreeing upon an ephemeral session key through a 2048bit Diffie-Hellman exchange, -station-to-station authentication with the other router's DSA key, plus each network -message has their own hash for local integrity checking. -Tunnel messages passed over the transports have their own -layered AES256/CBC encryption with an explicit IV and verified at the tunnel endpoint -with an additional SHA256 hash. Various other messages are passed along inside -"garlic messages", which are encrypted with ElGamal/AES+SessionTags (explained below). -

- -

Garlic messages

- -

-Garlic messages are an extension of "onion" layered encryption, allowing the contents -of a single message to contain multiple "cloves" - fully formed messages alongside -their own instructions for delivery. Messages are wrapped into a garlic message whenever -the message would otherwise be passing in cleartext through a peer who should not have -access to the information - for instance, when a router wants to ask another router to -participate in a tunnel, they wrap the request inside a garlic, encrypt that garlic to -the receiving router's 2048bit ElGamal public key, and forward it through a tunnel. -Another example is when a client wants to send a message to a destination - the sender's -router will wrap up that data message (alongside some other messages) into a garlic, -encrypt that garlic to the 2048bit ElGamal public key published in the recipient's -leaseSet, and forward it through the appropriate tunnels. -

- -

-The "instructions" attached to each clove inside the encryption layer includes the -ability to request that the clove be forwarded locally, to a remote router, or to a -remote tunnel on a remote router. There are fields in those instructions allowing a -peer to request that the delivery be delayed until a certain time or condition has -been met, though they won't be honored until the -nontrivial delays are deployed. It is possible to -explicitly route garlic messages any number of hops without building tunnels, or even -to reroute tunnel messages by wrapping them in garlic messages and forwarding them a -number of hops prior to delivering them to the next hop in the tunnel, but those -techniques are not currently used in the existing implementation. -

- -

Session tags

- -

-As an unreliable, unordered, message based system, I2P uses a simple combination of -asymmetric and symmetric encryption algorithms to provide data confidentiality and -integrity to garlic messages. As a whole, the combination is referred to as -ElGamal/AES+SessionTags, but that is an excessively verbose way to describe the simple -use of 2048bit ElGamal, AES256, SHA256, and 32 byte nonces. -

- -

-The first time a router wants to encrypt a garlic message to another router, they encrypt -the keying material for an AES256 session key with ElGamal and append the AES256/CBC -encrypted payload after that encrypted ElGamal block. In addition to the encrypted -payload, the AES encrypted section contains the payload length, the SHA256 hash of the -unencrypted payload, as well as a number of "session tags" - random 32 byte nonces. The -next time the sender wants to encrypt a garlic message to another router, rather than -ElGamal encrypt a new session key they simply pick one of the previously delivered session -tags and AES encrypt the payload like before, using the session key used with that -session tag, prepended with the session tag itself. When a router receives a garlic encrypted -message, they check the first 32 bytes to see if it matches an available session tag - if -it does, they simply AES decrypt the message, but if it does not, they ElGamal decrypt the -first block. -

- -

-Each session tag can be used only once so as to prevent internal adversaries from unnecessarily -correlating different messages as being between the same routers. The sender of an -ElGamal/AES+SessionTag encrypted message chooses when and how many tags to deliver, -prestocking the recipient with enough tags to cover a volley of messages. Garlic messages -may detect the successful tag delivery by bundling a small additional message as a clove (a -"delivery status message") - when the garlic message arrives at the intended recipient and -is decrypted successfully, this small delivery status message is one of the cloves exposed and -has instructions for the recipient to send the clove back to the original sender (through an -inbound tunnel, of course). When the original sender receives this delivery status message, -they know that the session tags bundled in the garlic message were successfully delivered. -

- -

-Session tags themselves have a very short lifetime, after which they are discarded -if not used. In addition, the quantity stored for each key is limited, as are the -number of keys themselves - if too many arrive, either new or old messages may be -dropped. The sender keeps track whether messages using session tags are getting -through, and if there isn't sufficient communication it may drop the ones previously -assumed to be properly delivered, reverting back to the full expensive ElGamal -encryption. -

- -

-One alternative is to transmit only a single session tag, and from that, seed a -deterministic PRNG for determining what tags to use or expect. By keeping this -PRNG roughly synchronized between the sender and recipient (the recipient precomputes a -window of the next e.g. 50 tags), the overhead of periodically bundling a large number -of tags is removed, allowing more options in the space/time tradeoff, and perhaps -reducing the number of ElGamal encryptions necessary. However, it would depend -upon the strength of the PRNG to provide the necessary cover against internal -adversaries, though perhaps by limiting the amount of times each PRNG is used, any -weaknesses can be minimized. At the moment, there are no immediate plans to move -towards these synchronized PRNGs. -

- -

Future

-

-While I2P is currently functional and sufficient for many scenarios, there are -several areas which require further improvement to meet the needs of those -facing more powerful adversaries as well as substantial user experience optimization. -

- -

Restricted route operation

- -

-I2P is an overlay network designed to be run on top of a functional packet switched -network, exploiting the end to end principle to offer anonymity and security. -While the Internet no longer fully embraces the end to end principle, I2P does require a -substantial portion of the network to be reachable - there may be a number of peers -along the edges running using restricted routes, but I2P does not include an -appropriate routing algorithm for the degenerate case where most peers are -unreachable. It would, however work on top of a network employing such an -algorithm. -

- -

-Restricted route operation, where there are limits to what peers are -reachable directly, has several different functional and anonymity -implications, dependent upon how the restricted routes are handled. At the most -basic level, restricted routes exist when a peer is behind a NAT or firewall which -does not allow inbound connections. This was largely addressed in I2P 0.6.0.6 by -integrating distributed hole punching into the transport layer, allowing people -behind most NATs and firewalls to receive unsolicited connections without any -configuration. However, this does not limit the exposure of the peer's IP address to -routers inside the network, as they can simply get introduced to the peer through -the published introducer. -

- -

-Beyond the functional handling of restricted routes, there are two levels of -restricted operation that can be used to limit the exposure of one's IP address - -using router-specific tunnels for communication, and offering 'client routers'. For -the former, routers can either build a new pool of tunnels or reuse their exploratory -pool, publishing the inbound gateways to some of them as part of their routerInfo in -place of their transport addresses. When a peer wants to get in touch with them, -they see those tunnel gateways in the netDb and simply send the relevant message to -them through one of the published tunnels. If the peer behind the restricted route -wants to reply, it may do so either directly (if they are willing to expose their IP -to the peer) or indirectly through their outbound tunnels. When the routers that the -peer has direct connections to want to reach it (to forward tunnel messages, for -instance), they simply prioritize their direct connection over the published tunnel -gateway. The concept of 'client routers' simply extends the restricted route by not -publishing any router addresses. Such a router would not even need to publish their -routerInfo in the netDb, merely providing their self signed routerInfo to the peers -that it contacts (necessary to pass the router's public keys). Both levels of -restricted route operation are planned for I2P 2.0. -

- -

-There are tradeoffs for those behind restricted routes, as they would likely -participate in other people's tunnels less frequently, and the routers which -they are connected to would be able to infer traffic patterns that would not -otherwise be exposed. On the other hand, if the cost of that exposure is less -than the cost of an IP being made available, it may be worthwhile. This, of course, -assumes that the peers that the router behind a restricted route contacts are not -hostile - either the network is large enough that the probability of using a hostile -peer to get connected is small enough, or trusted (and perhaps temporary) peers are -used instead. -

- -

Variable latency

- -

-Even though the bulk of I2P's initial efforts have been on low latency communication, -it was designed with variable latency services in mind from the beginning. At the -most basic level, applications running on top of I2P can offer the anonymity of -medium and high latency communication while still blending their traffic patterns -in with low latency traffic. Internally though, I2P can offer its own medium and -high latency communication through the garlic encryption - specifying that the -message should be sent after a certain delay, at a certain time, after a certain -number of messages have passed, or another mix strategy. With the layered encryption, -only the router that the clove exposed the delay request would know that the message -requires high latency, allowing the traffic to blend in further with the low latency -traffic. Once the transmission precondition is met, the router holding on to the -clove (which itself would likely be a garlic message) simply forwards it as -requested - to a router, to a tunnel, or, most likely, to a remote client destination. -

- -

-There are a substantial number of ways to exploit this capacity for high latency -comm in I2P, but for the moment, doing so has been scheduled for the I2P 3.0 release. -In the meantime, those requiring the anonymity that high latency comm can offer should -look towards the application layer to provide it. -

- -

Open questions

-
-How to get rid of the timing constraint?
-Can we deal with the sessionTags more efficiently?
-What, if any, batching/mixing strategies should be made available on the tunnels?
-What other tunnel peer selection and ordering strategies should be available?
-
- -

Similar systems

-

-I2P's architecture builds on the concepts of message oriented middleware, the topology -of DHTs, the anonymity and cryptography of free route mixnets, and the adaptability of -packet switched networking. The value comes not from novel concepts of algorithms -though, but from careful engineering combining the research results of existing -systems and papers. While there are a few similar efforts worth reviewing, both for -technical and functional comparisons, two in particular are pulled out here - Tor -and Freenet. -

- -

Tor

-

website

- -

-At first glance, Tor and I2P have many functional and anonymity related similarities. -While I2P's development began before we were aware of the early stage efforts on Tor, -many of the lessons of the original onion routing and ZKS efforts were integrated into -I2P's design. Rather than building an essentially trusted, centralized system with -directory servers, I2P has a self organizing network database with each peer taking on -the responsibility of profiling other routers to determine how best to exploit available -resources. Another key difference is that while both I2P and Tor use layered and -ordered paths (tunnels and circuits/streams), I2P is fundamentally a packet switched -network, while Tor is fundamentally a circuit switched one, allowing I2P to -transparently route around congestion or other network failures, operate redundant -pathways, and load balance the data across available resources. While Tor offers -the useful outproxy functionality by offering integrated outproxy discovery and -selection, I2P leaves such application layer decisions up to applications running on -top of I2P - in fact, I2P has even externalized the TCP-like streaming library itself -to the application layer, allowing developers to experiment with different strategies, -exploiting their domain specific knowledge to offer better performance. -

- -

-From an anonymity perspective, there is much similarity when the core networks are -compared. However, there are a few key differences. When dealing with an internal -adversary or most external adversaries, I2P's simplex tunnels expose half as much -traffic data than would be exposed with Tor's duplex circuits by simply looking at -the flows themselves - an HTTP request and response would follow the same path in -Tor, while in I2P the packets making up the request would go out through one or -more outbound tunnels and the packets making up the response would come back through -one or more different inbound tunnels. While I2P's peer selection and ordering -strategies should sufficiently address predecessor attacks, I2P can trivially -mimic Tor's non-redundant duplex tunnels by simply building an inbound and -outbound tunnel along the same routers.

- -

-Another anonymity issue comes up in Tor's use of telescopic tunnel creation, as -simple packet counting and timing measurements as the cells in a circuit pass -through an adversary's node exposes statistical information regarding where the -adversary is within the circuit. I2P's unidirectional tunnel creation with a -single message so that this data is not exposed. Protecting the position in a -tunnel is important, as an adversary would otherwise be able to mounting a -series of powerful predecessor, intersection, and traffic confirmation attacks. -

- -

-Tor's support for a second tier of "onion proxies" does offer a nontrivial degree -of anonymity while requiring a low cost of entry, while I2P will not offer this -topology until 2.0. -

- -

-On the whole, Tor and I2P complement each other in their focus - Tor works towards -offering high speed anonymous Internet outproxying, while I2P works towards offering -a decentralized resilient network in itself. In theory, both can be used to achieve -both purposes, but given limited development resources, they both have their -strengths and weaknesses. The I2P developers have considered the steps necessary to -modify Tor to take advantage of I2P's design, but concerns of Tor's viability under -resource scarcity suggest that I2P's packet switching architecture will be able to -exploit scarce resources more effectively. -

- -

Freenet

-

website

- -

-Freenet played a large part in the initial stages of I2P's design - giving proof to -the viability of a vibrant pseudonymous community completely contained within the -network, demonstrating that the dangers inherent in outproxies could be avoided. -The first seed of I2P began as a replacement communication layer for Freenet, -attempting to factor out the complexities of a scalable, anonymous and secure point -to point communication from the complexities of a censorship resistant distributed -data store. Over time however, some of the anonymity and scalability issues -inherent in Freenet's algorithms made it clear that I2P's focus should stay strictly -on providing a generic anonymous communication layer, rather than as a component of -Freenet. Over the years, the Freenet developers have come to see the weaknesses -in the older design, prompting them to suggest that they will require a "premix" -layer to offer substantial anonymity. In other words, Freenet needs to run on top -of a mixnet such as I2P or Tor, with "client nodes" requesting and publishing data -through the mixnet to the "server nodes" which then fetch and store the data according -to Freenet's heuristic distributed data storage algorithms. -

- -

-Freenet's functionality is very complementary to I2P's, as Freenet natively provides -many of the tools for operating medium and high latency systems, while I2P natively -provides the low latency mix network suitable for offering adequate anonymity. The -logic of separating the mixnet from the censorship resistant distributed data store -still seems self evident from an engineering, anonymity, security, and resource -allocation perspective, so hopefully the Freenet team will pursue efforts in that -direction, if not simply reusing (or helping to improve, as necessary) existing -mixnets like I2P or Tor. -

- -

-It is worth mentioning that there has recently been discussion and work by the -Freenet developers on a "globally scalable darknet" using restricted routes between -peers of various trust. While insufficient information has been made publicly -available regarding how such a system would operate for a full review, from what -has been said the anonymity and scalability claims seem highly dubious. In -particular, the appropriateness for use in hostile regimes against state level -adversaries has been tremendously overstated, and any analysis on the implications -of resource scarcity upon the scalability of the network has seemingly been avoided. -Further questions regarding susceptibility to traffic analysis, trust, and other topics -do exist, but a more in-depth review of this "globally scalable darknet" will have -to wait until the Freenet team makes more information available. -

- -

Appendix A: Application layer

- -

-I2P itself doesn't really do much - it simply sends messages to remote destinations -and receives messages targeting local destinations - most of the interesting work -goes on at the layers above it. By itself, I2P could be seen as an anonymous and -secure IP layer, and the bundled streaming library as -an implementation of an anonymous and secure TCP layer on top of it. Beyond that, -I2PTunnel exposes a generic TCP proxying system for -either getting into or out of the I2P network, plus a variety of network -applications provide further functionality for end users. -

- -

Streaming library

- -

-The streaming library has grown organically for I2P - first mihi implemented the -"mini streaming library" as part of I2PTunnel, which was limited to a window -size of 1 message (requiring an ACK before sending the next one), and then it was -refactored out into a generic streaming interface (mirroring TCP sockets) and the -full streaming implementation was deployed with a sliding window protocol and -optimizations to take into account the high bandwidth x delay product. Individual -streams may adjust the maximum packet size and other options, though the default -of 4KB compressed seems a reasonable tradeoff between the bandwidth costs of -retransmitting lost messages and the latency of multiple messages. -

- -

-In addition, in consideration of the relatively high cost of subsequent messages, -the streaming library's protocol for scheduling and delivering messages has been optimized to -allow individual messages passed to contain as much information as is available. -For instance, a small HTTP transaction proxied through the streaming library can -be completed in a single round trip - the first message bundles a SYN, FIN, and -the small payload (an HTTP request typically fits) and the reply bundles the SYN, -FIN, ACK, and the small payload (many HTTP responses fit). While an additional -ACK must be transmitted to tell the HTTP server that the SYN/FIN/ACK has been -received, the local HTTP proxy can deliver the full response to the browser -immediately. -

- -

-On the whole, however, the streaming library bears much resemblance to an -abstraction of TCP, with its sliding windows, congestion control algorithms -(both slow start and congestion avoidance), and general packet behavior (ACK, -SYN, FIN, RST, rto calculation, etc). -

- -

Naming library and addressbook

-

Developed by: mihi, Ragnarok

- -

-Naming within I2P has been an oft-debated topic since the very beginning with -advocates across the spectrum of possibilities. However, given I2P's inherent -demand for secure communication and decentralized operation, the traditional -DNS-style naming system is clearly out, as are "majority rules" voting systems. -Instead, I2P ships with a generic naming library and a base implementation -designed to work off a local name to destination mapping, as well as an optional -add-on application called the "addressbook". The addressbook is a web-of-trust -driven secure, distributed, and human readable naming system, sacrificing only -the call for all human readable names to be globally unique by mandating only -local uniqueness. While all messages in I2P are cryptographically addressed -by their destination, different people can have local addressbook entries for -"Alice" which refer to different destinations. People can still discover new -names by importing published addressbooks of peers specified in their web of trust, -by adding in the entries provided through a third party, or (if some people organize -a series of published addressbooks using a first come first serve registration -system) people can choose to treat these addressbooks as name servers, emulating -traditional DNS. -

- -

-I2P does not promote the use of DNS-like services though, as the damage done -by hijacking a site can be tremendous - and insecure destinations have no -value. DNSsec itself still falls back on registrars and certificate authorities, -while with I2P, requests sent to a destination cannot be intercepted or the reply -spoofed, as they are encrypted to the destination's public keys, and a destination -itself is just a pair of public keys and a certificate. DNS-style systems on the -other hand allow any of the name servers on the lookup path to mount simple denial -of service and spoofing attacks. Adding on a certificate authenticating the -responses as signed by some centralized certificate authority would address many of -the hostile nameserver issues but would leave open replay attacks as well as -hostile certificate authority attacks. -

- -

-Voting style naming is dangerous as well, especially given the effectiveness of -Sybil attacks in anonymous systems - the attacker can simply create an arbitrarily -high number of peers and "vote" with each to take over a given name. Proof-of-work -methods can be used to make identity non-free, but as the network grows the load -required to contact everyone to conduct online voting is implausible, or if the -full network is not queried, different sets of answers may be reachable. -

- -

-As with the Internet however, I2P is keeping the design and operation of a -naming system out of the (IP-like) communication layer. The bundled naming library -includes a simple service provider interface which alternate naming systems can -plug into, allowing end users to drive what sort of naming tradeoffs they prefer. -

- -

Syndie

- -

-Syndie is a safe, anonymous blogging / content publication / content aggregation system. -It lets you create information, share it with others, and read posts from those you're -interested in, all while taking into consideration your needs for security and anonymity. -Rather than building its own content distribution network, Syndie is designed to run on -top of existing networks, syndicating content through eepsites, Tor hidden services, -Freenet freesites, normal websites, usenet newgroups, email lists, RSS feeds, etc. Data -published with Syndie is done so as to offer pseudonymous authentication to anyone -reading or archiving it. -

- -

I2PTunnel

-

Developed by: mihi

- -

-I2PTunnel is probably I2P's most popular and versatile client application, allowing -generic proxying both into and out of the I2P network. I2PTunnel can be viewed as -four separate proxying applications - a "client" which receives inbound TCP connections -and forwards them to a given I2P destination, an "httpclient" (aka "eepproxy") which -acts like an HTTP proxy and forwards the requests to the appropriate I2P destination -(after querying the naming service if necessary), a "server" which receives inbound I2P -streaming connections on a destination and forwards them to a given TCP host+port, -and an "httpserver" which extends the "server" by parsing the HTTP request and -responses to allow safer operation. There is an additional "socksclient" application, -but its use is not encouraged for reasons previously mentioned. -

- -

-I2P itself is not an outproxy network - the anonymity and security concerns inherent -in a mix net which forwards data into and out of the mix have kept I2P's design focused -on providing an anonymous network which capable of meeting the user's needs without -requiring external resources. However, the I2PTunnel "httpclient" application offers -a hook for outproxying - if the hostname requested doesn't end in ".i2p", it picks a -random destination from a user-provided set of outproxies and forwards the request to -them. These destinations are simply I2PTunnel "server" instances run by volunteers -who have explicitly chosen to run outproxies - no one is an outproxy by default, and -running an outproxy doesn't automatically tell other people to proxy through you. -While outproxies do have inherent weaknesses, they offer a simple proof of concept for -using I2P and provide some functionality under a threat model which may be sufficient -for some users. -

- -

-I2PTunnel enables most of the applications in use. An "httpserver" pointing at a -webserver lets anyone run their own anonymous website (or "eepsite") - a webserver -is bundled with I2P for this purpose, but any webserver can be used. Anyone may -run a "client" pointing at one of the anonymously hosted IRC servers, each of which -are running a "server" pointing at their local IRCd and communicating between IRCds -over their own "client" tunnels. End users also have "client" tunnels pointing at -I2Pmail's POP3 and SMTP destinations (which in turn are -simply "server" instances pointing at POP3 and SMTP servers), as well as "client" -tunnels pointing at I2P's CVS server, allowing anonymous development. At times people have -even run "client" proxies to access the "server" instances pointing at an NNTP server. -

- -

i2p-bt

-

Developed by: duck, et al

- -

-i2p-bt is a port of the mainline python BitTorrent client to run both the tracker and -peer communication over I2P. Tracker requests are forwarded through the eepproxy to -eepsites specified in the torrent file while tracker responses refer to peers by their -destination explicitly, allowing i2p-bt to open up a -streaming lib connection to query them for blocks. -

- -

-In addition to i2p-bt, a port of bytemonsoon has been made to I2P, making a few -modifications as necessary to strip any anonymity-compromising information from the -application and to take into consideration the fact that IPs cannot be used for -identifying peers. -

- -

I2PSnark

-

I2PSnark developed: jrandom, et al, ported from mjw's Snark client

- -

-Bundled with the I2P install, I2PSnark offers a simple anonymous bittorrent -client with multitorrent capabilities, exposing all of the functionality through -a plain HTML web interface. -

- -

Azureus/azneti2p

-

Developed by: parg, et al

- -

-The developers of the Azureus BitTorrent client -have created an "azneti2p" plugin, allowing Azureus users to participate in anonymous -swarms over I2P, or simply to access anonymously hosted trackers while contacting -each peer directly. In addition, Azureus' built in tracker lets people run their -own anonymous trackers without running bytemonsoon (which has substantial prerequisites) -or i2p-bt's tracker. The plugin is currently (July 2005) fully functional, but is in early -beta and has a fairly complicated configuration process, though it is hopefully going -to be streamlined further. -

- -

I2Phex

-

Developed by: sirup

- -

-I2Phex is a fairly direct port of the Phex Gnutella filesharing client to run -entirely on top of I2P. While it has disabled some of Phex's functionality, -such as integration with Gnutella webcaches, the basic file sharing and chatting -system is fully functional. -

- -

I2Pmail/susimail

-

Developed by: postman, susi23, mastiejaner

- -

-I2Pmail is more a service than an application - postman offers both internal and -external email with POP3 and SMTP service through I2PTunnel instances accessing a -series of components developed with mastiejaner, allowing people to use their -preferred mail clients to send and receive mail pseudonymously. However, as most -mail clients expose substantial identifying information, I2P bundles susi23's -web based susimail client which has been built specifically with I2P's anonymity -needs in mind. The I2Pmail/mail.i2p service offers transparent virus filtering as -well as denial of service prevention with hashcash augmented quotas. -In addition, each user has control of their batching strategy prior to delivery -through the mail.i2p outproxies, which are separate from the mail.i2p SMTP and -POP3 servers - both the outproxies and inproxies communicate with the mail.i2p -SMTP and POP3 servers through I2P itself, so compromising those non-anonymous -locations does not give access to the mail accounts or activity patterns of the -user. At the moment the developers work on a decentralized mailsystem, called -"v2mail". More information can be found on the eepsite -hq.postman.i2p. -

- - - diff --git a/router/doc/tunnel-alt-creation.html b/router/doc/tunnel-alt-creation.html deleted file mode 100644 index 0eb4a5d90..000000000 --- a/router/doc/tunnel-alt-creation.html +++ /dev/null @@ -1,163 +0,0 @@ -$Id: tunnel-alt-creation.html,v 1.1.2.1 2006/02/01 20:28:34 jrandom Exp $ -
-1) Tunnel creation
-1.1) Tunnel creation request record
-1.2) Hop processing
-1.3) Tunnel creation reply record
-1.4) Request preparation
-1.5) Request delivery
-1.6) Endpoint handling
-1.7) Reply processing
-2) Notes
-
- -

1) Tunnel creation encryption:

- -

The tunnel creation is accomplished by a single message passed along -the path of peers in the tunnel, rewritten in place, and transmitted -back to the tunnel creator. This single tunnel message is made up -of a fixed number of records (8) - one for each potential peer in -the tunnel. Individual records are asymmetrically encrypted to be -read only by a specific peer along the path, while an additional -symmetric layer of encryption is added at each hop so as to expose -the asymmetrically encrypted record only at the appropriate time.

- -

1.1) Tunnel creation request record

- -

Cleartext of the record, visible only to the hop being asked:

-  bytes     0-3: tunnel ID to receive messages as
-  bytes    4-35: local router identity hash
-  bytes   36-39: next tunnel ID
-  bytes   40-71: next router identity hash
-  bytes  72-103: AES-256 tunnel layer key
-  bytes 104-135: AES-256 tunnel IV key
-  bytes 136-167: AES-256 reply key
-  bytes 168-183: reply IV
-  byte      184: flags
-  bytes 185-188: request time (in hours since the epoch)
-  bytes 189-192: next message ID
-  bytes 193-222: uninterpreted / random padding
- -

The next tunnel ID and next router identity hash fields are used to -specify the next hop in the tunnel, though for an outbound tunnel -endpoint, they specify where the rewritten tunnel creation reply -message should be sent. In addition, the next message ID specifies the -message ID that the message (or reply) should use.

- -

The flags field currently has two bits defined:

- bit 0: if set, allow messages from anyone
- bit 1: if set, allow messages to anyone, and send the reply to the
-        specified next hop in a tunnel message
- -

That cleartext record is ElGamal 2048 encrypted with the hop's -public encryption key and formatted into a 528 byte record:

-  bytes   0-15: SHA-256-128 of the current hop's router identity
-  bytes 16-527: ElGamal-2048 encrypted request record
- -

Since the cleartext uses the full field, there is no need for -additional padding beyond SHA256(cleartext) + cleartext.

- -

1.2) Hop processing

- -

When a hop receives a TunnelBuildMessage, it looks through the 8 -records contained within it for one starting with their own identity -hash (trimmed to 8 bytes). It then decryptes the ElGamal block from -that record and retrieves the protected cleartext. At that point, -they make sure the tunnel request is not a duplicate by feeding the -AES-256 reply key into a bloom filter and making sure the request -time is within an hour of current. Duplicates or invalid requests -are dropped.

- -

After deciding whether they will agree to participate in the tunnel -or not, they replace the record that had contained the request with -an encrypted reply block. All other records are AES-256/CBC -encrypted with the included reply key and IV (though each is -encrypted separately, rather than chained across records).

- -

1.3) Tunnel creation reply record

- -

After the current hop reads their record, they replace it with a -reply record stating whether or not they agree to participate in the -tunnel, and if they do not, they classify their reason for -rejection. This is simply a 1 byte value, with 0x0 meaning they -agree to participate in the tunnel, and higher values meaning higher -levels of rejection. The reply is encrypted with the AES session -key delivered to it in the encrypted block, padded with random data -until it reaches the full record size:

-  AES-256-CBC(SHA-256(padding+status) + padding + status, key, IV)
- -

1.4) Request preparation

- -

When building a new request, all of the records must first be -built and asymmetrically encrypted. Each record should then be -decrypted with the reply keys and IVs of the hops earlier in the -path. That decryption should be run in reverse order so that the -asymmetrically encrypted data will show up in the clear at the -right hop after their predecessor encrypts it.

- -

The excess records not needed for individual requests are simply -filled with random data by the creator.

- -

1.5) Request delivery

- -

For outbound tunnels, the delivery is done directly from the tunnel -creator to the first hop, packaging up the TunnelBuildMessage as if -the creator was just another hop in the tunnel. For inbound -tunnels, the delivery is done through an existing outbound tunnel -(and during startup, when no outbound tunnel exists yet, a fake 0 -hop outbound tunnel is used).

- -

1.6) Endpoint handling

- -

When the request reaches an outbound endpoint (as determined by the -'allow messages to anyone' flag), the hop is processed as usual, -encrypting a reply in place of the record and encrypting all of the -other records, but since there is no 'next hop' to forward the -TunnelBuildMessage on to, it instead places the encrypted reply -records into a TunnelBuildReplyMessage and delivers it to the -reply tunnel specified within the request record. That reply tunnel -forwards the reply records down to the tunnel creator for -processing, as below.

- -

When the request reaches the inbound endpoint (also known as the -tunnel creator), the router processes each of the replies, as below.

- -

1.7) Reply processing

- -

To process the reply records, the creator simply has to AES decrypt -each record individually, using the reply key and IV of each hop in -the tunnel after the peer (in reverse order). This then exposes the -reply specifying whether they agree to participate in the tunnel or -why they refuse. If they all agree, the tunnel is considered -created and may be used immediately, but if anyone refuses, the -tunnel is discarded.

- -

2) Notes

-
    -
  • This does not prevent two hostile peers within a tunnel from -tagging one or more request or reply records to detect that they are -within the same tunnel, but doing so can be detected by the tunnel -creator when reading the reply, causing the tunnel to be marked as -invalid.
  • -
  • This does not include a proof of work on the asymmetrically -encrypted section, though the 16 byte identity hash could be cut in -half with the later replaced by a hashcash function of up to 2^64 -cost. This will not immediately be pursued, however.
  • -
  • This alone does not prevent two hostile peers within a tunnel from -using timing information to determine whether they are in the same -tunnel. The use of batched and synchronized request delivery -could help (batching up requests and sending them off on the -(ntp-synchronized) minute). However, doing so lets peers 'tag' the -requests by delaying them and detecting the delay later in the -tunnel, though perhaps dropping requests not delivered in a small -window would work (though doing that would require a high degree of -clock synchronization). Alternately, perhaps individual hops could -inject a random delay before forwarding on the request?
  • -
  • Are there any nonfatal methods of tagging the request?
  • -
  • This strategy came about during a discussion on the I2P mailing list - between Michael Rogers, Matthew Toseland (toad), and jrandom regarding - the predecessor attack. See:
  • -
diff --git a/router/doc/tunnel-alt.html b/router/doc/tunnel-alt.html deleted file mode 100644 index 2d5f2be2f..000000000 --- a/router/doc/tunnel-alt.html +++ /dev/null @@ -1,467 +0,0 @@ -$Id: tunnel-alt.html,v 1.9 2005/07/27 14:04:07 jrandom Exp $ -
-1) Tunnel overview
-2) Tunnel operation
-2.1) Message preprocessing
-2.2) Gateway processing
-2.3) Participant processing
-2.4) Endpoint processing
-2.5) Padding
-2.6) Tunnel fragmentation
-2.7) Alternatives
-2.7.1) Adjust tunnel processing midstream
-2.7.2) Use bidirectional tunnels
-2.7.3) Backchannel communication
-2.7.4) Variable size tunnel messages
-3) Tunnel building
-3.1) Peer selection
-3.1.1) Exploratory tunnel peer selection
-3.1.2) Client tunnel peer selection
-3.2) Request delivery
-3.3) Pooling
-3.4) Alternatives
-3.4.1) Telescopic building
-3.4.2) Non-exploratory tunnels for management
-4) Tunnel throttling
-5) Mixing/batching
-
- -

1) Tunnel overview

- -

Within I2P, messages are passed in one direction through a virtual -tunnel of peers, using whatever means are available to pass the -message on to the next hop. Messages arrive at the tunnel's -gateway, get bundled up and/or fragmented into fixed sizes tunnel messages, -and are forwarded on to the next hop in the tunnel, which processes and verifies -the validity of the message and sends it on to the next hop, and so on, until -it reaches the tunnel endpoint. That endpoint takes the messages -bundled up by the gateway and forwards them as instructed - either -to another router, to another tunnel on another router, or locally.

- -

Tunnels all work the same, but can be segmented into two different -groups - inbound tunnels and outbound tunnels. The inbound tunnels -have an untrusted gateway which passes messages down towards the -tunnel creator, which serves as the tunnel endpoint. For outbound -tunnels, the tunnel creator serves as the gateway, passing messages -out to the remote endpoint.

- -

The tunnel's creator selects exactly which peers will participate -in the tunnel, and provides each with the necessary configuration -data. They may have any number of hops, but may be constrained with various -proof-of-work requests to add on additional steps. It is the intent to make -it hard for either participants or third parties to determine the length of -a tunnel, or even for colluding participants to determine whether they are a -part of the same tunnel at all (barring the situation where colluding peers are -next to each other in the tunnel).

- -

Beyond their length, there are additional configurable parameters -for each tunnel that can be used, such as a throttle on the frequency of -messages delivered, how padding should be used, how long a tunnel should be -in operation, whether to inject chaff messages, and what, if any, batching -strategies should be employed.

- -

In practice, a series of tunnel pools are used for different -purposes - each local client destination has its own set of inbound -tunnels and outbound tunnels, configured to meet its anonymity and -performance needs. In addition, the router itself maintains a series -of pools for participating in the network database and for managing -the tunnels themselves.

- -

I2P is an inherently packet switched network, even with these -tunnels, allowing it to take advantage of multiple tunnels running -in parallel, increasing resilience and balancing load. Outside of -the core I2P layer, there is an optional end to end streaming library -available for client applications, exposing TCP-esque operation, -including message reordering, retransmission, congestion control, etc.

- -

2) Tunnel operation

- -

Tunnel operation has four distinct processes, taken on by various -peers in the tunnel. First, the tunnel gateway accumulates a number -of tunnel messages and preprocesses them into something for tunnel -delivery. Next, that gateway encrypts that preprocessed data, then -forwards it to the first hop. That peer, and subsequent tunnel -participants, unwrap a layer of the encryption, verifying that it isn't -a duplicate, then forward it on to the next peer. -Eventually, the message arrives at the endpoint where the messages -bundled by the gateway are split out again and forwarded on as -requested.

- -

Tunnel IDs are 4 byte numbers used at each hop - participants know what -tunnel ID to listen for messages with and what tunnel ID they should be forwarded -on as to the next hop, and each hop chooses the tunnel ID which they receive messages -on. Tunnels themselves are short lived (10 minutes at the -moment), and even if subsequent tunnels are built using the same sequence of -peers, each hop's tunnel ID will change.

- -

2.1) Message preprocessing

- -

When the gateway wants to deliver data through the tunnel, it first -gathers zero or more I2NP messages, selects how much padding will be used, -fragments it across the necessary number of 1KB tunnel messages, and decides how -each I2NP message should be handled by the tunnel endpoint, encoding that -data into the raw tunnel payload:

-
    -
  • the first 4 bytes of the SHA256 of the remaining preprocessed data concatenated - with the IV, using the IV as will be seen on the tunnel endpoint (for - outbound tunnels) or the IV as was seen on the tunnel gateway (for inbound - tunnels) (see below for IV processing).
  • -
  • 0 or more bytes containing random nonzero integers
  • -
  • 1 byte containing 0x00
  • -
  • a series of zero or more { instructions, message } pairs
  • -
- -

The instructions are encoded with a single control byte, followed by any -necessary additional information. The first bit in that control byte determines -how the remainder of the header is interpreted - if it is not set, the message -is either not fragmented or this is the first fragment in the message. If it is -set, this is a follow on fragment.

- -

With the first bit being 0, the instructions are:

-
    -
  • 1 byte control byte:
    -      bit 0: is follow on fragment?  (1 = true, 0 = false, must be 0)
    -   bits 1-2: delivery type
    -             (0x0 = LOCAL, 0x01 = TUNNEL, 0x02 = ROUTER)
    -      bit 3: delay included?  (1 = true, 0 = false)
    -      bit 4: fragmented?  (1 = true, 0 = false)
    -      bit 5: extended options?  (1 = true, 0 = false)
    -   bits 6-7: reserved
  • -
  • if the delivery type was TUNNEL, a 4 byte tunnel ID
  • -
  • if the delivery type was TUNNEL or ROUTER, a 32 byte router hash
  • -
  • if the delay included flag is true, a 1 byte value:
    -      bit 0: type (0 = strict, 1 = randomized)
    -   bits 1-7: delay exponent (2^value minutes)
  • -
  • if the fragmented flag is true, a 4 byte message ID
  • -
  • if the extended options flag is true:
    -   = a 1 byte option size (in bytes)
    -   = that many bytes
  • -
  • 2 byte size of the I2NP message or this fragment
  • -
- -

If the first bit being 1, the instructions are:

-
    -
  • 1 byte control byte:
    -      bit 0: is follow on fragment?  (1 = true, 0 = false, must be 1)
    -   bits 1-6: fragment number
    -      bit 7: is last? (1 = true, 0 = false)
  • -
  • 4 byte message ID (same one defined in the first fragment)
  • -
  • 2 byte size of this fragment
  • -
- -

The I2NP message is encoded in its standard form, and the -preprocessed payload must be padded to a multiple of 16 bytes.

- -

2.2) Gateway processing

- -

After the preprocessing of messages into a padded payload, the gateway builds -a random 16 byte IV value, iteratively encrypting it and the tunnel message as -necessary, and forwards the tuple {tunnelID, IV, encrypted tunnel message} to the next hop.

- -

How encryption at the gateway is done depends on whether the tunnel is an -inbound or an outbound tunnel. For inbound tunnels, they simply select a random -IV, postprocessing and updating it to generate the IV for the gateway and using -that IV along side their own layer key to encrypt the preprocessed data. For outbound -tunnels they must iteratively decrypt the (unencrypted) IV and preprocessed -data with the IV and layer keys for all hops in the tunnel. The result of the outbound -tunnel encryption is that when each peer encrypts it, the endpoint will recover -the initial preprocessed data.

- -

2.3) Participant processing

- -

When a peer receives a tunnel message, it checks that the message came from -the same previous hop as before (initialized when the first message comes through -the tunnel). If the previous peer is a different router, or if the message has -already been seen, the message is dropped. The participant then encrypts the -received IV with AES256/ECB using their IV key to determine the current IV, uses -that IV with the participant's layer key to encrypt the data, encrypts the -current IV with AES256/ECB using their IV key again, then forwards the tuple -{nextTunnelId, nextIV, encryptedData} to the next hop. This double encryption -of the IV (both before and after use) help address a certain class of -confirmation attacks.

- -

Duplicate message detection is handled by a decaying Bloom filter on message -IVs. Each router maintains a single Bloom filter to contain the XOR of the IV and -the first block of the message received for all of the tunnels it is participating -in, modified to drop seen entries after 10-20 minutes (when the tunnels will have -expired). The size of the bloom filter and the parameters used are sufficient to -more than saturate the router's network connection with a negligible chance of -false positive. The unique value fed into the Bloom filter is the XOR of the IV -and the first block so as to prevent nonsequential colluding peers in the tunnel -from tagging a message by resending it with the IV and first block switched.

- -

2.4) Endpoint processing

- -

After receiving and validating a tunnel message at the last hop in the tunnel, -how the endpoint recovers the data encoded by the gateway depends upon whether -the tunnel is an inbound or an outbound tunnel. For outbound tunnels, the -endpoint encrypts the message with its layer key just like any other participant, -exposing the preprocessed data. For inbound tunnels, the endpoint is also the -tunnel creator so they can merely iteratively decrypt the IV and message, using the -layer and IV keys of each step in reverse order.

- -

At this point, the tunnel endpoint has the preprocessed data sent by the gateway, -which it may then parse out into the included I2NP messages and forwards them as -requested in their delivery instructions.

- -

2.5) Padding

- -

Several tunnel padding strategies are possible, each with their own merits:

- -
    -
  • No padding
  • -
  • Padding to a random size
  • -
  • Padding to a fixed size
  • -
  • Padding to the closest KB
  • -
  • Padding to the closest exponential size (2^n bytes)
  • -
- -

These padding strategies can be used on a variety of levels, addressing the -exposure of message size information to different adversaries. After gathering -and reviewing some statistics -from the 0.4 network, as well as exploring the anonymity tradeoffs, we're starting -with a fixed tunnel message size of 1024 bytes. Within this however, the fragmented -messages themselves are not padded by the tunnel at all (though for end to end -messages, they may be padded as part of the garlic wrapping).

- -

2.6) Tunnel fragmentation

- -

To prevent adversaries from tagging the messages along the path by adjusting -the message size, all tunnel messages are a fixed 1024 bytes in size. To accommodate -larger I2NP messages as well as to support smaller ones more efficiently, the -gateway splits up the larger I2NP messages into fragments contained within each -tunnel message. The endpoint will attempt to rebuild the I2NP message from the -fragments for a short period of time, but will discard them as necessary.

- -

Routers have a lot of leeway as to how the fragments are arranged, whether -they are stuffed inefficiently as discrete units, batched for a brief period to -fit more payload into the 1024 byte tunnel messages, or opportunistically padded -with other messages that the gateway wanted to send out.

- -

2.7) Alternatives

- -

2.7.1) Adjust tunnel processing midstream

- -

While the simple tunnel routing algorithm should be sufficient for most cases, -there are three alternatives that can be explored:

-
    -
  • Have a peer other than the endpoint temporarily act as the termination -point for a tunnel by adjusting the encryption used at the gateway to give them -the plaintext of the preprocessed I2NP messages. Each peer could check to see -whether they had the plaintext, processing the message when received as if they -did.
  • -
  • Allow routers participating in a tunnel to remix the message before -forwarding it on - bouncing it through one of that peer's own outbound tunnels, -bearing instructions for delivery to the next hop.
  • -
  • Implement code for the tunnel creator to redefine a peer's "next hop" in -the tunnel, allowing further dynamic redirection.
  • -
- -

2.7.2) Use bidirectional tunnels

- -

The current strategy of using two separate tunnels for inbound and outbound -communication is not the only technique available, and it does have anonymity -implications. On the positive side, by using separate tunnels it lessens the -traffic data exposed for analysis to participants in a tunnel - for instance, -peers in an outbound tunnel from a web browser would only see the traffic of -an HTTP GET, while the peers in an inbound tunnel would see the payload -delivered along the tunnel. With bidirectional tunnels, all participants would -have access to the fact that e.g. 1KB was sent in one direction, then 100KB -in the other. On the negative side, using unidirectional tunnels means that -there are two sets of peers which need to be profiled and accounted for, and -additional care must be taken to address the increased speed of predecessor -attacks. The tunnel pooling and building process outlined below should -minimize the worries of the predecessor attack, though if it were desired, -it wouldn't be much trouble to build both the inbound and outbound tunnels -along the same peers.

- -

2.7.3) Backchannel communication

- -

At the moment, the IV values used are random values. However, it is -possible for that 16 byte value to be used to send control messages from the -gateway to the endpoint, or on outbound tunnels, from the gateway to any of the -peers. The inbound gateway could encode certain values in the IV once, which -the endpoint would be able to recover (since it knows the endpoint is also the -creator). For outbound tunnels, the creator could deliver certain values to the -participants during the tunnel creation (e.g. "if you see 0x0 as the IV, that -means X", "0x1 means Y", etc). Since the gateway on the outbound tunnel is also -the creator, they can build a IV so that any of the peers will receive the -correct value. The tunnel creator could even give the inbound tunnel gateway -a series of IV values which that gateway could use to communicate with -individual participants exactly one time (though this would have issues regarding -collusion detection)

- -

This technique could later be used deliver message mid stream, or to allow the -inbound gateway to tell the endpoint that it is being DoS'ed or otherwise soon -to fail. At the moment, there are no plans to exploit this backchannel.

- -

2.7.4) Variable size tunnel messages

- -

While the transport layer may have its own fixed or variable message size, -using its own fragmentation, the tunnel layer may instead use variable size -tunnel messages. The difference is an issue of threat models - a fixed size -at the transport layer helps reduce the information exposed to external -adversaries (though overall flow analysis still works), but for internal -adversaries (aka tunnel participants) the message size is exposed. Fixed size -tunnel messages help reduce the information exposed to tunnel participants, but -does not hide the information exposed to tunnel endpoints and gateways. Fixed -size end to end messages hide the information exposed to all peers in the -network.

- -

As always, its a question of who I2P is trying to protect against. Variable -sized tunnel messages are dangerous, as they allow participants to use the -message size itself as a backchannel to other participants - e.g. if you see a -1337 byte message, you're on the same tunnel as another colluding peer. Even -with a fixed set of allowable sizes (1024, 2048, 4096, etc), that backchannel -still exists as peers could use the frequency of each size as the carrier (e.g. -two 1024 byte messages followed by an 8192). Smaller messages do incur the -overhead of the headers (IV, tunnel ID, hash portion, etc), but larger fixed size -messages either increase latency (due to batching) or dramatically increase -overhead (due to padding). Fragmentation helps ammortize the overhead, at the -cost of potential message loss due to lost fragments.

- -

Timing attacks are also relevent when reviewing the effectiveness of fixed -size messages, though they require a substantial view of network activity -patterns to be effective. Excessive artificial delays in the tunnel will be -detected by the tunnel's creator, due to periodic testing, causing that entire -tunnel to be scrapped and the profiles for peers within it to be adjusted.

- -

3) Tunnel building

- -

When building a tunnel, the creator must send a request with the necessary -configuration data to each of the hops and wait for all of them to agree before -enabling the tunnel. The requests are encrypted so that only the peers who need -to know a piece of information (such as the tunnel layer or IV key) has that -data. In addition, only the tunnel creator will have access to the peer's -reply. There are three important dimensions to keep in mind when producing -the tunnels: what peers are used (and where), how the requests are sent (and -replies received), and how they are maintained.

- -

3.1) Peer selection

- -

Beyond the two types of tunnels - inbound and outbound - there are two styles -of peer selection used for different tunnels - exploratory and client. -Exploratory tunnels are used for both network database maintenance and tunnel -maintenance, while client tunnels are used for end to end client messages.

- -

3.1.1) Exploratory tunnel peer selection

- -

Exploratory tunnels are built out of a random selection of peers from a subset -of the network. The particular subset varies on the local router and on what their -tunnel routing needs are. In general, the exploratory tunnels are built out of -randomly selected peers who are in the peer's "not failing but active" profile -category. The secondary purpose of the tunnels, beyond merely tunnel routing, -is to find underutilized high capacity peers so that they can be promoted for -use in client tunnels.

- -

3.1.2) Client tunnel peer selection

- -

Client tunnels are built with a more stringent set of requirements - the local -router will select peers out of its "fast and high capacity" profile category so -that performance and reliability will meet the needs of the client application. -However, there are several important details beyond that basic selection that -should be adhered to, depending upon the client's anonymity needs.

- -

For some clients who are worried about adversaries mounting a predecessor -attack, the tunnel selection can keep the peers selected in a strict order - -if A, B, and C are in a tunnel, the hop after A is always B, and the hop after -B is always C. A less strict ordering is also possible, assuring that while -the hop after A may be B, B may never be before A. Other configuration options -include the ability for just the inbound tunnel gateways and outbound tunnel -endpoints to be fixed, or rotated on an MTBF rate.

- -

In the initial implementation, only random ordering has been implemented, -though more strict ordering will be developed and deployed over time, as well -as controls for the user to select which strategy to use for individual clients.

- -

3.2) Request delivery

- -

A new tunnel request preparation, delivery, and response method has been -devised, which reduces the number of -predecessors exposed, cuts the number of messages transmitted, verifies proper -connectivity, and avoids the message counting attack of traditional telescopic -tunnel creation. The old technique is listed below as an alternative.

- -

Peers may reject tunnel creation requests for a variety of reasons, though -a series of four increasingly severe rejections are known: probabalistic rejection -(due to approaching the router's capacity, or in response to a flood of requests), -transient overload, bandwidth overload, and critical failure. When received, -those four are interpreted by the tunnel creator to help adjust their profile of -the router in question.

- -

3.3) Pooling

- -

To allow efficient operation, the router maintains a series of tunnel pools, -each managing a group of tunnels used for a specific purpose with their own -configuration. When a tunnel is needed for that purpose, the router selects one -out of the appropriate pool at random. Overall, there are two exploratory tunnel -pools - one inbound and one outbound - each using the router's exploration -defaults. In addition, there is a pair of pools for each local destination - -one inbound and one outbound tunnel. Those pools use the configuration specified -when the local destination connected to the router, or the router's defaults if -not specified.

- -

Each pool has within its configuration a few key settings, defining how many -tunnels to keep active, how many backup tunnels to maintain in case of failure, -how frequently to test the tunnels, how long the tunnels should be, whether those -lengths should be randomized, how often replacement tunnels should be built, as -well as any of the other settings allowed when configuring individual tunnels.

- -

3.4) Alternatives

- -

3.4.1) Telescopic building

- -

One question that may arise regarding the use of the exploratory tunnels for -sending and receiving tunnel creation messages is how that impacts the tunnel's -vulnerability to predecessor attacks. While the endpoints and gateways of -those tunnels will be randomly distributed across the network (perhaps even -including the tunnel creator in that set), another alternative is to use the -tunnel pathways themselves to pass along the request and response, as is done -in TOR. This, however, may lead to leaks -during tunnel creation, allowing peers to discover how many hops there are later -on in the tunnel by monitoring the timing or packet count as -the tunnel is built.

- -

3.4.2) Non-exploratory tunnels for management

- -

A second alternative to the tunnel building process is to give the router -an additional set of non-exploratory inbound and outbound pools, using those for -the tunnel request and response. Assuming the router has a well integrated view -of the network, this should not be necessary, but if the router was partitioned -in some way, using non-exploratory pools for tunnel management would reduce the -leakage of information about what peers are in the router's partition.

- -

3.4.3) Exploratory request delivery

- -

A third alternative, used until I2P 0.6.2, garlic encrypts individual tunnel -request messages and delivers them to the hops individually, transmitting them -through exploratory tunnels with their reply coming back in a separate -exploratory tunnel. This strategy has been dropped in favor of the one outlined -above.

- -

4) Tunnel throttling

- -

Even though the tunnels within I2P bear a resemblance to a circuit switched -network, everything within I2P is strictly message based - tunnels are merely -accounting tricks to help organize the delivery of messages. No assumptions are -made regarding reliability or ordering of messages, and retransmissions are left -to higher levels (e.g. I2P's client layer streaming library). This allows I2P -to take advantage of throttling techniques available to both packet switched and -circuit switched networks. For instance, each router may keep track of the -moving average of how much data each tunnel is using, combine that with all of -the averages used by other tunnels the router is participating in, and be able -to accept or reject additional tunnel participation requests based on its -capacity and utilization. On the other hand, each router can simply drop -messages that are beyond its capacity, exploiting the research used on the -normal internet.

- -

5) Mixing/batching

- -

What strategies should be used at the gateway and at each hop for delaying, -reordering, rerouting, or padding messages? To what extent should this be done -automatically, how much should be configured as a per tunnel or per hop setting, -and how should the tunnel's creator (and in turn, user) control this operation? -All of this is left as unknown, to be worked out for -I2P 3.0

diff --git a/router/doc/tunnel.html b/router/doc/tunnel.html deleted file mode 100644 index a15d19c12..000000000 --- a/router/doc/tunnel.html +++ /dev/null @@ -1,529 +0,0 @@ -Note: NOT used! see tunnel-alt.html - -$Id: tunnel.html,v 1.10 2005/01/16 01:07:07 jrandom Exp $ -
-1) Tunnel overview
-2) Tunnel operation
-2.1) Message preprocessing
-2.2) Gateway processing
-2.3) Participant processing
-2.4) Endpoint processing
-2.5) Padding
-2.6) Tunnel fragmentation
-2.7) Alternatives
-2.7.1) Don't use a checksum block
-2.7.2) Adjust tunnel processing midstream
-2.7.3) Use bidirectional tunnels
-2.7.4) Use smaller hashes
-3) Tunnel building
-3.1) Peer selection
-3.1.1) Exploratory tunnel peer selection
-3.1.2) Client tunnel peer selection
-3.2) Request delivery
-3.3) Pooling
-3.4) Alternatives
-3.4.1) Telescopic building
-3.4.2) Non-exploratory tunnels for management
-4) Tunnel throttling
-5) Mixing/batching
-
- -

1) Tunnel overview

- -

Within I2P, messages are passed in one direction through a virtual -tunnel of peers, using whatever means are available to pass the -message on to the next hop. Messages arrive at the tunnel's -gateway, get bundled up for the path, and are forwarded on to the -next hop in the tunnel, which processes and verifies the validity -of the message and sends it on to the next hop, and so on, until -it reaches the tunnel endpoint. That endpoint takes the messages -bundled up by the gateway and forwards them as instructed - either -to another router, to another tunnel on another router, or locally.

- -

Tunnels all work the same, but can be segmented into two different -groups - inbound tunnels and outbound tunnels. The inbound tunnels -have an untrusted gateway which passes messages down towards the -tunnel creator, which serves as the tunnel endpoint. For outbound -tunnels, the tunnel creator serves as the gateway, passing messages -out to the remote endpoint.

- -

The tunnel's creator selects exactly which peers will participate -in the tunnel, and provides each with the necessary confiruration -data. They may vary in length from 0 hops (where the gateway -is also the endpoint) to 8 hops (where there are 6 peers after -the gateway and before the endpoint). It is the intent to make -it hard for either participants or third parties to determine -the length of a tunnel, or even for colluding participants to -determine whether they are a part of the same tunnel at all -(barring the situation where colluding peers are next to each other -in the tunnel). Messages that have been corrupted are also dropped -as soon as possible, reducing network load.

- -

Beyond their length, there are additional configurable parameters -for each tunnel that can be used, such as a throttle on the size or -frequency of messages delivered, how padding should be used, how -long a tunnel should be in operation, whether to inject chaff -messages, whether to use fragmentation, and what, if any, batching -strategies should be employed.

- -

In practice, a series of tunnel pools are used for different -purposes - each local client destination has its own set of inbound -tunnels and outbound tunnels, configured to meet its anonymity and -performance needs. In addition, the router itself maintains a series -of pools for participating in the network database and for managing -the tunnels themselves.

- -

I2P is an inherently packet switched network, even with these -tunnels, allowing it to take advantage of multiple tunnels running -in parallel, increasing resiliance and balancing load. Outside of -the core I2P layer, there is an optional end to end streaming library -available for client applications, exposing TCP-esque operation, -including message reordering, retransmission, congestion control, etc.

- -

2) Tunnel operation

- -

Tunnel operation has four distinct processes, taken on by various -peers in the tunnel. First, the tunnel gateway accumulates a number -of tunnel messages and preprocesses them into something for tunnel -delivery. Next, that gateway encrypts that preprocessed data, then -forwards it to the first hop. That peer, and subsequent tunnel -participants, unwrap a layer of the encryption, verifying the -integrity of the message, then forward it on to the next peer. -Eventually, the message arrives at the endpoint where the messages -bundled by the gateway are split out again and forwarded on as -requested.

- -

Tunnel IDs are 4 byte numbers used at each hop - participants know what -tunnel ID to listen for messages with and what tunnel ID they should be forwarded -on as to the next hop. Tunnels themselves are short lived (10 minutes at the -moment), but depending upon the tunnel's purpose, and though subsequent tunnels -may be built using the same sequence of peers, each hop's tunnel ID will change.

- -

2.1) Message preprocessing

- -

When the gateway wants to deliver data through the tunnel, it first -gathers zero or more I2NP messages (no more than 32KB worth), -selects how much padding will be used, and decides how each I2NP -message should be handled by the tunnel endpoint, encoding that -data into the raw tunnel payload:

-
    -
  • 2 byte unsigned integer specifying the # of padding bytes
  • -
  • that many random bytes
  • -
  • a series of zero or more { instructions, message } pairs
  • -
- -

The instructions are encoded as follows:

-
    -
  • 1 byte value:
    -   bits 0-1: delivery type
    -             (0x0 = LOCAL, 0x01 = TUNNEL, 0x02 = ROUTER)
    -      bit 2: delay included?  (1 = true, 0 = false)
    -      bit 3: fragmented?  (1 = true, 0 = false)
    -      bit 4: extended options?  (1 = true, 0 = false)
    -   bits 5-7: reserved
  • -
  • if the delivery type was TUNNEL, a 4 byte tunnel ID
  • -
  • if the delivery type was TUNNEL or ROUTER, a 32 byte router hash
  • -
  • if the delay included flag is true, a 1 byte value:
    -      bit 0: type (0 = strict, 1 = randomized)
    -   bits 1-7: delay exponent (2^value minutes)
  • -
  • if the fragmented flag is true, a 4 byte message ID, and a 1 byte value:
    -   bits 0-6: fragment number
    -      bit 7: is last?  (1 = true, 0 = false)
  • -
  • if the extended options flag is true:
    -   = a 1 byte option size (in bytes)
    -   = that many bytes
  • -
  • 2 byte size of the I2NP message
  • -
- -

The I2NP message is encoded in its standard form, and the -preprocessed payload must be padded to a multiple of 16 bytes.

- -

2.2) Gateway processing

- -

After the preprocessing of messages into a padded payload, the gateway -encrypts the payload with the eight keys, building a checksum block so -that each peer can verify the integrity of the payload at any time, as -well as an end to end verification block for the tunnel endpoint to -verify the integrity of the checksum block. The specific details follow.

- -

The encryption used is such that decryption -merely requires running over the data with AES in CBC mode, calculating the -SHA256 of a certain fixed portion of the message (bytes 16 through $size-144), -and searching for the first 16 bytes of that hash in the checksum block. There is a fixed number -of hops defined (8 peers) so that we can verify the message -without either leaking the position in the tunnel or having the message -continually "shrink" as layers are peeled off. For tunnels shorter than 8 -hops, the tunnel creator will take the place of the excess hops, decrypting -with their keys (for outbound tunnels, this is done at the beginning, and for -inbound tunnels, the end).

- -

The hard part in the encryption is building that entangled checksum block, -which requires essentially finding out what the hash of the payload will look -like at each step, randomly ordering those hashes, then building a matrix of -what each of those randomly ordered hashes will look like at each step. The -gateway itself must pretend that it is one of the peers within the checksum -block so that the first hop cannot tell that the previous hop was the gateway. -To visualize this a bit:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IVPayloadeH[0]eH[1]eH[2]eH[3]eH[4]eH[5]eH[6]eH[7]V
peer0
key=K[0]
recv
sendIV[0]P[0]H(P[0])V[0]
peer1
key=K[1]
recv
sendIV[1]P[1]H(P[1])V[1]
peer2
key=K[2]
recv
sendIV[2]P[2]H(P[2])V[2]
peer3
key=K[3]
recv
sendIV[3]P[3]H(P[3])V[3]
peer4
key=K[4]
recv
sendIV[4]P[4]H(P[4])V[4]
peer5
key=K[5]
recv
sendIV[5]P[5]H(P[5])V[5]
peer6
key=K[6]
recv
sendIV[6]P[6]H(P[6])V[6]
peer7
key=K[7]
recv
sendIV[7]P[7]H(P[7])V[7]
- -

In the above, P[7] is the same as the original data being passed through the -tunnel (the preprocessed messages), and V[7] is the first 16 bytes of the SHA256 of eH[0-7] as seen on -peer7 after decryption. For -cells in the matrix "higher up" than the hash, their value is derived by encrypting -the cell below it with the key for the peer below it, using the end of the column -to the left of it as the IV. For cells in the matrix "lower down" than the hash, -they're equal to the cell above them, decrypted by the current peer's key, using -the end of the previous encrypted block on that row.

- -

With this randomized matrix of checksum blocks, each peer will be able to find -the hash of the payload, or if it is not there, know that the message is corrupt. -The entanglement by using CBC mode increases the difficulty in tagging the -checksum blocks themselves, but it is still possible for that tagging to go -briefly undetected if the columns after the tagged data have already been used -to check the payload at a peer. In any case, the tunnel endpoint (peer 7) knows -for certain whether any of the checksum blocks have been tagged, as that would -corrupt the verification block (V[7]).

- -

The IV[0] is a random 16 byte value, and IV[i] is the first 16 bytes of -H(D(IV[i-1], K[i-1]) xor IV_WHITENER). We don't use the same IV along the path, as that would -allow trivial collusion, and we use the hash of the decrypted value to propogate -the IV so as to hamper key leakage. IV_WHITENER is a fixed 16 byte value.

- -

When the gateway wants to send the message, they export the right row for the -peer who is the first hop (usually the peer1.recv row) and forward that entirely.

- -

2.3) Participant processing

- -

When a participant in a tunnel receives a message, they decrypt a layer with their -tunnel key using AES256 in CBC mode with the first 16 bytes as the IV. They then -calculate the hash of what they see as the payload (bytes 16 through $size-144) and -search for that first 16 bytes of that hash within the decrypted checksum block. If no match is found, the -message is discarded. Otherwise, the IV is updated by decrypting it, XORing that value -with the IV_WHITENER, and replacing it with the first 16 bytes of its hash. The -resulting message is then forwarded on to the next peer for processing.

- -

To prevent replay attacks at the tunnel level, each participant keeps track of -the IVs received during the tunnel's lifetime, rejecting duplicates. The memory -usage required should be minor, as each tunnel has only a very short lifespan (10m -at the moment). A constant 100KBps through a tunnel with full 32KB messages would -give 1875 messages, requiring less than 30KB of memory. Gateways and endpoints -handle replay by tracking the message IDs and expirations on the I2NP messages -contained in the tunnel.

- -

2.4) Endpoint processing

- -

When a message reaches the tunnel endpoint, they decrypts and verifies it like -a normal participant. If the checksum block has a valid match, the endpoint then -computes the hash of the checksum block itself (as seen after decryption) and compares -that to the decrypted verification hash (the last 16 bytes). If that verification -hash does not match, the endpoint takes note of the tagging attempt by one of the -tunnel participants and perhaps discards the message.

- -

At this point, the tunnel endpoint has the preprocessed data sent by the gateway, -which it may then parse out into the included I2NP messages and forwards them as -requested in their delivery instructions.

- -

2.5) Padding

- -

Several tunnel padding strategies are possible, each with their own merits:

- -
    -
  • No padding
  • -
  • Padding to a random size
  • -
  • Padding to a fixed size
  • -
  • Padding to the closest KB
  • -
  • Padding to the closest exponential size (2^n bytes)
  • -
- -

Which to use? no padding is most efficient, random padding is what -we have now, fixed size would either be an extreme waste or force us to -implement fragmentation. Padding to the closest exponential size (ala freenet) -seems promising. Perhaps we should gather some stats on the net as to what size -messages are, then see what costs and benefits would arise from different -strategies?

- -

2.6) Tunnel fragmentation

- -

For various padding and mixing schemes, it may be useful from an anonymity -perspective to fragment a single I2NP message into multiple parts, each delivered -seperately through different tunnel messages. The endpoint may or may not -support that fragmentation (discarding or hanging on to fragments as needed), -and handling fragmentation will not immediately be implemented.

- -

2.7) Alternatives

- -

2.7.1) Don't use a checksum block

- -

One alternative to the above process is to remove the checksum block -completely and replace the verification hash with a plain hash of the payload. -This would simplify processing at the tunnel gateway and save 144 bytes of -bandwidth at each hop. On the other hand, attackers within the tunnel could -trivially adjust the message size to one which is easily traceable by -colluding external observers in addition to later tunnel participants. The -corruption would also incur the waste of the entire bandwidth necessary to -pass on the message. Without the per-hop validation, it would also be possible -to consume excess network resources by building extremely long tunnels, or by -building loops into the tunnel.

- -

2.7.2) Adjust tunnel processing midstream

- -

While the simple tunnel routing algorithm should be sufficient for most cases, -there are three alternatives that can be explored:

-
    -
  • Delay a message within a tunnel at an arbitrary hop for either a specified -amount of time or a randomized period. This could be achieved by replacing the -hash in the checksum block with e.g. the first 8 bytes of the hash, followed by -some delay instructions. Alternately, the instructions could tell the -participant to actually interpret the raw payload as it is, and either discard -the message or continue to forward it down the path (where it would be -interpreted by the endpoint as a chaff message). The later part of this would -require the gateway to adjust its encryption algorithm to produce the cleartext -payload on a different hop, but it shouldn't be much trouble.
  • -
  • Allow routers participating in a tunnel to remix the message before -forwarding it on - bouncing it through one of that peer's own outbound tunnels, -bearing instructions for delivery to the next hop. This could be used in either -a controlled manner (with en-route instructions like the delays above) or -probabalistically.
  • -
  • Implement code for the tunnel creator to redefine a peer's "next hop" in -the tunnel, allowing further dynamic redirection.
  • -
- -

2.7.3) Use bidirectional tunnels

- -

The current strategy of using two seperate tunnels for inbound and outbound -communication is not the only technique available, and it does have anonymity -implications. On the positive side, by using separate tunnels it lessens the -traffic data exposed for analysis to participants in a tunnel - for instance, -peers in an outbound tunnel from a web browser would only see the traffic of -an HTTP GET, while the peers in an inbound tunnel would see the payload -delivered along the tunnel. With bidirectional tunnels, all participants would -have access to the fact that e.g. 1KB was sent in one direction, then 100KB -in the other. On the negative side, using unidirectional tunnels means that -there are two sets of peers which need to be profiled and accounted for, and -additional care must be taken to address the increased speed of predecessor -attacks. The tunnel pooling and building process outlined below should -minimize the worries of the predecessor attack, though if it were desired, -it wouldn't be much trouble to build both the inbound and outbound tunnels -along the same peers.

- -

2.7.4) Use smaller blocksize

- -

At the moment, our use of AES limits our block size to 16 bytes, which -in turn provides the minimum size for each of the checksum block columns. -If another algorithm was used with a smaller block size, or could otherwise -allow the safe building of the checksum block with smaller portions of the -hash, it might be worth exploring. The 16 bytes used now at each hop should -be more than sufficient.

- -

3) Tunnel building

- -

When building a tunnel, the creator must send a request with the necessary -configuration data to each of the hops, then wait for the potential participant -to reply stating that they either agree or do not agree. These tunnel request -messages and their replies are garlic wrapped so that only the router who knows -the key can decrypt it, and the path taken in both directions is tunnel routed -as well. There are three important dimensions to keep in mind when producing -the tunnels: what peers are used (and where), how the requests are sent (and -replies received), and how they are maintained.

- -

3.1) Peer selection

- -

Beyond the two types of tunnels - inbound and outbound - there are two styles -of peer selection used for different tunnels - exploratory and client. -Exploratory tunnels are used for both network database maintenance and tunnel -maintenance, while client tunnels are used for end to end client messages.

- -

3.1.1) Exploratory tunnel peer selection

- -

Exploratory tunnels are built out of a random selection of peers from a subset -of the network. The particular subset varies on the local router and on what their -tunnel routing needs are. In general, the exploratory tunnels are built out of -randomly selected peers who are in the peer's "not failing but active" profile -category. The secondary purpose of the tunnels, beyond merely tunnel routing, -is to find underutilized high capacity peers so that they can be promoted for -use in client tunnels.

- -

3.1.2) Client tunnel peer selection

- -

Client tunnels are built with a more stringent set of requirements - the local -router will select peers out of its "fast and high capacity" profile category so -that performance and reliability will meet the needs of the client application. -However, there are several important details beyond that basic selection that -should be adhered to, depending upon the client's anonymity needs.

- -

For some clients who are worried about adversaries mounting a predecessor -attack, the tunnel selection can keep the peers selected in a strict order - -if A, B, and C are in a tunnel, the hop after A is always B, and the hop after -B is always C. A less strict ordering is also possible, assuring that while -the hop after A may be B, B may never be before A. Other configuration options -include the ability for just the inbound tunnel gateways and outbound tunnel -endpoints to be fixed, or rotated on an MTBF rate.

- -

3.2) Request delivery

- -

As mentioned above, once the tunnel creator knows what peers should go into -a tunnel and in what order, the creator builds a series of tunnel request -messages, each containing the necessary information for that peer. For instance, -participating tunnels will be given the 4 byte tunnel ID on which they are to -receive messages, the 4 byte tunnel ID on which they are to send out the messages, -the 32 byte hash of the next hop's identity, and the 32 byte layer key used to -remove a layer from the tunnel. Of course, outbound tunnel endpoints are not -given any "next hop" or "next tunnel ID" information. Inbound tunnel gateways -are however given the 8 layer keys in the order they should be encrypted (as -described above). To allow replies, the request contains a random session tag -and a random session key with which the peer may garlic encrypt their decision, -as well as the tunnel to which that garlic should be sent. In addition to the -above information, various client specific options may be included, such as -what throttling to place on the tunnel, what padding or batch strategies to use, -etc.

- -

After building all of the request messages, they are garlic wrapped for the -target router and sent out an exploratory tunnel. Upon receipt, that peer -determines whether they can or will participate, creating a reply message and -both garlic wrapping and tunnel routing the response with the supplied -information. Upon receipt of the reply at the tunnel creator, the tunnel is -considered valid on that hop (if accepted). Once all peers have accepted, the -tunnel is active.

- -

3.3) Pooling

- -

To allow efficient operation, the router maintains a series of tunnel pools, -each managing a group of tunnels used for a specific purpose with their own -configuration. When a tunnel is needed for that purpose, the router selects one -out of the appropriate pool at random. Overall, there are two exploratory tunnel -pools - one inbound and one outbound - each using the router's exploration -defaults. In addition, there is a pair of pools for each local destination - -one inbound and one outbound tunnel. Those pools use the configuration specified -when the local destination connected to the router, or the router's defaults if -not specified.

- -

Each pool has within its configuration a few key settings, defining how many -tunnels to keep active, how many backup tunnels to maintain in case of failure, -how frequently to test the tunnels, how long the tunnels should be, whether those -lengths should be randomized, how often replacement tunnels should be built, as -well as any of the other settings allowed when configuring individual tunnels.

- -

3.4) Alternatives

- -

3.4.1) Telescopic building

- -

One question that may arise regarding the use of the exploratory tunnels for -sending and receiving tunnel creation messages is how that impacts the tunnel's -vulnerability to predecessor attacks. While the endpoints and gateways of -those tunnels will be randomly distributed across the network (perhaps even -including the tunnel creator in that set), another alternative is to use the -tunnel pathways themselves to pass along the request and response, as is done -in TOR. This, however, may lead to leaks -during tunnel creation, allowing peers to discover how many hops there are later -on in the tunnel by monitoring the timing or packet count as the tunnel is -built. Techniques could be used to minimize this issue, such as using each of -the hops as endpoints (per 2.7.2) for a random -number of messages before continuing on to build the next hop.

- -

3.4.2) Non-exploratory tunnels for management

- -

A second alternative to the tunnel building process is to give the router -an additional set of non-exploratory inbound and outbound pools, using those for -the tunnel request and response. Assuming the router has a well integrated view -of the network, this should not be necessary, but if the router was partitioned -in some way, using non-exploratory pools for tunnel management would reduce the -leakage of information about what peers are in the router's partition.

- -

4) Tunnel throttling

- -

Even though the tunnels within I2P bear a resemblence to a circuit switched -network, everything within I2P is strictly message based - tunnels are merely -accounting tricks to help organize the delivery of messages. No assumptions are -made regarding reliability or ordering of messages, and retransmissions are left -to higher levels (e.g. I2P's client layer streaming library). This allows I2P -to take advantage of throttling techniques available to both packet switched and -circuit switched networks. For instance, each router may keep track of the -moving average of how much data each tunnel is using, combine that with all of -the averages used by other tunnels the router is participating in, and be able -to accept or reject additional tunnel participation requests based on its -capacity and utilization. On the other hand, each router can simply drop -messages that are beyond its capacity, exploiting the research used on the -normal internet.

- -

5) Mixing/batching

- -

What strategies should be used at the gateway and at each hop for delaying, -reordering, rerouting, or padding messages? To what extent should this be done -automatically, how much should be configured as a per tunnel or per hop setting, -and how should the tunnel's creator (and in turn, user) control this operation? -All of this is left as unknown, to be worked out for -I2P 3.0

diff --git a/router/doc/udp.html b/router/doc/udp.html deleted file mode 100644 index 4a855ece9..000000000 --- a/router/doc/udp.html +++ /dev/null @@ -1,759 +0,0 @@ -$Id: udp.html,v 1.19 2006/02/15 00:33:32 jrandom Exp $ - -

Secure Semireliable UDP (SSU)

-DRAFT - -

-The goal of this protocol is to provide secure, authenticated, -semireliable, and unordered message delivery, exposing only a minimal -amount of data easily discernible to third parties. It should -support high degree communication as well as TCP-friendly congestion -control, and may include PMTU detection. It should be capable of -efficiently moving bulk data at rates sufficient for home users. -In addition, it should support techniques for addressing network -obstacles, like most NATs or firewalls.

- -

Addressing and introduction

- -

To contact an SSU peer, one of two sets of information is necessary: -a direct address, for when the peer is publicly reachable, or an -indirect address, for using a third party to introduce the peer. -There is no restriction on the number of addresses a peer may have.

- -
-    Direct: ssu://host:port/introKey[?opts=[A-Z]*]
-  Indirect: ssu://tag@relayhost:port/relayIntroKey/targetIntroKey[?opts=[A-Z]*]
-
- -

These introduction keys are delivered through an external channel -and must be used when establishing a session key. For the indirect -address, the peer must first contact the relayhost and ask them for -an introduction to the peer known at that relayhost under the given -tag. If possible, the relayhost sends a message to the addressed -peer telling them to contact the requesting peer, and also gives -the requesting peer the IP and port on which the addressed peer is -located. In addition, the peer establishing the connection must -already know the public keys of the peer they are connecting to (but -not necessary to any intermediary relay peer).

- -

Each of the addresses may also expose a series of options - special -capabilities of that particular peer. For a list of available -capabilities, see below.

- -

Header

- -

All UDP datagrams begin with a MAC and an IV, followed by a variable -size payload encrypted with the appropriate key. The MAC used is -HMAC-MD5, truncated to 16 bytes, while the key is a full AES256 -key. The specific construct of the MAC is the first 16 bytes from:

-
-  HMAC-MD5(payload || IV || (payloadLength ^ protocolVersion), macKey)
-
- -

The payload itself is AES256/CBC encrypted with the IV and the -sessionKey, with replay prevention addressed within its body, -explained below. The payloadLength in the MAC is a 2 byte unsigned -integer in 2s complement.

- -

The protocolVersion is a 2 byte unsigned integer in 2s complement, -and currently set to 0. Peers using a different protocol version will -not be able to communicate with this peer, though earlier versions not -using this flag are.

- -

Payload

- -

Within the AES encrypted payload, there is a minimal common structure -to the various messages - a one byte flag and a four byte sending -timestamp (*seconds* since the unix epoch). The flag byte contains -the following bitfields:

-
-  bits 0-3: payload type
-     bit 4: rekey?
-     bit 5: extended options included
-  bits 6-7: reserved
-
- -

If the rekey flag is set, 64 bytes of keying material follow the -timestamp. If the extended options flag is set, a one byte option -size value is appended to, followed by that many extended option -bytes, which are currently uninterpreted.

- -

When rekeying, the first 32 bytes of the keying material is fed -into a SHA256 to produce the new MAC key, and the next 32 bytes are -fed into a SHA256 to produce the new session key, though the keys are -not immediately used. The other side should also reply with the -rekey flag set and that same keying material. Once both sides have -sent and received those values, the new keys should be used and the -previous keys discarded. It may be useful to keep the old keys -around briefly, to address packet loss and reordering.

- -
- Header: 37+ bytes
- +----+----+----+----+----+----+----+----+
- |                  MAC                  |
- |                                       |
- +----+----+----+----+----+----+----+----+
- |                   IV                  |
- |                                       |
- +----+----+----+----+----+----+----+----+
- |flag|        time       | (optionally  |
- +----+----+----+----+----+              |
- | this may have 64 byte keying material |
- | and/or a one+N byte extended options) |
- +---------------------------------------|
-
- -

Messages

- -

SessionRequest (type 0)

- - - - - - - -
Peer:Alice to Bob
Data:
    -
  • 256 byte X, to begin the DH agreement
  • -
  • 1 byte IP address size
  • -
  • that many byte representation of Bob's IP address
  • -
  • N bytes, currently uninterpreted (later, for challenges)
  • -
Key used:introKey
- -
- +----+----+----+----+----+----+----+----+
- |         X, as calculated from DH      |
- |                                       |
-                 .   .   .               
- |                                       |
- +----+----+----+----+----+----+----+----+
- |size| that many byte IP address (4-16) |
- +----+----+----+----+----+----+----+----+
- |           arbitrary amount            |
- |        of uninterpreted data          |
-                 .   .   .               
- |                                       |
- +----+----+----+----+----+----+----+----+
-
- -

SessionCreated (type 1)

- - - - - - - -
Peer:Bob to Alice
Data:
    -
  • 256 byte Y, to complete the DH agreement
  • -
  • 1 byte IP address size
  • -
  • that many byte representation of Alice's IP address
  • -
  • 2 byte port number (unsigned, big endian 2s complement)
  • -
  • 4 byte relay tag which Alice can publish (else 0x0)
  • -
  • 4 byte timestamp (seconds from the epoch) for use in the DSA - signature
  • -
  • 40 byte DSA signature of the critical exchanged data - (X + Y + Alice's IP + Alice's port + Bob's IP + Bob's port + Alice's - new relay tag + Bob's signed on time), encrypted with another - layer of encryption using the negotiated sessionKey. The IV - is reused here.
  • -
  • 8 bytes padding, encrypted with an additional layer of encryption - using the negotiated session key as part of the DSA block
  • -
  • N bytes, currently uninterpreted (later, for challenges)
  • -
Key used:introKey, with an additional layer of encryption over the 40 byte - signature and the following 8 bytes padding.
- -
- +----+----+----+----+----+----+----+----+
- |         Y, as calculated from DH      |
- |                                       |
-                 .   .   .               
- |                                       |
- +----+----+----+----+----+----+----+----+
- |size| that many byte IP address (4-16) |
- +----+----+----+----+----+----+----+----+
- | Port (A)| public relay tag  |  signed
- +----+----+----+----+----+----+----+----+
-   on time |                             |
- +----+----+                             |
- |              DSA signature            |
- |                                       |
- |                                       |
- |                                       |
- |         +----+----+----+----+----+----+
- |         |     (8 bytes of padding) 
- +----+----+----+----+----+----+----+----+
-           |                             |
- +----+----+                             |
- |           arbitrary amount            |
- |        of uninterpreted data          |
-                 .   .   .               
- |                                       |
- +----+----+----+----+----+----+----+----+
-
- -

SessionConfirmed (type 2)

- - - - - - - -
Peer:Alice to Bob
Data:
    -
  • 1 byte identity fragment info:
    -bits 0-3: current identity fragment #
    -bits 4-7: total identity fragments
  • -
  • 2 byte size of the current identity fragment
  • -
  • that many byte fragment of Alice's identity.
  • -
  • on the last identity fragment, the signed on time is - included after the identity fragment, and the last 40 - bytes contain the DSA signature of the critical exchanged - data (X + Y + Alice's IP + Alice's port + Bob's IP + Bob's port - + Alice's new relay key + Alice's signed on time)
  • -
Key used:sessionKey
- -
- Fragment 1 through N-1
- +----+----+----+----+----+----+----+----+
- |info| cursize |                        |
- +----+----+----+                        |
- |      fragment of Alice's full         |
- |            identity keys              |
-                 .   .   .               
- |                                       |
- +----+----+----+----+----+----+----+----+
- 
- Fragment N:
- +----+----+----+----+----+----+----+----+
- |info| cursize |                        |
- +----+----+----+                        |
- |      fragment of Alice's full         |
- |            identity keys              |
-                 .   .   .               
- |                                       |
- +----+----+----+----+----+----+----+----+
- |  signed on time   |                   |
- +----+----+----+----+                   |
- |  arbitrary amount of uninterpreted    |
- |        data, up from the end of the   |
- |  identity key to 40 bytes prior to    |
- |       end of the current packet       |
- +----+----+----+----+----+----+----+----+
- | DSA signature                         |
- |                                       |
- |                                       |
- |                                       |
- |                                       |
- +----+----+----+----+----+----+----+----+
-
- -

RelayRequest (type 3)

- - - - - - - -
Peer:Alice to Bob
Data:
    -
  • 4 byte relay tag
  • -
  • 1 byte IP address size
  • -
  • that many byte representation of Alice's IP address
  • -
  • 2 byte port number (of Alice)
  • -
  • 1 byte challenge size
  • -
  • that many bytes to be relayed to Charlie in the intro
  • -
  • Alice's intro key (so Bob can reply with Charlie's info)
  • -
  • 4 byte nonce of alice's relay request
  • -
  • N bytes, currently uninterpreted
  • -
Key used:introKey (or sessionKey, if Alice/Bob is established)
- -
- +----+----+----+----+----+----+----+----+
- |      relay tag    |size| that many    |
- +----+----+----+----+----+         +----|
- | bytes for Alice's IP address     |port
- +----+----+----+----+----+----+----+----+
-  (A) |size| that many challenge bytes   |
- +----+----+                             |
- | to be delivered to Charlie            |
- +----+----+----+----+----+----+----+----+
- | Alice's intro key                     |
- |                                       |
- |                                       |
- |                                       |
- +----+----+----+----+----+----+----+----+
- |       nonce       |                   |
- +----+----+----+----+                   |
- | arbitrary amount of uninterpreted data|
- +----+----+----+----+----+----+----+----+
-
- -

RelayResponse (type 4)

- - - - - - - -
Peer:Bob to Alice
Data:
    -
  • 1 byte IP address size
  • -
  • that many byte representation of Charlie's IP address
  • -
  • 2 byte port number
  • -
  • 1 byte IP address size
  • -
  • that many byte representation of Alice's IP address
  • -
  • 2 byte port number
  • -
  • 4 byte nonce sent by Alice
  • -
  • N bytes, currently uninterpreted
  • -
Key used:introKey (or sessionKey, if Alice/Bob is established)
- -
- +----+----+----+----+----+----+----+----+
- |size| that many bytes making up        |
- +----+                        +----+----+
- | Charlie's IP address        | Port (C)|
- +----+----+----+----+----+----+----+----+
- |size| that many bytes making up        |
- +----+                        +----+----+
- | Alice's IP address          | Port (A)|
- +----+----+----+----+----+----+----+----+
- |       nonce       |                   |
- +----+----+----+----+                   |
- | arbitrary amount of uninterpreted data|
- +----+----+----+----+----+----+----+----+
-
- -

RelayIntro (type 5)

- - - - - - - -
Peer:Bob to Charlie
Data:
    -
  • 1 byte IP address size
  • -
  • that many byte representation of Alice's IP address
  • -
  • 2 byte port number (of Alice)
  • -
  • 1 byte challenge size
  • -
  • that many bytes relayed from Alice
  • -
  • N bytes, currently uninterpreted
  • -
Key used:sessionKey
- -
- +----+----+----+----+----+----+----+----+
- |size| that many bytes making up        |
- +----+                        +----+----+
- | Alice's IP address          | Port (A)|
- +----+----+----+----+----+----+----+----+
- |size| that many bytes of challenge     |
- +----+                                  |
- | data relayed from Alice               |
- +----+----+----+----+----+----+----+----+
- | arbitrary amount of uninterpreted data|
- +----+----+----+----+----+----+----+----+
-
- -

Data (type 6)

- - - - - - - -
Peer:Any
Data:
    -
  • 1 byte flags:
    -   bit 0: explicit ACKs included
    -   bit 1: ACK bitfields included
    -   bit 2: reserved
    -   bit 3: explicit congestion notification
    -   bit 4: request previous ACKs
    -   bit 5: want reply
    -   bit 6: extended data included
    -   bit 7: reserved
  • -
  • if explicit ACKs are included:
      -
    • a 1 byte number of ACKs
    • -
    • that many 4 byte MessageIds being fully ACKed
    • -
  • -
  • if ACK bitfields are included:
      -
    • a 1 byte number of ACK bitfields
    • -
    • that many 4 byte MessageIds + a 1 or more byte ACK bitfield. - The bitfield uses the 7 low bits of each byte, with the high - bit specifying whether an additional bitfield byte follows it - (1 = true, 0 = the current bitfield byte is the last). These - sequence of 7 bit arrays represent whether a fragment has been - received - if a bit is 1, the fragment has been received. To - clarify, assuming fragments 0, 2, 5, and 9 have been received, - the bitfield bytes would be as follows:
      -byte 0
      -   bit 0: 1 (further bitfield bytes follow)
      -   bit 1: 1 (fragment 0 received)
      -   bit 2: 0 (fragment 1 not received)
      -   bit 3: 1 (fragment 2 received)
      -   bit 4: 0 (fragment 3 not received)
      -   bit 5: 0 (fragment 4 not received)
      -   bit 6: 1 (fragment 5 received)
      -   bit 7: 0 (fragment 6 not received)
      -byte 1
      -   bit 0: 0 (no further bitfield bytes)
      -   bit 1: 0 (fragment 7 not received)
      -   bit 1: 0 (fragment 8 not received)
      -   bit 1: 1 (fragment 9 received)
      -   bit 1: 0 (fragment 10 not received)
      -   bit 1: 0 (fragment 11 not received)
      -   bit 1: 0 (fragment 12 not received)
      -   bit 1: 0 (fragment 13 not received)
    • -
  • -
  • If extended data included:
      -
    • 1 byte data size
    • -
    • that many bytes of extended data (currently uninterpreted)
    • -
    • 1 byte number of fragments
    • -
    • that many message fragments:
        -
      • 4 byte messageId
      • -
      • 3 byte fragment info:
        -  bits 0-6: fragment #
        -     bit 7: isLast (1 = true)
        -  bits 8-9: unused
        -bits 10-23: fragment size
      • -
      • that many bytes
      -
    • N bytes padding, uninterpreted
    • -
Key used:sessionKey
- -
- +----+----+----+----+----+----+----+----+
- |flag| (additional headers, determined  |
- +----+                                  |
- | by the flags, such as ACKs or         |
- | bitfields                             |
- +----+----+----+----+----+----+----+----+
- |#frg|     messageId     |   frag info  |
- +----+----+----+----+----+----+----+----+
- | that many bytes of fragment data      |
-                  .  .  .                                       
- |                                       |
- +----+----+----+----+----+----+----+----+
- |     messageId     |   frag info  |    |
- +----+----+----+----+----+----+----+    |
- | that many bytes of fragment data      |
-                  .  .  .                                       
- |                                       |
- +----+----+----+----+----+----+----+----+
- |     messageId     |   frag info  |    |
- +----+----+----+----+----+----+----+    |
- | that many bytes of fragment data      |
-                  .  .  .                                       
- |                                       |
- +----+----+----+----+----+----+----+----+
- | arbitrary amount of uninterpreted data|
- +----+----+----+----+----+----+----+----+
-
- -

PeerTest (type 7)

- - - - - - - -
Peer:Any
Data:
    -
  • 4 byte nonce
  • -
  • 1 byte IP address size
  • -
  • that many byte representation of Alice's IP address
  • -
  • 2 byte port number
  • -
  • Alice's introduction key
  • -
  • N bytes, currently uninterpreted
  • -
Key used:introKey (or sessionKey if the connection has already been established)
- -
- +----+----+----+----+----+----+----+----+
- |    test nonce     |size| that many    |
- +----+----+----+----+----+              |
- |bytes making up Alice's IP address     |
- |----+----+----+----+----+----+----+----+
- | Port (A)| Alice or Charlie's          |
- +----+----+                             |
- | introduction key (Alice's is sent to  |
- | Bob and Charlie, while Charlie's is   |                                      |
- | sent to Alice)                        |
- |         +----+----+----+----+----+----+
- |         | arbitrary amount of         |
- |----+----+                             |
- | uninterpreted data                    |
- +----+----+----+----+----+----+----+----+
-
- -

Congestion control

- -

SSU's need for only semireliable delivery, TCP-friendly operation, -and the capacity for high throughput allows a great deal of latitude in -congestion control. The congestion control algorithm outlined below is -meant to be both efficient in bandwidth as well as simple to implement.

- -

Packets are scheduled according to the the router's policy, taking care -not to exceed the router's outbound capacity or to exceed the measured -capacity of the remote peer. The measured capacity should operate along the -lines of TCP's slow start and congestion avoidance, with additive increases -to the sending capacity and multiplicative decreases in face of congestion. -Veering away from TCP, however, routers may give up on some messages after -a given period or number of retransmissions while continuing to transmit -other messages.

- -

The congestion detection techniques vary from TCP as well, since each -message has its own unique and nonsequential identifier, and each message -has a limited size - at most, 32KB. To efficiently transmit this feedback -to the sender, the receiver periodically includes a list of fully ACKed -message identifiers and may also include bitfields for partially received -messages, where each bit represents the reception of a fragment. If -duplicate fragments arrive, the message should be ACKed again, or if the -message has still not been fully received, the bitfield should be -retransmitted with any new updates.

- -

The simplest possible implementation does not need to pad the packets to -any particular size, but instead just places a single message fragment into -a packet and sends it off (careful not to exceed the MTU). A more efficient -strategy would be to bundle multiple message fragments into the same packet, -so long as it doesn't exceed the MTU, but this is not necessary. Eventually, -a set of fixed packet sizes may be appropriate to further hide the data -fragmentation to external adversaries, but the tunnel, garlic, and end to -end padding should be sufficient for most needs until then.

- -

Keys

- -

All encryption used is AES256/CBC with 32 byte keys and 16 byte IVs. -The MAC and session keys are negotiated as part of the DH exchange, used -for the HMAC and encryption, respectively. Prior to the DH exchange, -the publicly knowable introKey is used for the MAC and encryption.

- -

When using the introKey, both the initial message and any subsequent -reply use the introKey of the responder (Bob) - the responder does -not need to know the introKey of the requestor (Alice). The DSA -signing key used by Bob should already be known to Alice when she -contacts him, though Alice's DSA key may not already be known by -Bob.

- -

Upon receiving a message, the receiver checks the from IP address -with any established sessions - if there is one or more matches, -those session's MAC keys are tested sequentially in the HMAC. If none -of those verify or if there are no matching IP addresses, the -receiver tries their introKey in the MAC. If that does not verify, -the packet is dropped. If it does verify, it is interpreted -according to the message type, though if the receiver is overloaded, -it may be dropped anyway.

- -

If Alice and Bob have an established session, but Alice loses the -keys for some reason and she wants to contact Bob, she may at any -time simply establish a new session through the SessionRequest and -related messages. If Bob has lost the key but Alice does not know -that, she will first attempt to prod him to reply, by sending a -DataMessage with the wantReply flag set, and if Bob continually -fails to reply, she will assume the key is lost and reestablish a -new one.

- -

For the DH key agreement, -RFC3526 2048bit -MODP group (#14) is used:

-
-  p = 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
-  g = 2
-
- -

The DSA p, q, and g are shared according to the scope of the -identity which created them.

- -

Replay prevention

- -

Replay prevention at the SSU layer occurs by rejecting packets -with exceedingly old timestamps or those which reuse an IV. To -detect duplicate IVs, a sequence of Bloom filters are employed to -"decay" periodically so that only recently added IVs are detected.

- -

The messageIds used in DataMessages are defined at layers above -the SSU transport and are passed through transparently. These IDs -are not in any particular order - in fact, they are likely to be -entirely random. The SSU layer makes no attempt at messageId -replay prevention - higher layers should take that into account.

- -

Introduction

- -

Indirect session establishment by means of a third party introduction -is necessary for efficient NAT traversal. Charlie, a router behind a -NAT or firewall which does not allow unsolicited inbound UDP packets, -first contacts a few peers, choosing some to serve as introducers. Each -of these peers (Bob, Bill, Betty, etc) provide Charlie with an introduction -tag - a 4 byte random number - which he then makes available to the public -as methods of contacting him. Alice, a router who has Charlie's published -contact methods, first sends a RelayRequest packet to one or more of the -introducers, asking each to introduce her to Charlie (offering the -introduction tag to identify Charlie). Bob then forwards a RelayIntro -packet to Charlie including Alice's public IP and port number, then sends -Alice back a RelayResponse packet containing Charlie's public IP and port -number. When Charlie receives the RelayIntro packet, he sends off a small -random packet to Alice's IP and port (poking a hole in his NAT/firewall), -and when Alice receive's Bob's RelayResponse packet, she begins a new -full direction session establishment with the specified IP and port.

- - - -

Peer testing

- -

The automation of collaborative reachability testing for peers is -enabled by a sequence of PeerTest messages. With its proper -execution, a peer will be able to determine their own reachability -and may update its behavior accordingly. The testing process is -quite simple:

- -
-        Alice                  Bob                  Charlie
-    PeerTest ------------------->
-                             PeerTest-------------------->
-                                <-------------------PeerTest
-         <-------------------PeerTest
-         <------------------------------------------PeerTest
-    PeerTest------------------------------------------>
-         <------------------------------------------PeerTest
-
- -

Each of the PeerTest messages carry a nonce identifying the -test series itself, as initialized by Alice. If Alice doesn't -get a particular message that she expects, she will retransmit -accordingly, and based upon the data received or the messages -missing, she will know her reachability. The various end states -that may be reached are as follows:

- -
    -
  • If she doesn't receive a response from Bob, she will retransmit -up to a certain number of times, but if no response ever arrives, -she will know that her firewall or NAT is somehow misconfigured, -rejecting all inbound UDP packets even in direct response to an -outbound packet. Alternately, Bob may be down or unable to get -Charlie to reply.
  • - -
  • If Alice doesn't receive a PeerTest message with the -expected nonce from a third party (Charlie), she will retransmit -her initial request to Bob up to a certain number of times, even -if she has received Bob's reply already. If Charlie's first message -still doesn't get through but Bob's does, she knows that she is -behind a NAT or firewall that is rejecting unsolicited connection -attempts and that port forwarding is not operating properly (the -IP and port that Bob offered up should be forwarded).
  • - -
  • If Alice receives Bob's PeerTest message and both of Charlie's -PeerTest messages but the enclosed IP and port numbers in Bob's -and Charlie's second messages don't match, she knows that she is -behind a symmetric NAT, rewriting all of her outbound packets with -different 'from' ports for each peer contacted. She will need to -explicitly forward a port and always have that port exposed for -remote connectivity, ignoring further port discovery.
  • - -
  • If Alice receives Charlie's first message but not his second, -she will retransmit her PeerTest message to Charlie up to a -certain number of times, but if no response is received she knows -that Charlie is either confused or no longer online.
  • -
- -

Alice should choose Bob arbitrarily from known peers who seem -to be capable of participating in peer tests. Bob in turn should -choose Charlie arbitrarily from peers that he knows who seem to be -capable of participating in peer tests and who are on a different -IP from both Bob and Alice. If the first error condition occurs -(Alice doesn't get PeerTest messages from Bob), Alice may decide -to designate a new peer as Bob and try again with a different nonce.

- -

Alice's introduction key is included in all of the PeerTest -messages so that she doesn't need to already have an established -session with Bob and so that Charlie can contact her without knowing -any additional information. Alice may go on to establish a session -with either Bob or Charlie, but it is not required.

- -

Message sequences

- -

Connection establishment (direct)

- -
-        Alice                         Bob
-    SessionRequest--------------------->
-          <---------------------SessionCreated
-    SessionConfirmed------------------->
-    SessionConfirmed------------------->
-    SessionConfirmed------------------->
-    SessionConfirmed------------------->
-          <--------------------------Data
-
- -

Connection establishment (indirect)

- -
-        Alice                         Bob                  Charlie
-    RelayRequest ---------------------->
-         <--------------RelayResponse    RelayIntro----------->
-         <--------------------------------------------Data (ignored)
-    SessionRequest-------------------------------------------->
-         <--------------------------------------------SessionCreated
-    SessionConfirmed------------------------------------------>
-    SessionConfirmed------------------------------------------>
-    SessionConfirmed------------------------------------------>
-    SessionConfirmed------------------------------------------>
-         <---------------------------------------------------Data
-
- -

Sample datagrams

- -Minimal data message (no fragments, no ACKs, no NACKs, etc)
-(Size: 39 bytes) - -
- +----+----+----+----+----+----+----+----+
- |                  MAC                  |
- |                                       |
- +----+----+----+----+----+----+----+----+
- |                   IV                  |
- |                                       |
- +----+----+----+----+----+----+----+----+
- |flag|        time       |flag|#frg|    |
- +----+----+----+----+----+----+----+    |
- |  padding to fit a full AES256 block   |
- +----+----+----+----+----+----+----+----+
-
- -Minimal data message with payload
-(Size: 46+fragmentSize bytes) - -
- +----+----+----+----+----+----+----+----+
- |                  MAC                  |
- |                                       |
- +----+----+----+----+----+----+----+----+
- |                   IV                  |
- |                                       |
- +----+----+----+----+----+----+----+----+
- |flag|        time       |flag|#frg| 
- +----+----+----+----+----+----+----+----+
-   messageId    |   frag info  |         |
- +----+----+----+----+----+----+         |
- | that many bytes of fragment data      |
-                  .  .  .                                       
- |                                       |
- +----+----+----+----+----+----+----+----+
-
- -

Peer capabilities

- -
-
B
-
If the peer address contains the 'B' capability, that means - they are willing and able to participate in peer tests as - a 'Bob' or 'Charlie'.
-
C
-
If the peer address contains the 'C' capability, that means - they are willing and able to serve as an introducer - serving - as a Bob for an otherwise unreachable Alice.
-
diff --git a/router/doc/udp.png b/router/doc/udp.png deleted file mode 100644 index b4fd6241a1b9bc666f5e71fa498882ca60303cba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34329 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 Date: Tue, 1 Mar 2011 13:37:40 +0000 Subject: [PATCH 2/6] updates after review --- apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java | 2 +- apps/i2psnark/java/src/org/klomp/snark/MagnetState.java | 4 ++-- apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java | 2 +- apps/i2psnark/java/src/org/klomp/snark/dht/DHT.java | 2 +- .../java/src/org/klomp/snark/web/I2PSnarkServlet.java | 5 +++-- core/java/src/net/i2p/stat/StatManager.java | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java index 13bda2e55..6f35f66bd 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java +++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java @@ -134,7 +134,7 @@ public class I2PSnarkUtil { } /** - * @param KBps + * @param limit KBps */ public void setMaxUpBW(int limit) { _maxUpBW = limit; diff --git a/apps/i2psnark/java/src/org/klomp/snark/MagnetState.java b/apps/i2psnark/java/src/org/klomp/snark/MagnetState.java index e9eb56389..f19f1091a 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/MagnetState.java +++ b/apps/i2psnark/java/src/org/klomp/snark/MagnetState.java @@ -57,7 +57,7 @@ class MagnetState { } /** - * @param call this for a new magnet when you have the size + * Call this for a new magnet when you have the size * @throws IllegalArgumentException */ public void initialize(int size) { @@ -77,7 +77,7 @@ class MagnetState { } /** - * @param Call this for a new magnet when the download is complete. + * Call this for a new magnet when the download is complete. * @throws IllegalArgumentException */ public void setMetaInfo(MetaInfo meta) { diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 29c6a1ed2..e47ffcc0c 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -644,7 +644,7 @@ public class SnarkManager implements Snark.CompleteListener { * @param name hex or b32 name from the magnet link * @param ih 20 byte info hash * @param trackerURL may be null - * @param udpateStatus should we add this magnet to the config file, + * @param updateStatus should we add this magnet to the config file, * to save it across restarts, in case we don't get * the metadata before shutdown? * @throws RuntimeException via Snark.fatal() diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/DHT.java b/apps/i2psnark/java/src/org/klomp/snark/dht/DHT.java index a074890b9..6a16e4e60 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/DHT.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/DHT.java @@ -54,7 +54,7 @@ public interface DHT { * Non-blocking. * * @param ih the Info Hash (torrent) - * @param peer the peer's Hash + * @param peerHash the peer's Hash */ public void announce(byte[] ih, byte[] peerHash); diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index 92a82de3e..788741bd8 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -353,7 +353,7 @@ public class I2PSnarkServlet extends Default { boolean isDegraded = ua != null && (ua.startsWith("Lynx") || ua.startsWith("w3m") || ua.startsWith("ELinks") || ua.startsWith("Dillo")); - boolean noThinsp = isDegraded || ua.startsWith("Opera"); + boolean noThinsp = isDegraded || (ua != null && ua.startsWith("Opera")); if (_manager.util().connected()) { if (isDegraded) out.write(" Date: Tue, 1 Mar 2011 19:32:42 +0000 Subject: [PATCH 3/6] cleanup/fix console build dependencies --- apps/routerconsole/java/build.xml | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/apps/routerconsole/java/build.xml b/apps/routerconsole/java/build.xml index 346fe8ffe..dc686d29c 100644 --- a/apps/routerconsole/java/build.xml +++ b/apps/routerconsole/java/build.xml @@ -63,11 +63,16 @@ - + + + + + + @@ -85,15 +90,16 @@ - - - + + + @@ -117,8 +123,7 @@ - - + @@ -135,6 +140,7 @@ + @@ -164,7 +170,7 @@ - + @@ -265,7 +271,7 @@ - + From 28bd1802b42ae0dccf916a8dad557a4abfbdfe9c Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 2 Mar 2011 15:41:34 +0000 Subject: [PATCH 4/6] 0.8.4 --- core/java/src/net/i2p/CoreVersion.java | 2 +- history.txt | 2 ++ installer/install.xml | 2 +- installer/resources/news.xml | 25 ++++++------------- .../src/net/i2p/router/RouterVersion.java | 4 +-- 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/core/java/src/net/i2p/CoreVersion.java b/core/java/src/net/i2p/CoreVersion.java index 1d05325c7..230f55eae 100644 --- a/core/java/src/net/i2p/CoreVersion.java +++ b/core/java/src/net/i2p/CoreVersion.java @@ -16,7 +16,7 @@ package net.i2p; public class CoreVersion { /** deprecated */ public final static String ID = "Monotone"; - public final static String VERSION = "0.8.3"; + public final static String VERSION = "0.8.4"; public static void main(String args[]) { System.out.println("I2P Core version: " + VERSION); diff --git a/history.txt b/history.txt index 590cd5457..44c8a9288 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,5 @@ +* 2011-03-02 0.8.4 released + 2011-02-27 zzz * Console: - Fix numerous readme HTML errors diff --git a/installer/install.xml b/installer/install.xml index e48aa9164..7c28ad1ce 100644 --- a/installer/install.xml +++ b/installer/install.xml @@ -4,7 +4,7 @@ i2p - 0.8.3 + 0.8.4 diff --git a/installer/resources/news.xml b/installer/resources/news.xml index b33b74b3d..c8a740085 100644 --- a/installer/resources/news.xml +++ b/installer/resources/news.xml @@ -1,24 +1,13 @@
-

2011-01-24: 0.8.3 Released

+

2011-03-02: 0.8.4 Released

-The 0.8.3 release contains several performance improvements, including reduction of threads and -memory usage, and faster I2CP (client-router) communication. -

-There is also new SSL router console support -(instructions here), -a new reseed configuration page including HTTP proxy support for those behind restrictive firewalls, -a new I2CP configuration section including I2CP-over-SSL support for remote clients, -a new server connection limits and blacklist configuration section for enhanced DoS protection, -and a new -HTTP proxy jump server configuration section so you may easily add alternative jump servers. -Statistics are now limited by default to reduce memory usage; the full set of statistics may be re-enabled on the -stats configuration page. -There are also bug fixes, of course, so -as usual, upgrading is recommended. +The 0.8.4 release contains some performance improvements and important bug fixes. +Also, i2psnark now supports magnet links. +As usual, upgrading is recommended.

Please help grow the network. Say hello to the volunteers on the #i2p-help IRC channel. @@ -27,6 +16,6 @@ spread the word, and donate! If you find a bug, please enter a report on trac. We are still looking for volunteers to work on new and existing translations. -Please volunteer on IRC #i2p. +Please volunteer on IRC #i2p-dev.

diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 601535dbe..a411b0099 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,10 +18,10 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 21; + public final static long BUILD = 0; /** for example "-test" */ - public final static String EXTRA = "-rc"; + public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA; public static void main(String args[]) { System.out.println("I2P Router version: " + FULL_VERSION); From f938cc7b0c5ac6d4d31e04cdbe7149d11c056415 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 2 Mar 2011 17:18:37 +0000 Subject: [PATCH 5/6] * BuildHandler: - Limit request queue size - Concurrent request queue - Remove dead code for queued rely handling --- .../i2p/router/tunnel/TunnelDispatcher.java | 3 +- .../i2p/router/tunnel/pool/BuildExecutor.java | 23 +- .../i2p/router/tunnel/pool/BuildHandler.java | 285 ++++++------------ 3 files changed, 100 insertions(+), 211 deletions(-) diff --git a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java index 48305513a..8669f8cb5 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java @@ -709,7 +709,8 @@ public class TunnelDispatcher implements Service { _validator = new BloomFilterIVValidator(_context, getShareBandwidth(_context)); } - private static int getShareBandwidth(RouterContext ctx) { + /** @return in KBps */ + public static int getShareBandwidth(RouterContext ctx) { int irateKBps = ctx.bandwidthLimiter().getInboundKBytesPerSecond(); int orateKBps = ctx.bandwidthLimiter().getOutboundKBytesPerSecond(); double pct = ctx.router().getSharePercentage(); diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java index 19505c0c7..b6394dc5c 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java +++ b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java @@ -27,9 +27,9 @@ import net.i2p.util.Log; */ class BuildExecutor implements Runnable { private final ArrayList _recentBuildIds = new ArrayList(100); - private RouterContext _context; - private Log _log; - private TunnelPoolManager _manager; + private final RouterContext _context; + private final Log _log; + private final TunnelPoolManager _manager; /** list of TunnelCreatorConfig elements of tunnels currently being built */ private final Object _currentlyBuilding; /** indexed by ptcc.getReplyMessageId() */ @@ -37,7 +37,7 @@ class BuildExecutor implements Runnable { /** indexed by ptcc.getReplyMessageId() */ private final ConcurrentHashMap _recentlyBuildingMap; private boolean _isRunning; - private BuildHandler _handler; + private final BuildHandler _handler; private boolean _repoll; private static final int MAX_CONCURRENT_BUILDS = 10; /** accept replies up to a minute after we gave up on them */ @@ -248,8 +248,6 @@ class BuildExecutor implements Runnable { int pendingRemaining = 0; //long loopBegin = 0; - //long beforeHandleInboundReplies = 0; - //long afterHandleInboundReplies = 0; //long afterBuildZeroHop = 0; long afterBuildReal = 0; long afterHandleInbound = 0; @@ -268,10 +266,6 @@ class BuildExecutor implements Runnable { wanted.add(pool); } - //beforeHandleInboundReplies = System.currentTimeMillis(); - _handler.handleInboundReplies(); - //afterHandleInboundReplies = System.currentTimeMillis(); - // allowed() also expires timed out requests (for new style requests) int allowed = allowed(); @@ -327,9 +321,6 @@ class BuildExecutor implements Runnable { _log.debug("Configuring new tunnel " + i + " for " + pool + ": " + cfg); buildTunnel(pool, cfg); realBuilt++; - - // we want replies to go to the top of the queue - _handler.handleInboundReplies(); } else { i--; } @@ -391,10 +382,8 @@ class BuildExecutor implements Runnable { * This prevents a large number of client pools from starving the exploratory pool. * */ - private static class TunnelPoolComparator implements Comparator { - public int compare(Object l, Object r) { - TunnelPool tpl = (TunnelPool) l; - TunnelPool tpr = (TunnelPool) r; + private static class TunnelPoolComparator implements Comparator { + public int compare(TunnelPool tpl, TunnelPool tpr) { if (tpl.getSettings().isExploratory() && !tpr.getSettings().isExploratory()) return -1; if (tpr.getSettings().isExploratory() && !tpl.getSettings().isExploratory()) diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java index 618395576..afdd0600b 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java +++ b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java @@ -1,7 +1,7 @@ package net.i2p.router.tunnel.pool; -import java.util.ArrayList; import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; import net.i2p.data.Base64; import net.i2p.data.ByteArray; @@ -27,14 +27,20 @@ import net.i2p.router.peermanager.TunnelHistory; import net.i2p.router.tunnel.BuildMessageProcessor; import net.i2p.router.tunnel.BuildReplyHandler; import net.i2p.router.tunnel.HopConfig; +import net.i2p.router.tunnel.TunnelDispatcher; import net.i2p.stat.Rate; import net.i2p.stat.RateStat; import net.i2p.util.Log; /** + * Handle the received tunnel build message requests and replies, + * including sending responsses to requests, updating the + * lists of our tunnels and participating tunnels, + * and updating stats. + * + * Replies are handled immediately on reception; requests are queued. * * Note that 10 minute tunnel expiration is hardcoded in here. - * */ class BuildHandler { private final RouterContext _context; @@ -42,29 +48,21 @@ class BuildHandler { private final BuildExecutor _exec; private final Job _buildMessageHandlerJob; private final Job _buildReplyMessageHandlerJob; - /** list of BuildMessageState, oldest first */ - private final List _inboundBuildMessages; - /** list of BuildReplyMessageState, oldest first - unused unless HANDLE_REPLIES_INLINE == false */ - private final List _inboundBuildReplyMessages; - /** list of BuildEndMessageState, oldest first - unused unless HANDLE_REPLIES_INLINE == false */ - private final List _inboundBuildEndMessages; + private final LinkedBlockingQueue _inboundBuildMessages; private final BuildMessageProcessor _processor; private final ParticipatingThrottler _throttler; - private static final boolean HANDLE_REPLIES_INLINE = true; - + /** TODO these may be too high, review and adjust */ + private static final int MIN_QUEUE = 12; + private static final int MAX_QUEUE = 96; + public BuildHandler(RouterContext ctx, BuildExecutor exec) { _context = ctx; _log = ctx.logManager().getLog(getClass()); _exec = exec; - _inboundBuildMessages = new ArrayList(16); - if (HANDLE_REPLIES_INLINE) { - _inboundBuildEndMessages = null; - _inboundBuildReplyMessages = null; - } else { - _inboundBuildEndMessages = new ArrayList(16); - _inboundBuildReplyMessages = new ArrayList(16); - } + // Queue size = 12 * share BW / 48K + int sz = Math.min(MAX_QUEUE, Math.max(MIN_QUEUE, TunnelDispatcher.getShareBandwidth(ctx) * MIN_QUEUE / 48)); + _inboundBuildMessages = new LinkedBlockingQueue(sz); _context.statManager().createRateStat("tunnel.reject.10", "How often we reject a tunnel probabalistically", "Tunnels", new long[] { 60*1000, 10*60*1000 }); _context.statManager().createRateStat("tunnel.reject.20", "How often we reject a tunnel because of transient overload", "Tunnels", new long[] { 60*1000, 10*60*1000 }); @@ -94,6 +92,7 @@ class BuildHandler { _context.statManager().createRateStat("tunnel.corruptBuildReply", "", "Tunnels", new long[] { 24*60*60*1000l }); _processor = new BuildMessageProcessor(ctx); + _throttler = new ParticipatingThrottler(ctx); _buildMessageHandlerJob = new TunnelBuildMessageHandlerJob(ctx); _buildReplyMessageHandlerJob = new TunnelBuildReplyMessageHandlerJob(ctx); TunnelBuildMessageHandlerJobBuilder tbmhjb = new TunnelBuildMessageHandlerJobBuilder(); @@ -102,7 +101,6 @@ class BuildHandler { ctx.inNetMessagePool().registerHandlerJobBuilder(TunnelBuildReplyMessage.MESSAGE_TYPE, tbrmhjb); ctx.inNetMessagePool().registerHandlerJobBuilder(VariableTunnelBuildMessage.MESSAGE_TYPE, tbmhjb); ctx.inNetMessagePool().registerHandlerJobBuilder(VariableTunnelBuildReplyMessage.MESSAGE_TYPE, tbrmhjb); - _throttler = new ParticipatingThrottler(ctx); } private static final int MAX_HANDLE_AT_ONCE = 2; @@ -110,122 +108,37 @@ class BuildHandler { /** * Blocking call to handle a few of the pending inbound requests, returning how many - * requests remain after this pass + * requests remain after this pass. This is called by BuildExecutor. */ int handleInboundRequests() { - int dropExpired = 0; - int remaining = 0; - List handled = null; - long beforeFindHandled = System.currentTimeMillis(); - synchronized (_inboundBuildMessages) { - int toHandle = _inboundBuildMessages.size(); - if (toHandle > 0) { - if (toHandle > MAX_HANDLE_AT_ONCE) - toHandle = MAX_HANDLE_AT_ONCE; - handled = new ArrayList(toHandle); - //if (false) { - // for (int i = 0; i < toHandle; i++) // LIFO for lower response time (should we RED it for DoS?) - // handled.add(_inboundBuildMessages.remove(_inboundBuildMessages.size()-1)); - //} else { - // drop any expired messages - long dropBefore = System.currentTimeMillis() - (BuildRequestor.REQUEST_TIMEOUT/4); - do { - BuildMessageState state = (BuildMessageState)_inboundBuildMessages.get(0); - if (state.recvTime <= dropBefore) { - _inboundBuildMessages.remove(0); - dropExpired++; - if (_log.shouldLog(Log.WARN)) - _log.warn("Not even trying to handle/decrypt the request " + state.msg.getUniqueId() - + ", since we received it a long time ago: " + (System.currentTimeMillis() - state.recvTime)); - _context.statManager().addRateData("tunnel.dropLoadDelay", System.currentTimeMillis() - state.recvTime, 0); - } else { - break; - } - } while (!_inboundBuildMessages.isEmpty()); - - if (dropExpired > 0) - _context.throttle().setTunnelStatus(_x("Dropping tunnel requests: Too slow")); + for (int i = 0; i < MAX_HANDLE_AT_ONCE; ) { + BuildMessageState state = _inboundBuildMessages.poll(); + if (state == null) + return 0; + long dropBefore = System.currentTimeMillis() - (BuildRequestor.REQUEST_TIMEOUT/4); + if (state.recvTime <= dropBefore) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Not even trying to handle/decrypt the request " + state.msg.getUniqueId() + + ", since we received it a long time ago: " + (System.currentTimeMillis() - state.recvTime)); + _context.statManager().addRateData("tunnel.dropLoadDelay", System.currentTimeMillis() - state.recvTime, 0); + _context.throttle().setTunnelStatus(_x("Dropping tunnel requests: Too slow")); + continue; + } - // now pull off the oldest requests first (we're doing a tail-drop - // when adding) - for (int i = 0; i < toHandle && !_inboundBuildMessages.isEmpty(); i++) - handled.add(_inboundBuildMessages.remove(0)); - //} - } - remaining = _inboundBuildMessages.size(); - } - if (handled != null) { + i++; + long beforeHandle = System.currentTimeMillis(); + long actualTime = handleRequest(state); if (_log.shouldLog(Log.DEBUG)) - _log.debug("Handling " + handled.size() + " requests (took " + (System.currentTimeMillis()-beforeFindHandled) + "ms to find them)"); - - for (int i = 0; i < handled.size(); i++) { - BuildMessageState state = (BuildMessageState)handled.get(i); - long beforeHandle = System.currentTimeMillis(); - long actualTime = handleRequest(state); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Handle took " + (System.currentTimeMillis()-beforeHandle) + "/" + actualTime + " (" + i + " out of " + handled.size() + " with " + remaining + " remaining)"); - } - handled.clear(); + _log.debug("Handle took " + (System.currentTimeMillis()-beforeHandle) + "/" + actualTime + + " (" + i + " with " + _inboundBuildMessages.size() + " remaining)"); } - if (!HANDLE_REPLIES_INLINE) { - synchronized (_inboundBuildEndMessages) { - int toHandle = _inboundBuildEndMessages.size(); - if (toHandle > 0) { - if (handled == null) - handled = new ArrayList(_inboundBuildEndMessages); - else - handled.addAll(_inboundBuildEndMessages); - _inboundBuildEndMessages.clear(); - } - } - } - if (handled != null) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Handling " + handled.size() + " requests that are actually replies"); - // these are inbound build messages that actually contain the full replies, since - // they are for inbound tunnels we have created - for (int i = 0; i < handled.size(); i++) { - BuildEndMessageState state = (BuildEndMessageState)handled.get(i); - handleRequestAsInboundEndpoint(state); - } - } - - // anything else? - /* - synchronized (_inboundBuildMessages) { - int remaining = _inboundBuildMessages.size(); - return remaining; - } - */ + + int remaining = _inboundBuildMessages.size(); if (remaining > 0) _context.statManager().addRateData("tunnel.handleRemaining", remaining, 0); return remaining; } - /** Warning - noop if HANDLE_REPLIES_INLINE == true */ - void handleInboundReplies() { - if (HANDLE_REPLIES_INLINE) - return; - List handled = null; - synchronized (_inboundBuildReplyMessages) { - int toHandle = _inboundBuildReplyMessages.size(); - if (toHandle > 0) { - // always handle all of them - they're replies that we were waiting for! - handled = new ArrayList(_inboundBuildReplyMessages); - _inboundBuildReplyMessages.clear(); - } - } - if (handled != null) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Handling " + handled.size() + " replies"); - - for (int i = 0; i < handled.size(); i++) { - BuildReplyMessageState state = (BuildReplyMessageState)handled.get(i); - handleReply(state); - } - } - } - private void handleReply(BuildReplyMessageState state) { // search through the tunnels for a reply long replyMessageId = state.msg.getUniqueId(); @@ -343,7 +256,7 @@ class BuildHandler { } } - /** @return handle time or -1 */ + /** @return handle time or -1 if it wasn't completely handled */ private long handleRequest(BuildMessageState state) { long timeSinceReceived = System.currentTimeMillis()-state.recvTime; if (_log.shouldLog(Log.DEBUG)) @@ -365,7 +278,7 @@ class BuildHandler { BuildRequestRecord req = _processor.decrypt(_context, state.msg, _context.routerHash(), _context.keyManager().getPrivateKey()); long decryptTime = System.currentTimeMillis() - beforeDecrypt; _context.statManager().addRateData("tunnel.decryptRequestTime", decryptTime, decryptTime); - if (decryptTime > 500) + if (decryptTime > 500 && _log.shouldLog(Log.WARN)) _log.warn("Took too long to decrypt the request: " + decryptTime + " for message " + state.msg.getUniqueId() + " received " + (timeSinceReceived+decryptTime) + " ago"); if (req == null) { // no records matched, or the decryption failed. bah @@ -379,7 +292,7 @@ class BuildHandler { long readPeerTime = System.currentTimeMillis()-beforeLookup; RouterInfo nextPeerInfo = _context.netDb().lookupRouterInfoLocally(nextPeer); long lookupTime = System.currentTimeMillis()-beforeLookup; - if (lookupTime > 500) + if (lookupTime > 500 && _log.shouldLog(Log.WARN)) _log.warn("Took too long to lookup the request: " + lookupTime + "/" + readPeerTime + " for message " + state.msg.getUniqueId() + " received " + (timeSinceReceived+decryptTime) + " ago"); if (nextPeerInfo == null) { if (_log.shouldLog(Log.DEBUG)) @@ -416,9 +329,9 @@ class BuildHandler { } private class HandleReq extends JobImpl { - private BuildMessageState _state; - private BuildRequestRecord _req; - private Hash _nextPeer; + private final BuildMessageState _state; + private final BuildRequestRecord _req; + private final Hash _nextPeer; HandleReq(RouterContext ctx, BuildMessageState state, BuildRequestRecord req, Hash nextPeer) { super(ctx); _state = state; @@ -439,9 +352,9 @@ class BuildHandler { } private static class TimeoutReq extends JobImpl { - private BuildMessageState _state; - private BuildRequestRecord _req; - private Hash _nextPeer; + private final BuildMessageState _state; + private final BuildRequestRecord _req; + private final Hash _nextPeer; TimeoutReq(RouterContext ctx, BuildMessageState state, BuildRequestRecord req, Hash nextPeer) { super(ctx); _state = state; @@ -580,7 +493,7 @@ class BuildHandler { Hash from = state.fromHash; if (from == null) from = state.from.calculateHash(); - if (_throttler.shouldThrottle(from)) { + if (from != null && _throttler.shouldThrottle(from)) { if (_log.shouldLog(Log.WARN)) _log.warn("Rejecting tunnel (hop throttle), previous hop: " + from); // no setTunnelStatus() indication @@ -731,9 +644,7 @@ class BuildHandler { } public int getInboundBuildQueueSize() { - synchronized (_inboundBuildMessages) { return _inboundBuildMessages.size(); - } } /** @@ -756,14 +667,7 @@ class BuildHandler { _log.error("received it, but its not inbound? " + cfg); } BuildEndMessageState state = new BuildEndMessageState(cfg, receivedMessage); - if (HANDLE_REPLIES_INLINE) { - handleRequestAsInboundEndpoint(state); - } else { - synchronized (_inboundBuildEndMessages) { - _inboundBuildEndMessages.add(state); - } - _exec.repoll(); - } + handleRequestAsInboundEndpoint(state); } else { if (_exec.wasRecentlyBuilding(reqId)) { // we are the IBEP but we already gave up? @@ -771,52 +675,53 @@ class BuildHandler { _log.warn("Dropping the reply " + reqId + ", as we used to be building that"); _context.statManager().addRateData("tunnel.buildReplyTooSlow", 1, 0); } else { - synchronized (_inboundBuildMessages) { - boolean removed = false; - int dropped = 0; - for (int i = 0; i < _inboundBuildMessages.size(); i++) { - BuildMessageState cur = (BuildMessageState)_inboundBuildMessages.get(i); - long age = System.currentTimeMillis() - cur.recvTime; - if (age >= BuildRequestor.REQUEST_TIMEOUT/4) { - _inboundBuildMessages.remove(i); - i--; - dropped++; - _context.statManager().addRateData("tunnel.dropLoad", age, _inboundBuildMessages.size()); - } - } - if (dropped > 0) { + int sz = _inboundBuildMessages.size(); + BuildMessageState cur = _inboundBuildMessages.peek(); + boolean accept = true; + if (cur != null) { + long age = System.currentTimeMillis() - cur.recvTime; + if (age >= BuildRequestor.REQUEST_TIMEOUT/4) { + _context.statManager().addRateData("tunnel.dropLoad", age, sz); _context.throttle().setTunnelStatus(_x("Dropping tunnel requests: High load")); // if the queue is backlogged, stop adding new messages - _context.statManager().addRateData("tunnel.dropLoadBacklog", _inboundBuildMessages.size(), _inboundBuildMessages.size()); + _context.statManager().addRateData("tunnel.dropLoadBacklog", sz, sz); + accept = false; + } + } + if (accept) { + int queueTime = estimateQueueTime(sz); + float pDrop = queueTime/((float)BuildRequestor.REQUEST_TIMEOUT*3); + pDrop = (float)Math.pow(pDrop, 16); // steeeep + float f = _context.random().nextFloat(); + //if ( (pDrop > f) && (allowProactiveDrop()) ) { + if (pDrop > f) { + _context.throttle().setTunnelStatus(_x("Dropping tunnel requests: Queue time")); + _context.statManager().addRateData("tunnel.dropLoadProactive", queueTime, sz); } else { - int queueTime = estimateQueueTime(_inboundBuildMessages.size()); - float pDrop = queueTime/((float)BuildRequestor.REQUEST_TIMEOUT*3); - pDrop = (float)Math.pow(pDrop, 16); // steeeep - float f = _context.random().nextFloat(); - if ( (pDrop > f) && (allowProactiveDrop()) ) { - _context.throttle().setTunnelStatus(_x("Dropping tunnel requests: Queue time")); - _context.statManager().addRateData("tunnel.dropLoadProactive", queueTime, _inboundBuildMessages.size()); + accept = _inboundBuildMessages.offer(new BuildMessageState(receivedMessage, from, fromHash)); + if (accept) { + // wake up the Executor to call handleInboundRequests() + _exec.repoll(); } else { - _inboundBuildMessages.add(new BuildMessageState(receivedMessage, from, fromHash)); + _context.throttle().setTunnelStatus(_x("Dropping tunnel requests: High load")); + _context.statManager().addRateData("tunnel.dropLoadBacklog", sz, sz); } } } - _exec.repoll(); } } return _buildMessageHandlerJob; } } +/**** private boolean allowProactiveDrop() { - String allow = _context.getProperty("router.allowProactiveDrop", "true"); - boolean rv = false; - if ( (allow == null) || (Boolean.valueOf(allow).booleanValue()) ) - rv = true; + boolean rv = _context.getBooleanPropertyDefaultTrue("router.allowProactiveDrop"); if (!rv) _context.statManager().addRateData("tunnel.dropLoadProactiveAbort", 1, 0); return rv; } +****/ private int estimateQueueTime(int numPendingMessages) { int decryptTime = 200; @@ -845,24 +750,17 @@ class BuildHandler { if (_log.shouldLog(Log.DEBUG)) _log.debug("Receive tunnel build reply message " + receivedMessage.getUniqueId() + " from " + (fromHash != null ? fromHash.toBase64() : from != null ? from.calculateHash().toBase64() : "a tunnel")); - if (HANDLE_REPLIES_INLINE) { - handleReply(new BuildReplyMessageState(receivedMessage)); - } else { - synchronized (_inboundBuildReplyMessages) { - _inboundBuildReplyMessages.add(new BuildReplyMessageState(receivedMessage)); - } - _exec.repoll(); - } + handleReply(new BuildReplyMessageState(receivedMessage)); return _buildReplyMessageHandlerJob; } } /** normal inbound requests from other people */ private static class BuildMessageState { - TunnelBuildMessage msg; - RouterIdentity from; - Hash fromHash; - long recvTime; + final TunnelBuildMessage msg; + final RouterIdentity from; + final Hash fromHash; + final long recvTime; public BuildMessageState(I2NPMessage m, RouterIdentity f, Hash h) { msg = (TunnelBuildMessage)m; from = f; @@ -872,8 +770,8 @@ class BuildHandler { } /** replies for outbound tunnels that we have created */ private static class BuildReplyMessageState { - TunnelBuildReplyMessage msg; - long recvTime; + final TunnelBuildReplyMessage msg; + final long recvTime; public BuildReplyMessageState(I2NPMessage m) { msg = (TunnelBuildReplyMessage)m; recvTime = System.currentTimeMillis(); @@ -881,9 +779,9 @@ class BuildHandler { } /** replies for inbound tunnels we have created */ private static class BuildEndMessageState { - TunnelBuildMessage msg; - PooledTunnelCreatorConfig cfg; - long recvTime; + final TunnelBuildMessage msg; + final PooledTunnelCreatorConfig cfg; + final long recvTime; public BuildEndMessageState(PooledTunnelCreatorConfig c, I2NPMessage m) { cfg = c; msg = (TunnelBuildMessage)m; @@ -891,13 +789,14 @@ class BuildHandler { } } - // noop + /** noop */ private static class TunnelBuildMessageHandlerJob extends JobImpl { private TunnelBuildMessageHandlerJob(RouterContext ctx) { super(ctx); } public void runJob() {} public String getName() { return "Receive tunnel build message"; } } - // noop + + /** noop */ private static class TunnelBuildReplyMessageHandlerJob extends JobImpl { private TunnelBuildReplyMessageHandlerJob(RouterContext ctx) { super(ctx); } public void runJob() {} @@ -910,7 +809,7 @@ class BuildHandler { * but it affects capacity calculations */ private static class TunnelBuildNextHopFailJob extends JobImpl { - HopConfig _cfg; + final HopConfig _cfg; private TunnelBuildNextHopFailJob(RouterContext ctx, HopConfig cfg) { super(ctx); _cfg = cfg; From cb707785cc4a14738b58585d0b1de91673efe569 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 2 Mar 2011 17:27:02 +0000 Subject: [PATCH 6/6] * OutNetMessage: Remove dead code * Tunnel cleanups, final, javadoc --- history.txt | 8 +++ .../src/net/i2p/router/InNetMessagePool.java | 15 ++++-- .../src/net/i2p/router/OutNetMessage.java | 6 +-- .../src/net/i2p/router/OutNetMessagePool.java | 54 +++---------------- .../src/net/i2p/router/RouterVersion.java | 2 +- .../router/transport/TransportManager.java | 4 +- .../router/tunnel/pool/BuildRequestor.java | 26 +++++---- .../net/i2p/router/tunnel/pool/ExpireJob.java | 15 ++++-- .../net/i2p/router/tunnel/pool/TestJob.java | 32 +++++++---- .../i2p/router/tunnel/pool/TunnelPool.java | 27 +++++----- .../router/tunnel/pool/TunnelPoolManager.java | 5 +- 11 files changed, 96 insertions(+), 98 deletions(-) diff --git a/history.txt b/history.txt index 44c8a9288..fba494287 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,11 @@ +2011-03-02 zzz + * BuildHandler: + - Limit request queue size + - Concurrent request queue + - Remove dead code for queued rely handling + * OutNetMessage: Remove dead code + * Tunnel cleanups, final, javadoc + * 2011-03-02 0.8.4 released 2011-02-27 zzz diff --git a/router/java/src/net/i2p/router/InNetMessagePool.java b/router/java/src/net/i2p/router/InNetMessagePool.java index 4e6846b46..d6067b2b3 100644 --- a/router/java/src/net/i2p/router/InNetMessagePool.java +++ b/router/java/src/net/i2p/router/InNetMessagePool.java @@ -81,7 +81,6 @@ public class InNetMessagePool implements Service { _shortCircuitGatewayJob = new SharedShortCircuitGatewayJob(context); } _log = _context.logManager().getLog(InNetMessagePool.class); - _alive = false; _context.statManager().createRateStat("inNetPool.dropped", "How often do we drop a message", "InNetPool", new long[] { 60*60*1000l }); _context.statManager().createRateStat("inNetPool.droppedDeliveryStatusDelay", "How long after a delivery status message is created do we receive it back again (for messages that are too slow to be handled)", "InNetPool", new long[] { 60*60*1000l }); _context.statManager().createRateStat("inNetPool.duplicate", "How often do we receive a duplicate message", "InNetPool", new long[] { 60*60*1000l }); @@ -89,12 +88,20 @@ public class InNetMessagePool implements Service { _context.statManager().createRateStat("inNetPool.droppedDbLookupResponseMessage", "How often we drop a slow-to-arrive db search response", "InNetPool", new long[] { 60*60*1000l }); } + /** + * @return previous builder for this message type, or null + * @throws AIOOBE if i2npMessageType is greater than MAX_I2NP_MESSAGE_TYPE + */ public HandlerJobBuilder registerHandlerJobBuilder(int i2npMessageType, HandlerJobBuilder builder) { HandlerJobBuilder old = _handlerJobBuilders[i2npMessageType]; _handlerJobBuilders[i2npMessageType] = builder; return old; } + /** + * @return previous builder for this message type, or null + * @throws AIOOBE if i2npMessageType is greater than MAX_I2NP_MESSAGE_TYPE + */ public HandlerJobBuilder unregisterHandlerJobBuilder(int i2npMessageType) { HandlerJobBuilder old = _handlerJobBuilders[i2npMessageType]; _handlerJobBuilders[i2npMessageType] = null; @@ -102,12 +109,14 @@ public class InNetMessagePool implements Service { } /** - * Add a new message to the pool, returning the number of messages in the - * pool so that the comm system can throttle inbound messages. If there is + * Add a new message to the pool. + * If there is * a HandlerJobBuilder for the inbound message type, the message is loaded * into a job created by that builder and queued up for processing instead * (though if the builder doesn't create a job, it is added to the pool) * + * @return -1 for some types of errors but not all; 0 otherwise + * (was queue length, long ago) */ public int add(I2NPMessage messageBody, RouterIdentity fromRouter, Hash fromRouterHash) { long exp = messageBody.getMessageExpiration(); diff --git a/router/java/src/net/i2p/router/OutNetMessage.java b/router/java/src/net/i2p/router/OutNetMessage.java index 0c430b07e..07fd8259c 100644 --- a/router/java/src/net/i2p/router/OutNetMessage.java +++ b/router/java/src/net/i2p/router/OutNetMessage.java @@ -29,8 +29,8 @@ import net.i2p.util.Log; * */ public class OutNetMessage { - private Log _log; - private RouterContext _context; + private final Log _log; + private final RouterContext _context; private RouterInfo _target; private I2NPMessage _message; /** cached message class name, for use after we discard the message */ @@ -50,7 +50,7 @@ public class OutNetMessage { private long _sendBegin; private long _transmitBegin; private Exception _createdBy; - private long _created; + private final long _created; /** for debugging, contains a mapping of even name to Long (e.g. "begin sending", "handleOutbound", etc) */ private HashMap _timestamps; /** diff --git a/router/java/src/net/i2p/router/OutNetMessagePool.java b/router/java/src/net/i2p/router/OutNetMessagePool.java index 8e057ff49..6b5ddfaab 100644 --- a/router/java/src/net/i2p/router/OutNetMessagePool.java +++ b/router/java/src/net/i2p/router/OutNetMessagePool.java @@ -18,24 +18,18 @@ import net.i2p.util.Log; * that wants to send a message, and the communication subsystem periodically * retrieves messages for delivery. * + * Actually, this doesn't 'pool' anything, it calls the comm system directly. + * Nor does it organize by priority. But perhaps it could someday. */ public class OutNetMessagePool { - private Log _log; - private RouterContext _context; + private final Log _log; + private final RouterContext _context; public OutNetMessagePool(RouterContext context) { _context = context; _log = _context.logManager().getLog(OutNetMessagePool.class); } - /** - * Remove the highest priority message, or null if none are available. - * - */ - public OutNetMessage getNext() { - return null; - } - /** * Add a new message to the pool * @@ -47,8 +41,8 @@ public class OutNetMessagePool { return; } - if (_log.shouldLog(Log.INFO)) - _log.info("Adding outbound message to " + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Adding outbound message to " + msg.getTarget().getIdentity().getHash().toBase64().substring(0,6) + " with id " + msg.getMessage().getUniqueId() + " expiring on " + msg.getMessage().getMessageExpiration() @@ -70,7 +64,7 @@ public class OutNetMessagePool { return false; } if (msg.getTarget() == null) { - _log.error("No target in the OutNetMessage: " + msg, new Exception("Definitely a fuckup")); + _log.error("No target in the OutNetMessage: " + msg, new Exception()); return false; } if (msg.getPriority() < 0) { @@ -83,38 +77,4 @@ public class OutNetMessagePool { } return true; } - - /** - * Clear any messages that have expired, enqueuing any appropriate jobs - * - */ - public void clearExpired() { - // noop - } - - /** - * Retrieve the number of messages, regardless of priority. - * - */ - public int getCount() { return 0; } - - /** - * Retrieve the number of messages at the given priority. This can be used for - * subsystems that maintain a pool of messages to be sent whenever there is spare time, - * where all of these 'spare' messages are of the same priority. - * - */ - public int getCount(int priority) { return 0; } - - public void dumpPoolInfo() { return; } - - private static class ReverseIntegerComparator implements Comparator { - public int compare(Object lhs, Object rhs) { - if ( (lhs == null) || (rhs == null) ) return 0; // invalid, but never used - if ( !(lhs instanceof Integer) || !(rhs instanceof Integer)) return 0; - Integer lv = (Integer)lhs; - Integer rv = (Integer)rhs; - return - (lv.compareTo(rv)); - } - } } diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index a411b0099..87e5bffca 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 0; + public final static long BUILD = 1; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java index 266561cbc..81cf89340 100644 --- a/router/java/src/net/i2p/router/transport/TransportManager.java +++ b/router/java/src/net/i2p/router/transport/TransportManager.java @@ -459,9 +459,9 @@ public class TransportManager implements TransportEventListener { if (_log.shouldLog(Log.DEBUG)) _log.debug("I2NPMessage received: " + message.getClass().getName(), new Exception("Where did I come from again?")); try { - int num = _context.inNetMessagePool().add(message, fromRouter, fromRouterHash); + _context.inNetMessagePool().add(message, fromRouter, fromRouterHash); if (_log.shouldLog(Log.DEBUG)) - _log.debug("Added to in pool: "+ num); + _log.debug("Added to in pool"); } catch (IllegalArgumentException iae) { if (_log.shouldLog(Log.WARN)) _log.warn("Error receiving message", iae); diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java b/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java index 0ccb53fbd..6d1099410 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java +++ b/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java @@ -22,9 +22,9 @@ import net.i2p.util.Log; import net.i2p.util.VersionComparator; /** - * + * Methods for creating Tunnel Build Messages, i.e. requests */ -class BuildRequestor { +abstract class BuildRequestor { private static final List ORDER = new ArrayList(TunnelBuildMessage.MAX_RECORD_COUNT); static { for (int i = 0; i < TunnelBuildMessage.MAX_RECORD_COUNT; i++) @@ -37,7 +37,7 @@ class BuildRequestor { * expl. vs. client, uptime, and network conditions. * Put the expiration in the PTCC. * - * Also, perhaps, save the PTCC even after expiration for an extended time, + * Also, we now save the PTCC even after expiration for an extended time, * so can we use a successfully built tunnel anyway. * */ @@ -49,12 +49,16 @@ class BuildRequestor { /** some randomization is added on to this */ private static final int BUILD_MSG_TIMEOUT = 60*1000; + /** + * "paired tunnels" means using a client's own inbound tunnel to receive the + * reply for an outbound build request, and using a client's own outbound tunnel + * to send an inbound build request. + * This is more secure than using the router's exploratory tunnels, as it + * makes correlation of multiple clients more difficult. + */ private static boolean usePairedTunnels(RouterContext ctx) { - String val = ctx.getProperty("router.usePairedTunnels"); - if ( (val == null) || (Boolean.valueOf(val).booleanValue()) ) - return true; - else - return false; + return true; + //return ctx.getBooleanPropertyDefaultTrue("router.usePairedTunnels"); } /** new style requests need to fill in the tunnel IDs before hand */ @@ -321,9 +325,9 @@ class BuildRequestor { * Can't do this for inbound tunnels since the msg goes out an expl. tunnel. */ private static class TunnelBuildFirstHopFailJob extends JobImpl { - TunnelPool _pool; - PooledTunnelCreatorConfig _cfg; - BuildExecutor _exec; + final TunnelPool _pool; + final PooledTunnelCreatorConfig _cfg; + final BuildExecutor _exec; private TunnelBuildFirstHopFailJob(RouterContext ctx, TunnelPool pool, PooledTunnelCreatorConfig cfg, BuildExecutor exec) { super(ctx); _cfg = cfg; diff --git a/router/java/src/net/i2p/router/tunnel/pool/ExpireJob.java b/router/java/src/net/i2p/router/tunnel/pool/ExpireJob.java index e2271ec90..e82c4a8b0 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/ExpireJob.java +++ b/router/java/src/net/i2p/router/tunnel/pool/ExpireJob.java @@ -5,16 +5,21 @@ import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.router.tunnel.TunnelCreatorConfig; +/** + * This runs twice for each tunnel. + * The first time, remove it from the LeaseSet. + * The second time, stop accepting data for it. + */ class ExpireJob extends JobImpl { - private TunnelPool _pool; - private TunnelCreatorConfig _cfg; + private final TunnelPool _pool; + private final TunnelCreatorConfig _cfg; private boolean _leaseUpdated; - private long _dropAfter; + private final long _dropAfter; + public ExpireJob(RouterContext ctx, TunnelCreatorConfig cfg, TunnelPool pool) { super(ctx); _pool = pool; _cfg = cfg; - _leaseUpdated = false; // we act as if this tunnel expires a random skew before it actually does // so we rebuild out of sync. otoh, we will honor tunnel messages on it // up through the full lifetime of the tunnel, plus a clock skew, since @@ -28,9 +33,11 @@ class ExpireJob extends JobImpl { cfg.setExpiration(expire); getTiming().setStartAfter(expire); } + public String getName() { return "Expire tunnel"; } + public void runJob() { if (!_leaseUpdated) { _pool.removeTunnel(_cfg); diff --git a/router/java/src/net/i2p/router/tunnel/pool/TestJob.java b/router/java/src/net/i2p/router/tunnel/pool/TestJob.java index 7c64df958..e63e681b3 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TestJob.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TestJob.java @@ -22,9 +22,9 @@ import net.i2p.router.message.PayloadGarlicConfig; import net.i2p.util.Log; class TestJob extends JobImpl { - private Log _log; - private TunnelPool _pool; - private PooledTunnelCreatorConfig _cfg; + private final Log _log; + private final TunnelPool _pool; + private final PooledTunnelCreatorConfig _cfg; private boolean _found; private TunnelInfo _outTunnel; private TunnelInfo _replyTunnel; @@ -39,9 +39,10 @@ class TestJob extends JobImpl { public TestJob(RouterContext ctx, PooledTunnelCreatorConfig cfg, TunnelPool pool) { super(ctx); _log = ctx.logManager().getLog(TestJob.class); - _pool = pool; _cfg = cfg; - if (_pool == null) + if (pool != null) + _pool = pool; + else _pool = cfg.getTunnelPool(); if ( (_pool == null) && (_log.shouldLog(Log.ERROR)) ) _log.error("Invalid tunnel test configuration: no pool for " + cfg, new Exception("origin")); @@ -61,7 +62,9 @@ class TestJob extends JobImpl { ctx.statManager().createRateStat("tunnel.testAborted", "Tunnel test could not occur, since there weren't any tunnels to test with", "Tunnels", RATES); } + public String getName() { return "Test tunnel"; } + public void runJob() { if (_pool == null) return; @@ -246,9 +249,10 @@ class TestJob extends JobImpl { } private class ReplySelector implements MessageSelector { - private RouterContext _context; - private long _id; - private long _expiration; + private final RouterContext _context; + private final long _id; + private final long _expiration; + public ReplySelector(RouterContext ctx, long id, long expiration) { _context = ctx; _id = id; @@ -257,7 +261,9 @@ class TestJob extends JobImpl { } public boolean continueMatching() { return !_found && _context.clock().now() < _expiration; } + public long getExpiration() { return _expiration; } + public boolean isMatch(I2NPMessage message) { if (message instanceof DeliveryStatusMessage) { return ((DeliveryStatusMessage)message).getMessageId() == _id; @@ -280,9 +286,13 @@ class TestJob extends JobImpl { private class OnTestReply extends JobImpl implements ReplyJob { private long _successTime; private OutNetMessage _sentMessage; + public OnTestReply(RouterContext ctx) { super(ctx); } + public String getName() { return "Tunnel test success"; } + public void setSentMessage(OutNetMessage m) { _sentMessage = m; } + public void runJob() { if (_sentMessage != null) getContext().messageRegistry().unregisterPending(_sentMessage); @@ -292,6 +302,7 @@ class TestJob extends JobImpl { testFailed(_successTime); _found = true; } + // who cares about the details... public void setMessage(I2NPMessage message) { _successTime = getContext().clock().now() - ((DeliveryStatusMessage)message).getArrival(); @@ -310,12 +321,15 @@ class TestJob extends JobImpl { * Test failed (boo, hiss) */ private class OnTestTimeout extends JobImpl { - private long _started; + private final long _started; + public OnTestTimeout(RouterContext ctx) { super(ctx); _started = ctx.clock().now(); } + public String getName() { return "Tunnel test timeout"; } + public void runJob() { if (_log.shouldLog(Log.WARN)) _log.warn("Timeout: found? " + _found); diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java index 03fa5bd71..c432bfe9e 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java @@ -22,21 +22,21 @@ import net.i2p.stat.RateStat; import net.i2p.util.Log; /** - * + * A group of tunnels for the router or a particular client, in a single direction. */ public class TunnelPool { private final List _inProgress = new ArrayList(); - private RouterContext _context; - private Log _log; + private final RouterContext _context; + private final Log _log; private TunnelPoolSettings _settings; private final ArrayList _tunnels; - private TunnelPeerSelector _peerSelector; - private TunnelPoolManager _manager; + private final TunnelPeerSelector _peerSelector; + private final TunnelPoolManager _manager; private boolean _alive; private long _lifetimeProcessed; private TunnelInfo _lastSelected; private long _lastSelectionPeriod; - private int _expireSkew; + private final int _expireSkew; private long _started; private long _lastRateUpdate; private long _lastLifetimeProcessed; @@ -50,14 +50,9 @@ public class TunnelPool { _settings = settings; _tunnels = new ArrayList(settings.getLength() + settings.getBackupQuantity()); _peerSelector = sel; - _alive = false; - _lastSelectionPeriod = 0; - _lastSelected = null; - _lifetimeProcessed = 0; _expireSkew = _context.random().nextInt(90*1000); _started = System.currentTimeMillis(); _lastRateUpdate = _started; - _lastLifetimeProcessed = 0; _rateName = "tunnel.Bps." + (_settings.isExploratory() ? "exploratory" : _settings.getDestinationNickname()) + (_settings.isInbound() ? ".in" : ".out"); @@ -412,11 +407,12 @@ public class TunnelPool { } } + /** noop for outbound */ void refreshLeaseSet() { - if (_log.shouldLog(Log.DEBUG)) - _log.debug(toString() + ": refreshing leaseSet on tunnel expiration (but prior to grace timeout)"); - LeaseSet ls = null; if (_settings.isInbound() && (_settings.getDestination() != null) ) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug(toString() + ": refreshing leaseSet on tunnel expiration (but prior to grace timeout)"); + LeaseSet ls = null; synchronized (_tunnels) { ls = locked_buildNewLeaseSet(); } @@ -427,7 +423,7 @@ public class TunnelPool { } /** - * Return true if a fallback tunnel is built + * @return true if a fallback tunnel is built * */ boolean buildFallback() { @@ -851,6 +847,7 @@ public class TunnelPool { } PooledTunnelCreatorConfig configureNewTunnel() { return configureNewTunnel(false); } + private PooledTunnelCreatorConfig configureNewTunnel(boolean forceZeroHop) { TunnelPoolSettings settings = getSettings(); List peers = null; diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java index 3a53be552..59eec7d81 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java @@ -37,8 +37,8 @@ import net.i2p.util.SimpleTimer; * */ public class TunnelPoolManager implements TunnelManagerFacade { - private RouterContext _context; - private Log _log; + private final RouterContext _context; + private final Log _log; /** Hash (destination) to TunnelPool */ private final Map _clientInboundPools; /** Hash (destination) to TunnelPool */ @@ -61,7 +61,6 @@ public class TunnelPoolManager implements TunnelManagerFacade { _clientInboundPools = new ConcurrentHashMap(4); _clientOutboundPools = new ConcurrentHashMap(4); - _isShutdown = false; _executor = new BuildExecutor(ctx, this); I2PThread execThread = new I2PThread(_executor, "BuildExecutor"); execThread.setDaemon(true);