In the
previous article , the tasks needed to build an I2P marshutizer capable of participating in the network, including interacting with other marshutizers via the usual Internet, building different types of tunnels and collecting information about other nodes of the network, were considered. Despite the importance of these tasks, the I2P client, performing only the functions of a marshuff, from the user's point of view is a “thing in itself”, since it does not do anything interesting to the user. This article focuses on application layer protocols for transmitting user data over the I2P network.
If the issues of the I2P marshruzier's work are more or less logically consecrated in the official documentation, then the application protocols are a jumble of different ideas, which boil down to the fact that everyone is free to implement their own protocol for their own application, using the destination point as addresses. However, this does not at all bring closer to the implementation of a client of its own, since the existing network resources already use some protocols and any new implementation should be able to work with them. As an example, you can see the
description of "garlic" , from this page you can only understand how to pack "garlic" and how to encrypt. What is transmitted between Alice and Bob, and how Bob knows that the answer should be sent to Alice, is completely incomprehensible. It also contains the statement that, in order to confirm delivery, a DeliveryStatus message is transmitted in one of the “garnecines” with an indication of the sender marshulliser in the delivery instructions, thereby revealing the marshrutizer on which the sender sits. Of course it is not. Unfortunately, the only way to find out how things really were was to analyze the traffic generated by the official Java client.
“Garlic” data transfer
We will understand this issue in more detail, somewhat modifying the original example and making it more practical. Suppose that Vasya Pupkin refers to the Flibusta site, which is now available only through I2P, while Vasya has his own website that offends someone’s feelings, and therefore is in I2P. To do this, Vasya runs a marshutizer that constantly supports at least one outgoing and one incoming tunnel. Vasya created a separate destination for his website and published its address everywhere. To contact Flibusta, her address Vasya already knows, and his marshallizer knows her LeaseSet and can send a message there, the problem is that Vasya needs to receive a response from Flibusta, and for that she needs to know Vasya's address. To this end, another destination is created on the vasin marshrutizer, serving as a return address for all connections initiated by Vasya. Not only for Flibusta, but for all other sites. If necessary, you can create several such return addresses, but then you also have to build LeaseSets with different non-intersecting sets of tunnels.

Garlic's I2NP message is used to transfer data between destinations. The messages themselves are transmitted and encrypted between the routers, using a separate encryption key pair, the public key of which is transmitted to the LeaseSet. This key does not coincide with the public key of the marshrouser, and, unlike the latter, is generated with a new one at each start. Inside the message consists of "garlic", each of which consists of I2NP messages and instructions for its delivery (delivery instructions) of 4 types:
- Local. The message is intended for the marshutizer itself. As a rule, someone's LeaseSet.
- Destination point The message is intended to a destination connected to the marshutizer. It is the only way to send data to a destination.
- Tunnel. The message is intended to be sent to the specified inbound tunnel starting on the specified marshrutizer. Used to confirm the delivery of "garlic".
- Marshutizator. The message is intended to be sent to another marshutizer. Extremely dangerous from the point of view of security, if the specified marshutizer is different from its own or trusted. In practice, not met.
As a rule, "garlic" consisting of two "garlic" is used. The first is the I2NP message DeliveryStatus with the message number of the “garlic” itself and the delivery to one of the incoming tunnels of the sender’s marshrouter. Used to confirm the delivery of all "garlic" for its intended purpose. The second is the I2NP Data message, which contains the data being transmitted and delivered to the destination. Sometimes there is a third "garlic" - LeaseSet destination point (not marshutizator) of the sender. In our example, this is Vasya's LeaseSet return address. Such “garlic” is present in two cases: in the very first message and when the LeaseSet changes. This is done in order for the marshruzator to know how to send a response to the sender, otherwise you would have to request the corresponding LeaseSet from the floodfill marshutizers, which most likely will not be there, such as Vasya’s return address, and vice versa, the Vasya’s secret website will be present there.
The question arises, how does Flibusta know that the answer should be sent to Vasya, and not to Petya or anyone else who is turning to her at the same moment. Even if it was known that “garlic” came from Vasya's marshrutizer, which, of course, is not the case, it would not help much, because Vasya's marshrutizer also has its website and, possibly, a lot more . It turns out that this information should be contained within the data transmitted in the Data message, which indicates an unsuccessful design of the entire system, since it does not allow isolation of different levels of protocols from each other. In other words, the address of the destination must be present simultaneously in both the data itself and the protocol for transmitting this data.
')
I2CP protocol data
Initially, the I2CP protocol was developed exclusively for the exchange between different applications and the marshrutizer - its messages should not get into the I2P network itself. However, the contents of the SendMessageMessage and MessagePayloadMessage messages are transmitted over the network within I2NP Data messages, and are gzipped data with a specially modified view header.
0x1F 0x8B 0x08 - gzip prefix
1 byte of gzip flags
2 bytes TCP or UDP port of the sender
2 bytes TCP or UDP port of the receiver
1 byte of additional gzip flags
1 byte of protocol type: 6 - streaming (streaming), 17 - datagram (datagram), 18 - “raw” (raw)
Thus, each Data message transmitted via garlic will always begin with 0x1F 0x8B 0x08 and first of all unpacked gzip and, depending on the type of protocol, is processed by the corresponding implementation.
Streaming protocol
The streaming protocol is similar to the TCP protocol and guarantees the sequence of data transfer. Messages consist of a header and the actual data. The type of message is determined by the flags field; similarly to TCP, there are SYN / FIN flags for establishing and breaking the connection. Unlike TCP, there can also be up to 255 NACKs — the numbers of the missed messages with the requirement to send them again, which is more typical of packet protocols and requires a more complex implementation. It also contains standard TCP fields: 4-byte ports of the sender and receiver, called streams, sequence numbers and confirmation numbers.
When a connection is established, messages are exchanged with the SYNCHRONIZE flag set, and the FROM_INCLUDED and SIGNATURE_INCLUDED flags must be set. The first means that the full 387-byte I2P address is present in the header, and the second that the entire message is signed with the sender's I2P private key. Thus, the parties recognize each other’s I2P addresses, and verification of the signature ensures that these addresses are real. In other words, Vasya, connecting to the Flibusta address, after establishing the connection, can be sure that this is Flibusta, and Flibusta will know the return address of Vasya.
Thus, the interface of streaming protocols can be implemented as ordinary sockets, which allows the use of I2P in network applications with minimal changes.