gnunet − GNUnet
GNUnet in its current version is the result of almost 20 years of work from many contributors. So far, most contributions were made by volunteers or people paid to do fundamental research. At this stage, GNUnet remains an experimental system where significant parts of the software lack a reasonable degree of professionalism in its implementation. Furthermore, we are aware of a significant number of existing bugs and critical design flaws, as some unfortunate early design decisions remain to be rectified. There are still known open problems; GNUnet remains an active research project.
The project was started in 2001 when some initial ideas for improving Freenetâs file−sharing turned out to be too radical to be easily realized within the scope of the existing Freenet project. We lost our first contributor on 11.9.2001 as the contributor realized that privacy may help terrorists. The rest of the team concluded that it was now even more important to fight for civil liberties. The first release was called âGNetâ â already with the name GNUnet in mind, but without the blessing of GNU we did not dare to call it GNUnet immediately. A few months after the first release we contacted the GNU project, happily agreed to their governance model and became an official GNU package.
Within the first year, we created GNU libextractor, a helper library for meta data extraction which has been used by a few other projects as well. 2003 saw the emergence of pluggable transports, the ability for GNUnet to use different mechanisms for communication, starting with TCP, UDP and SMTP (support for the latter was later dropped due to a lack of maintenance). In 2005, the project first started to evolve beyond the original file−sharing application with a first simple P2P chat. In 2007, we created GNU libmicrohttpd to support a pluggable transport based on HTTP. In 2009, the architecture was radically modularized into the multi−process system that exists today. Coincidentally, the first version of the ARM service (ARM: Automatic Restart Manager) was implemented a day before systemd was announced. From 2009 to 2014 work progressed rapidly thanks to a significant research grant from the Deutsche Forschungsgesellschaft. This resulted in particular in the creation of the R5N DHT, CADET, ATS and the GNU Name System. In 2010, GNUnet was selected as the basis for the secushare online social network, resulting in a significant growth of the core team. In 2013, we launched GNU Taler to address the challenge of convenient and privacy−preserving online payments. In 2015, the pretty Easy privacy (pEp) project announced that they will use GNUnet as the technology for their meta−data protection layer, ultimately resulting in GNUnet e.V. entering into a formal long−term collaboration with the pEp Foundation. In 2016, Taler Systems SA, a first startup using GNUnet technology, was founded with support from the community.
GNUnet is not merely a technical project, but also a political mission: like the GNU project as a whole, we are writing software to achieve political goals with a focus on the human right of informational self−determination. Putting users in control of their computing has been the core driver of the GNU project. With GNUnet we are focusing on informational self−determination for collaborative computing and communication over networks.
The Internet is shaped as much by code and protocols as it is by its associated political processes (IETF, ICANN, IEEE, etc.). Similarly its flaws are not limited to the protocol design. Thus, technical excellence by itself will not suffice to create a better network. We also need to build a community that is wise, humble and has a sense of humor to achieve our goal to create a technical foundation for a society we would like to live in.
Project
governance
GNUnet, like the GNU project and many other free software
projects, follows the governance model of a benevolent
dictator. This means that ultimately, the GNU project
appoints the GNU maintainer and can overrule decisions made
by the GNUnet maintainer. Similarly, the GNUnet maintainer
can overrule any decisions made by individual developers.
Still, in practice neither has happened in the last 20 years
for GNUnet, and we hope to keep it that way.
The current maintainers of GNUnet are:
• |
Christian Grothoff |
|||
• |
Martin Schanzenbach |
The GNUnet project is supported by GNUnet e.V., a German association where any developer can become a member. GNUnet e.V. serves as a legal entity to hold the copyrights to GNUnet. GNUnet e.V. may also choose to pay for project resources, and can collect donations as well as choose to adjust the license of the software (with the constraint that it has to remain free software). In 2018 we switched from GPL3 to AGPL3, in practice these changes do not happen very often.
Philosophy
The primary goal of the GNUnet project is to provide a
reliable, open, non−discriminating and
censorship−resistant system for information exchange.
We value free speech above state interests and intellectual
monopoly. GNUnetâs long−term goal is to serve as
a development platform for the next generation of Internet
protocols.
Participants are encouraged to contribute at least as much resources (storage, bandwidth) to the network as they consume, so that their participation does not have a negative impact on other users.
Design
Principles
These are the GNUnet design principles, in order of
importance:
• |
GNUnet must be implemented as Free Software â This means that you have the four essential freedoms: to run the program, to study and change the program in source code form, to redistribute exact copies, and to distribute modified versions. (- https://www.gnu.org/philosophy/free−sw.html). | ||
• |
GNUnet must minimize the amount of personally identifiable information exposed. | ||
• |
GNUnet must be fully distributed and resilient to external attacks and rogue participants. | ||
• |
GNUnet must be self−organizing and not depend on administrators or centralized infrastructure. | ||
• |
GNUnet must inform the user which other participants have to be trusted when establishing private communications. | ||
• |
GNUnet must be open and permit new peers to join. | ||
• |
GNUnet must support a diverse range of applications and devices. | ||
• |
GNUnet must use compartmentalization to protect sensitive information. | ||
• |
The GNUnet architecture must be resource efficient. | ||
• |
GNUnet must provide incentives for peers to contribute more resources than they consume. |
Privacy and
Anonymity
The GNUnet protocols minimize the leakage of personally
identifiable information of participants and do not allow
adversaries to control, track, monitor or censor users
activities. The GNUnet protocols also make it as hard as
possible to disrupt operations by participating in the
network with malicious intent.
Analyzing participantâs activities becomes more difficult as the number of peers and applications that generate traffic on the network grows, even if the additional traffic generated is not related to anonymous communication. This is one of the reasons why GNUnet is developed as a peer−to−peer framework where many applications share the lower layers of an increasingly complex protocol stack. The GNUnet architecture encourages many different forms of peer−to−peer applications.
Practicality
Wherever possible GNUnet allows the peer to adjust its
operations and functionalities to specific use cases. A
GNUnet peer running on a mobile device with limited battery
for example might choose not to relay traffic for other
participants.
For certain applications like file−sharing GNUnet allows participants to trade degrees of anonymity in exchange for increased efficiency. However, it is not possible for any userâs efficiency requirements to compromise the anonymity of any other user.
Key
Concepts
In this section, the fundamental concepts of GNUnet are
explained. Most of them are also described in our research
papers. First, some of the concepts used in the GNUnet
framework are detailed. The second part describes concepts
specific to anonymous file−sharing.
Authentication
Almost all peer−to−peer communications in GNUnet
are between mutually authenticated peers. The authentication
works by using ECDHE, that is a DH (DiffieâHellman)
key exchange using ephemeral elliptic curve cryptography.
The ephemeral ECC (Elliptic Curve Cryptography) keys are
signed using ECDSA. The shared secret from ECDHE is used to
create a pair of session keys (using HKDF) which are then
used to encrypt the communication between the two peers
using both 256−bit AES (Advanced Encryption Standard)
and 256−bit Twofish (with independently derived secret
keys). As only the two participating hosts know the shared
secret, this authenticates each packet without requiring
signatures each time. GNUnet uses SHA−512 (Secure Hash
Algorithm) hash codes to verify the integrity of
messages.
In GNUnet, the identity of a host is its public key. For that reason, man−in−the−middle attacks will not break the authentication or accounting goals. Essentially, for GNUnet, the IP of the host has nothing to do with the identity of the host. As the public key is the only thing that truly matters, faking an IP, a port or any other property of the underlying transport protocol is irrelevant. In fact, GNUnet peers can use multiple IPs (IPv4 and IPv6) on multiple ports â or even not use the IP protocol at all (by running directly on layer 2).
GNUnet uses a special type of message to communicate a binding between public (ECC) keys to their current network address. These messages are commonly called HELLOs or peer advertisements. They contain the public key of the peer and its current network addresses for various transport services. A transport service is a special kind of shared library that provides (possibly unreliable, out−of−order) message delivery between peers. For the UDP and TCP transport services, a network address is an IP and a port. GNUnet can also use other transports (HTTP, HTTPS, WLAN, etc.) which use various other forms of addresses. Note that any node can have many different active transport services at the same time, and each of these can have a different addresses. Binding messages expire after at most a week (the timeout can be shorter if the user configures the node appropriately). This expiration ensures that the network will eventually get rid of outdated advertisements.
For more information, refer to the following paper:
Ronaldo A. Ferreira, Christian Grothoff, and Paul Ruth. A Transport Layer Abstraction for Peer−to−Peer Networks Proceedings of the 3rd International Symposium on Cluster Computing and the Grid (GRID 2003), 2003. (- https://git.gnunet.org/bibliography.git/plain/docs/transport.pdf)
Accounting
to Encourage Resource Sharing
Most distributed P2P networks suffer from a lack of defenses
or precautions against attacks in the form of freeloading.
While the intentions of an attacker and a freeloader are
different, their effect on the network is the same; they
both render it useless. Most simple attacks on networks such
as Gnutella involve flooding the network with traffic,
particularly with queries that are, in the worst case,
multiplied by the network.
In order to ensure that freeloaders or attackers have a minimal impact on the network, GNUnetâs file−sharing implementation (FS) tries to distinguish good (contributing) nodes from malicious (freeloading) nodes. In GNUnet, every file−sharing node keeps track of the behavior of every other node it has been in contact with. Many requests (depending on the application) are transmitted with a priority (or importance) level. That priority is used to establish how important the sender believes this request is. If a peer responds to an important request, the recipient will increase its trust in the responder: the responder contributed resources. If a peer is too busy to answer all requests, it needs to prioritize. For that, peers do not take the priorities of the requests received at face value. First, they check how much they trust the sender, and depending on that amount of trust they assign the request a (possibly lower) effective priority. Then, they drop the requests with the lowest effective priority to satisfy their resource constraints. This way, GNUnetâs economic model ensures that nodes that are not currently considered to have a surplus in contributions will not be served if the network load is high.
For more information, refer to the following paper: Christian Grothoff. An Excess−Based Economic Model for Resource Allocation in Peer−to−Peer Networks. Wirtschaftsinformatik, June 2003. (- https://git.gnunet.org/bibliography.git/plain/docs/ebe.pdf)
Confidentiality
Adversaries (malicious, bad actors) outside of GNUnet are
not supposed to know what kind of actions a peer is involved
in. Only the specific neighbor of a peer that is the
corresponding sender or recipient of a message may know its
contents, and even then application protocols may place
further restrictions on that knowledge. In order to ensure
confidentiality, GNUnet uses link encryption, that is each
message exchanged between two peers is encrypted using a
pair of keys only known to these two peers. Encrypting
traffic like this makes any kind of traffic analysis much
harder. Naturally, for some applications, it may still be
desirable if even neighbors cannot determine the concrete
contents of a message. In GNUnet, this problem is addressed
by the specific application−level protocols. See for
example the following sections: Anonymity, see How
file−sharing achieves Anonymity, and see
Deniability.
Anonymity
Providing anonymity for users is the central goal for the
anonymous file−sharing application. Many other design
decisions follow in the footsteps of this requirement.
Anonymity is never absolute. While there are various
scientific metrics (Claudia DÃaz, Stefaan Seys, Joris
Claessens, and Bart Preneel. Towards measuring anonymity.
2002. (-
https://git.gnunet.org/bibliography.git/plain/docs/article−89.pdf))
that can help quantify the level of anonymity that a given
mechanism provides, there is no such thing as
âcomplete anonymityâ.
GNUnetâs file−sharing implementation allows users to select for each operation (publish, search, download) the desired level of anonymity. The metric used is based on the amount of cover traffic needed to hide the request.
While there is no clear way to relate the amount of available cover traffic to traditional scientific metrics such as the anonymity set or information leakage, it is probably the best metric available to a peer with a purely local view of the world, in that it does not rely on unreliable external information or a particular adversary model.
The default anonymity level is 1, which uses anonymous routing but imposes no minimal requirements on cover traffic. It is possible to forego anonymity when this is not required. The anonymity level of 0 allows GNUnet to use more efficient, non−anonymous routing.
How
file−sharing achieves Anonymity
Contrary to other designs, we do not believe that users
achieve strong anonymity just because their requests are
obfuscated by a couple of indirections. This is not
sufficient if the adversary uses traffic analysis. The
threat model used for anonymous file sharing in GNUnet
assumes that the adversary is quite powerful. In particular,
we assume that the adversary can see all the traffic on the
Internet. And while we assume that the adversary can not
break our encryption, we assume that the adversary has many
participating nodes in the network and that it can thus see
many of the node−to−node interactions since it
controls some of the nodes.
The system tries to achieve anonymity based on the idea that users can be anonymous if they can hide their actions in the traffic created by other users. Hiding actions in the traffic of other users requires participating in the traffic, bringing back the traditional technique of using indirection and source rewriting. Source rewriting is required to gain anonymity since otherwise an adversary could tell if a message originated from a host by looking at the source address. If all packets look like they originate from one node, the adversary can not tell which ones originate from that node and which ones were routed. Note that in this mindset, any node can decide to break the source−rewriting paradigm without violating the protocol, as this only reduces the amount of traffic that a node can hide its own traffic in.
If we want to hide our actions in the traffic of other nodes, we must make our traffic indistinguishable from the traffic that we route for others. As our queries must have us as the receiver of the reply (otherwise they would be useless), we must put ourselves as the receiver of replies that actually go to other hosts; in other words, we must indirect replies. Unlike other systems, in anonymous file−sharing as implemented on top of GNUnet we do not have to indirect the replies if we donât think we need more traffic to hide our own actions.
This increases the efficiency of the network as we can indirect less under higher load. Refer to the following paper for more: Krista Bennett and Christian Grothoff. GAP â practical anonymous networking. In Proceedings of Designing Privacy Enhancing Technologies, 2003. (- https://git.gnunet.org/bibliography.git/plain/docs/aff.pdf)
How
messaging provided Anonymity
While the file−sharing tries to achieve anonymity
through hiding actions in other traffic, the messaging
service provides a weaker form of protection against
identification.
The messaging service allows the use of an anonymous ego for the signing and verification process of messages instead of a unique ego. This anonymous ego is a publicly known key pair which is shared between all peers in GNUnet.
Using this ego only ensures that individual messages alone canât identify its sender inside of a messenger room. It should be clarified that the route of the traffic for each message can still be tracked to identify the senders peer inside of a messenger room if the threat agent controls certain peers hosting the room.
Also opening a room in the messenger service will potentially match your peer identity with the internal member identity from the messenger service. So despite using the anonymous ego you can reveal your peer identity. This means to decrease the chance of being identified, it is recommended to enter rooms but you should not open them for others.
Deniability
Even if the user that downloads data and the server that
provides data are anonymous, the intermediaries may still be
targets. In particular, if the intermediaries can find out
which queries or which content they are processing, a strong
adversary could try to force them to censor certain
materials.
With the file−encoding used by GNUnetâs anonymous file−sharing, this problem does not arise. The reason is that queries and replies are transmitted in an encrypted format such that intermediaries cannot tell what the query is for or what the content is about. Mind that this is not the same encryption as the link−encryption between the nodes. GNUnet has encryption on the network layer (link encryption, confidentiality, authentication) and again on the application layer (provided by gnunet−publish, gnunet−download, gnunet−search and gnunet−fs−gtk).
Refer to the following paper for more: Christian Grothoff, Krista Grothoff, Tzvetan Horozov, and Jussi T. Lindgren. An Encoding for Censorship−Resistant Sharing. 2009. (- https://git.gnunet.org/bibliography.git/plain/docs/ecrs.pdf)
Peer
Identities
Peer identities are used to identify peers in the network
and are unique for each peer. The identity for a peer is
simply its public key, which is generated along with a
private key when the peer is started for the first time.
While the identity is binary data, it is often expressed as
an ASCII string. For example, the following is a peer
identity as you might see it in various places:
UAT1S6PMPITLBKSJ2DGV341JI6KF7B66AC4JVCN9811NNEGQLUN0
You can find your peer identity by running gnunet−peerinfo −s.
Zones in the
GNU Name System (GNS Zones)
GNS zones are similar to those of DNS zones, but instead of
a hierarchy of authorities to governing their use, GNS zones
are controlled by a private key. When you create a record in
a DNS zone, that information is stored in your nameserver.
Anyone trying to resolve your domain then gets pointed
(hopefully) by the centralised authority to your nameserver.
Whereas GNS, being fully decentralized by design, stores
that information in DHT. The validity of the records is
assured cryptographically, by signing them with the private
key of the respective zone.
Anyone trying to resolve records in a zone of your domain can then verify the signature of the records they get from the DHT and be assured that they are indeed from the respective zone. To make this work, there is a 1:1 correspondence between zones and their public−private key pairs. So when we talk about the owner of a GNS zone, thatâs really the owner of the private key. And a user accessing a zone needs to somehow specify the corresponding public key first.
For more information, refer to the following paper:
Matthias Wachs, Martin Schanzenbach, and Christian Grothoff. A Censorship−Resistant, Privacy−Enhancing and Fully Decentralized Name System. In proceedings of 13th International Conference on Cryptology and Network Security (CANS 2014). 2014. https://git.gnunet.org/bibliography.git/plain/docs/gns2014wachs.pdf
Egos
Egos are your âidentitiesâ in GNUnet. Any user
can assume multiple identities, for example to separate
their activities online. Egos can correspond to
âpseudonymsâ or âreal−world
identitiesâ. Technically an ego is first of all a key
pair of a public− and private−key.
This guide is intended for those who want to install GNUnet from source. For instructions on how to install GNUnet as a binary package please refer to the official documentation of your operating system or package manager.
For understanding this guide properly it is important to know that there are two different ways of running GNUnet:
• |
the single−user setup |
|||
• |
the multi−user setup |
The latter variant has a better security model and requires extra preparation before running make install and a different configuration. Beginners who want to quickly try out GNUnet can use the single−user setup.
Dependencies
GNUnet needs few libraries and applications for being able
to run and another few optional ones for using certain
features. Preferably they should be installed with a package
manager.
The mandatory libraries and applications are
• |
autoconf 2.59 or above (when building from git) | ||
• |
automake 1.11.1 or above (when building from git) | ||
• |
recutils 1.0 or above (when building from git) | ||
• |
gettext | ||
• |
glibc (read below, other libcs work) | ||
• |
GnuTLS 3.2.12 or above, recommended to be linked against libunbound | ||
• |
GNU make 4.0 or higher (other make implementations do work) | ||
• |
iptables (on Linux systems) | ||
• |
libtool 2.2 or above | ||
• |
libltdl (part of libtool) | ||
• |
libgcrypt 1.6 or above | ||
• |
libidn2 or libidn | ||
• |
libmicrohttpd 0.9.63 or above | ||
• |
libunistring | ||
• |
libjansson | ||
• |
libjose (optional, for reclaimID) | ||
• |
libgmp | ||
• |
libgnurl or libcurl (libcurl has to be linked to GnuTLS) 7.35.0 or above | ||
• |
Texinfo 5.2 or above (for building the documentation) | ||
• |
Texlive 2012 or above (for building the documentation, and for gnunet−bcd) | ||
• |
makeinfo 4.8 or above | ||
• |
pkgconf (or pkg−config) | ||
• |
zlib |
Glibc is required for certain NSS features:
One mechanism of integrating GNS with legacy applications via NSS is not available if this is disabled. But applications that don't use the glibc for NS resolution won't work anyway with this, so little is lost on *BSD systems. GNS via direct use or via the HTTP or DNS proxies is unaffected.
Other libcs should work, the resulting builds just donât include the glibc NSS specific code. One example is the build against NetBSDâs libc as detailed in https://bugs.gnunet.org/view.php?id=5605.
In addition GNUnet needs at least one of these three databases (at the minimum sqlite3)
• |
sqlite + libsqlite 3.8 or above (the default, requires no further configuration) | ||
• |
postgres + libpq | ||
• |
mysql + libmysqlclient |
These are the dependencies only required for certain features
• |
miniupnpc (for traversing NAT boxes more reliably) | ||
• |
libnss | ||
• |
libopus (for running the GNUnet conversation telephony application) | ||
• |
libogg (for running the GNUnet conversation telephony application) | ||
• |
gstreamer OR libpulse (for running the GNUnet conversation telephony application) | ||
• |
bluez (for bluetooth support) | ||
• |
libextractor (optional but highly recommended, read below) | ||
• |
texi2mdoc (for automatic mdoc generation) | ||
• |
perl5 for some utilities (which are not installed) |
About libextractor being optional:
While libextractor ("LE") is optional, it is recommended to build gnunet against it. If you install it later, you won't benefit from libextractor. If you are a distributor, we recommend to split LE into basis + plugins rather than making LE an option as an afterthought by the user. LE itself is very small, but its dependency chain on first, second, third etc level can be big. There is a small effect on privacy if your LE build differs from one which includes all plugins (plugins are build as shared objects): if users publish a directory with a mixture of file types (for example mpeg, jpeg, png, gif) the configuration of LE could leak which plugins are installed for which filetypes are not providing more details. However, this leak is just a minor concern.
These are the test−suite requirements:
• |
python3.6 or higher | ||
• |
gnunet (installation first) | ||
• |
some core−utils: which(1), bc(1), curl(1), sed(1), awk(1), etc. | ||
• |
a shell (very few Bash scripts, the majority are POSIX sh scripts) |
These are runtime requirements:
• |
nss (the certutil binary, for gnunet−gns−proxy−setup−ca) | ||
• |
openssl (openssl binary, for gnunet−gns−proxy−setup−ca) |
Getting the
Source Code
You can either download the source code using git (you
obviously need git installed) or as an archive.
Using git type
$ git clone https://git.gnunet.org/gnunet.git
The archive can be found at https://ftpmirror.gnu.org/gnu/gnunet/. Extract it using a graphical archive tool or tar:
tar xf gnunet−0.17.1−26−g233ec6111.tar.gz
In the next chapter we will assume that the source code is available in the home directory at ~/gnunet.
Create user
and groups for the system services
For single−user setup this section can be
skipped
The multi−user setup means that there are system services, which are run once per machine as a dedicated system user (called gnunet) and user services which can be started by every user who wants to use GNUnet applications. The user services communicate with the system services over unix domain sockets. To gain permissions to read and write those sockets the users running GNUnet applications will need to be in the gnunet group. In addition the group gnunetdns may be needed (see below).
Create user gnunet who is member of the group gnunet (automatically created) and specify a home directory where the GNUnet services will store persistent data such as information about peers.
$ sudo useradd −−system −−home−dir /var/lib/gnunet −−create−home gnunet
Now add your own user to the gnunet group:
$ sudo usermod −aG gnunet <user>
Create a group gnunetdns. This allows using setgid in a way that only the DNS service can run the gnunet−helper−dns binary. This is only needed if system−wide DNS interception will be used. For more information see Configuring system−wide DNS interception.
$ sudo groupadd gnunetdns
Preparing
and Compiling the Source Code
For preparing the source code for compilation a bootstrap
script and configure has to be run from the source code
directory. When running configure, options can be specified
to customize the compilation and installation process. For
details execute:
$ ./configure −−help
The following example configures the installation prefix /usr/local and disables building the documentation
$ cd ~/gnunet $ ./bootstrap $ ./configure −−prefix=/usr/local −−disable−documentation
After running the bootstrap script and configure successfully the source code can be compiled and the compiled binaries can be installed using:
$ make $ make install
The latter command may need to be run as root (or with sudo) because some binaries need the suid bit set. Without that some features (e.g. the VPN service, system−wide DNS interception, NAT traversal using ICMP) will not work.
NSS plugin
(optional)
NOTE: The installation of the NSS plugin is only necessary
if GNS resolution shall be used with legacy applications
(that only support DNS) and if you cannot do not want to use
the DNS2GNS service.
One important library is the GNS plugin for NSS (the name services switch) which allows using GNS (the GNU name system) in the normal DNS resolution process. Unfortunately NSS expects it in a specific location (probably /lib) which may differ from the installation prefix (see −−prefix option in the previous section). This is why the plugin has to be installed manually.
Find the directory where nss plugins are installed on your system, e.g.:
$ ls −l /lib/libnss_* /lib/libnss_mymachines.so.2 /lib/libnss_resolve.so.2 /lib/libnss_myhostname.so.2 /lib/libnss_systemd.so.2
Copy the GNS NSS plugin to that directory:
cp ~/gnunet/src/gns/nss/.libs/libnss_gns.so.2 /lib
Now, to activate the plugin, you need to edit your /etc/nsswitch.conf where you should find a line like this:
hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4
The exact details may differ a bit, which is fine. Add the text gns [NOTFOUND=return] after files:
hosts: files gns [NOTFOUND=return] mdns4_minimal [NOTFOUND=return] dns mdns4
Installing
the GNS Certificate Authority (Optional)
NOTE: Installing the GNS certificate authority is only
necessary if GNS shall be used in a browser and if you
cannot or do not want to use the DNS2GNS service.
The GNS Certificate authority can provide TLS certificates for GNS names while downloading webpages from legacy webservers. This allows browsers to use HTTPS in combinations with GNS name resolution.
To install it execute the GNS CA−setup script. So far Firefox and Chromium are supported.
$ gnunet−gns−proxy−setup−ca
A local proxy server, that takes care of the name resolution and provides certificates on−the−fly needs to be started:
$ /usr/lib/gnunet/libexec/gnunet−gns−proxy
Now GNS should work in browsers that are configured to use a SOCKS proxy on localhost:7777.
Minimal
configuration
GNUnet needs a configuration file to start (see Config
file format). For the single−user setup an
empty file is sufficient:
$ touch ~/.config/gnunet.conf
For the multi−user setup we need an extra config file for the system services. The default location is /etc/gnunet.conf. The minimal content of that file which activates the system services roll is:
[arm] START_SYSTEM_SERVICES = YES START_USER_SERVICES = NO
The config file for the user services (~/.config/gnunet.conf) needs the opposite configuration to activate the user services roll:
[arm] START_SYSTEM_SERVICES = NO START_USER_SERVICES = YES
This tutorial is supposed to give a first introduction for users trying to do something real with GNUnet. Installation and configuration are specifically outside of the scope of this tutorial. Instead, we start by briefly checking that the installation works, and then dive into uncomplicated, concrete practical things that can be done with the framework provided by GNUnet.
In short, this chapter of the âGNUnet Reference Documentationâ will show you how to use the various peer−to−peer applications of the GNUnet system. As GNUnet evolves, we will add new sections for the various applications that are being created.
Comments on the content of this chapter, and extensions of it are always welcome.
Starting and
stopping
Prior to using any GNUnet−based application, one has
to start a node:
$ gnunet−arm −s
To stop GNUnet:
$ gnunet−arm −e
You can usually find the logs under ~/.cache/gnunet and all files such as databases and private keys in ~/.local/share/gnunet.
The list of running services can be displayed using the −I option. It should look similar to this example:
$ gnunet−arm −I Running services: topology (gnunet−daemon−topology) nat (gnunet−service−nat) vpn (gnunet−service−vpn) gns (gnunet−service−gns) cadet (gnunet−service−cadet) namecache (gnunet−service−namecache) hostlist (gnunet−daemon−hostlist) revocation (gnunet−service−revocation) ats (gnunet−service−ats) peerinfo (gnunet−service−peerinfo) zonemaster (gnunet−service−zonemaster) zonemaster−monitor (gnunet−service−zonemaster−monitor) dht (gnunet−service−dht) namestore (gnunet−service−namestore) set (gnunet−service−set) statistics (gnunet−service−statistics) nse (gnunet−service−nse) fs (gnunet−service−fs) peerstore (gnunet−service−peerstore) core (gnunet−service−core) rest (gnunet−rest−server) transport (gnunet−service−transport) datastore (gnunet−service−datastore)
For the multi−user setup first the system services need to be started as the system user, i.e. the user gnunet needs to execute gnunet−arm −s. This should be done by the systemâs init system. Then the user who wants to start GNUnet applications has to run gnunet−arm −s, too. It is recommended to automate this, e.g. using the userâs crontab.
First, you should launch the peer information tool. You can do this from the command−line by typing:
$ gnunet−peerinfo
Once you have done this, you will see a list of known peers. If hardly any peers are listed, there is likely a problem with your network configuration. You can also check directly connected peers with:
$ gnunet−core
This should return (at least) one established connection peer. Otherwise, again, there is likely a problem with your network configuration.
The GNU Name
System
The GNU Name System (GNS) is secure and decentralized naming
system. It allows its users to register names as
top−level domains (TLDs) and resolve other namespaces
within their TLDs.
GNS is designed to provide:
• |
Censorship resistance |
|||
• |
Query privacy |
|||
• |
Secure name resolution |
|||
• |
Compatibility with DNS |
For the initial configuration and population of your GNS installation, please follow the GNS setup instructions. The remainder of this chapter will provide some background on GNS and then describe how to use GNS in more detail.
Unlike DNS, GNS does not rely on central root zones or authorities. Instead any user administers their own root and can can create arbitrary name value mappings. Furthermore users can delegate resolution to other usersâ zones just like DNS NS records do. Zones are uniquely identified via public keys and resource records are signed using the corresponding public key. Delegation to another userâs zone is done using special PKEY records and petnames. A petname is a name that can be freely chosen by the user. This results in non−unique name−value mappings as www.bob to one user might be www.friend for someone else.
Start
Zones
In the default configuration, there are two zones defined
and shipped with GNUnet:
The first is âgnunet.orgâ, which points to the authoritate zone of the GNUnet project. It can be used to resolve, for example, âwww.gnunet.orgâ.
â.pinâ is another default zone which points to a special zone also managed by gnunet.org. Users may register submodomains on a first−come first−served−basis at https://fcfs.gnunet.org.
Use gnunet−config −s gns to view the GNS configuration, including all configured zones that are operated by other users. The respective configuration entry names start with a â.â, e.g. â.pinâ.
You can configure any number of top−level domains, and point them to the respective zones of your friends! For this, simply obtain the respective public key (you will learn how below) and extend the configuration:
$ gnunet−config −s gns −o .myfriend −V PUBLIC_KEY
Zones and
Egos
In GNUnet, identity management is about managing egos. Egos
can correspond to pseudonyms or real−world identities.
If you value your privacy, you are encouraged to use
separate egos for separate activities.
Technically, an ego is first of all a public−private key pair, and thus egos also always correspond to a GNS zone. Egos are managed by the IDENTITY service. Note that this service has nothing to do with the peer identity. The IDENTITY service essentially stores the private keys under human−readable names, and keeps a mapping of which private key should be used for particular important system functions.
You probably should create at least one zone of your own. You can create any number of zones using the gnunet−identity tool using:
$ gnunet−identity −−create="myzone"
Henceforth, on your system you control the TLD âmyzoneâ.
All of your zones can be listed (displayed) using the gnunet−identity command line tool as well:
$ gnunet−identity −−display
Maintaining
Zones
Now you can add (or edit, or remove) records in your GNS
zone using the gnunet−namestore−gtk GUI or using
the gnunet−namestore command−line tool. In
either case, your records will be stored in an SQL database
under control of the gnunet−service−namestore.
Note that if multiple users use one peer, the namestore
database will include the combined records of all users.
However, users will not be able to see each otherâs
records if they are marked as private.
To provide a short example for editing your own zone, suppose you have your own web server with the IP 1.2.3.4. Then you can put an A record (A records in DNS are for IPv4 IP addresses) into your local zone âmyzoneâ using the command:
$ gnunet−namestore −z myzone −a −n www −t A −V 1.2.3.4 −e 1d
Similar commands will work for other types of DNS and GNS records, the syntax largely depending on the type of the record. Naturally, most users may find editing the zones using the gnunet−namestore−gtk GUI to be easier.
Each zone in GNS has a public−private key. Usually, gnunet−namestore and gnunet−setup will access your private key as necessary, so you do not have to worry about those. What is important is your public key (or rather, the hash of your public key), as you will likely want to give it to others so that they can securely link to you.
A central operation in GNS is the ability to securely delegate to other zones. Basically, by adding a delegation you make all of the names from the other zone available to yourself. This section describes how to create delegations.
Suppose you have a friend who you call âbobâ who also uses GNS. You can then delegate resolution of names to Bobâs zone by adding a PKEY record to their local zone:
$ gnunet−namestore −a −n bob −−type PKEY −V XXXX −e 1d −z myzone
Note that âXXXXâ in the command above must be replaced with the hash of Bobâs public key (the output your friend obtained using the gnunet−identity command from the previous section and told you, for example by giving you a business card containing this information as a QR code).
Assuming Bob has an âAâ record for their website under the name of âwwwâ in his zone, you can then access Bobâs website under âwww.bob.myzoneâ â as well as any (public) GNS record that Bob has in their zone by replacing www with the respective name of the record in Bobâs zone.
Furthermore, if Bob has themselves a (public) delegation to Carolâs zone under âcarolâ, you can access Carolâs records under âNAME.carol.bob.myzoneâ (where âNAMEâ is the name of Carolâs record you want to access).
Resolving
GNS records
Next, you should try resolving your own GNS records. The
method we found to be the most uncomplicated is to do this
by explicitly resolving using gnunet−gns. For this
exercise, we will assume that you used the string
âgnuâ for the pseudonym (or label) of your GNS
zone. If you used something else, replace â.gnuâ
with your real pseudonym in the examples below.
In the shell, type:
$ gnunet−gns −u www.gnunet.org www.gnunet.org: Got `A' record: ...
That shows that resolution works, once GNS is integrated with the application.
Integration
with Browsers (DNS2GNS service)
Most OSes allow you to either modify your
/etc/resolv.conf directly or through
resolvectl. We are going to configure the dns2gns
service in order to translate DNS name queries by
applications to GNS name queries where applicable and else
fall back to DNS.
Optionally, you may want to configure your dns2gns service to run on a non−priviledged port like 5353. But, in case you are going to edit /etc/resolv.conf directly, the dns2gns service MUST run on port 53 as you cannot specify the port number. A $FALLBACK_DNS variable should be a DNS server you trust such as your local router:
$ gnunet−config −s dns2gns −o OPTIONS −V "−d $FALLBACK_DNS −p 5252" $ gnunet−arm −i dns2gns # Make sure the service is started
If you edit your resolv.conf directly, it should contain and entry like this:
nameserver 127.0.0.1
In any case, it is very likely that the method of modification of your resolver is OS specific. Recently, the combination of NetworkManager and systemd−resolved is becoming increasingly popular.
If you use resolvectl and systemd−resolved you can temporarily set the nameserver like this:
$ resolvectl $INTERFACE 127.0.0.1:5353
Where $INTERFACE is your network interface such as eth0.
In order to automatically set the DNS2GNS server if it is running already you can use NetworkManager−dispatcher. First, enable it:
$ sudo systemctl enable NetworkManager−dispatcher.service $ sudo systemctl start NetworkManager−dispatcher.service
Then, create a script /etc/NetworkManager/dispatch.h/10−dns2−gns.sh:
#!/bin/sh interface=$1 status=$2 if [ "$interface" = "eth0" ]; then case $status in up) if nc −u −z 127.0.0.1 5353; then resolvectl dns $interface 127.0.0.1:5353 fi ;; down) ;; esac fi
Make sure the script is owned by root and executable:
$ sudo root:root /etc/NetworkManager/dispatch.d/10−dns2gns.sh $ sudo +x /etc/NetworkManager/dispatch.d/10−dns2gns.sh
You can test accessing this website using your browser or curl:
$ curl www.gnunet.org
Note that âgnunet.orgâ is a domain that also exists in DNS and for which the GNUnet project webservers can provide trusted TLS certificates. When using non−DNS names with GNS or aliases, this may result in issues when accessing HTTPS websites with browsers. In order learn how to provide relief for this issue, read on.
Integration
with Browsers (SOCKS proxy)
While we recommend integrating GNS using the DNS2GNS service
or the NSSwitch plugin, you can also integrate GNS directly
with your browser via the gnunet−gns−proxy. This
method can have the advantage that the proxy can validate
TLS/X.509 records and thus strengthen web security; however,
the proxy is still a bit brittle, so expect subtle failures.
We have had reasonable success with Chromium, and various
frustrations with Firefox in this area recently.
The first step is to start the proxy. As the proxy is (usually) not started by default, this is done as a unprivileged user using gnunet−arm −i gns−proxy. Use gnunet−arm −I as a unprivileged user to check that the proxy was actually started. (The most common error for why the proxy may fail to start is that you did not run gnunet−gns−proxy−setup−ca during installation.) The proxy is a SOCKS5 proxy running (by default) on port 7777. Thus, you need to now configure your browser to use this proxy. With Chromium, you can do this by starting the browser as a unprivileged user using chromium âproxy−server=âsocks5://localhost:7777â For Firefox (or Icecat), select âEdit−Preferencesâ in the menu, and then select the âAdvancedâ tab in the dialog and then âNetworkâ:
Here, select âSettingsâ¦â to open the proxy settings dialog. Select âManual proxy configurationâ and enter localhost with port 7777 under SOCKS Host. Furthermore, set the checkbox âProxy DNS when using SOCKS v5â at the bottom of the dialog. Finally, push âOKâ.
You must also go to about:config and change the browser.fixup.alternate.enabled option to false, otherwise the browser will autoblunder an address like www.gnu to www.gnu.com. If you want to resolve @ in your own TLDs, you must additionally set browser.fixup.dns_first_use_for_single_words to true.
After configuring your browser, you might want to first confirm that it continues to work as before. (The proxy is still experimental and if you experience âoddâ failures with some webpages, you might want to disable it again temporarily.) Next, test if things work by typing â- http://test.gnu/â into the URL bar of your browser. This currently fails with (my version of) Firefox as Firefox is super−smart and tries to resolve âhttp://www.test.gnu/â instead of âtest.gnuâ. Chromium can be convinced to comply if you explicitly include the âhttp://â prefix â otherwise a Google search might be attempted, which is not what you want. If successful, you should see a simple website.
Note that while you can use GNS to access ordinary websites, this is more an experimental feature and not really our primary goal at this time. Still, it is a possible use−case and we welcome help with testing and development.
Creating a
Business Card
Before we can really use GNS, you should create a business
card. Note that this requires having LaTeX installed on your
system. If you are using a Debian GNU/Linux based operating
system, the following command should install the required
components. Keep in mind that this requires 3GB of
downloaded data and possibly even more when unpacked. On a
GNU Guix based system texlive 2017 has returns a DAG size of
5032.4 MiB. The packages which are confirmed to be required
are:
• |
texlive−units |
|||
• |
texlive−labels |
|||
• |
texlive−pst−barcode |
|||
• |
texlive−luatex85 |
|||
• |
texlive−preview |
|||
• |
texlive−pdfcrop |
|||
• |
texlive−koma−script |
We welcome any help in identifying the required components of the TexLive Distribution. This way we could just state the required components without pulling in the full distribution of TexLive.
apt−get install texlive−full
Start creating a business card by clicking the âCopyâ button in gnunet−namestore−gtk. Next, you should start the gnunet−bcd program (in the terminal, on the command−line). You do not need to pass any options, and please be not surprised if there is no output:
$ gnunet−bcd # does not return
Then, start a browser and point it to http://localhost:8888/ where gnunet−bcd is running a Web server!
First, you might want to fill in the âGNS Public Keyâ field by right−clicking and selecting âPasteâ, filling in the public key from the copy you made in gnunet−namestore−gtk. Then, fill in all of the other fields, including your GNS NICKname. Adding a GPG fingerprint is optional. Once finished, click âSubmit Queryâ. If your LaTeX installation is incomplete, the result will be disappointing. Otherwise, you should get a PDF containing fancy 5x2 double−sided translated business cards with a QR code containing your public key and a GNUnet logo. Weâll explain how to use those a bit later. You can now go back to the shell running gnunet−bcd and press CTRL−C to shut down the Web server.
Be
Social
Next, you should print out your business card and be social.
Find a friend, help them install GNUnet and exchange
business cards with them. Or, if youâre a desperate
loner, you might try the next step with your own card.
Still, itâll be hard to have a conversation with
yourself later, so it would be better if you could find a
friend. You might also want a camera attached to your
computer, so you might need a trip to the store
together.
Before we get started, we need to tell gnunet−qr which zone it should import new records into. For this, run:
$ gnunet−identity −s namestore −e NAME
where NAME is the name of the zone you want to import records into. In our running example, this would be âgnuâ.
Henceforth, for every business card you collect, simply run:
$ gnunet−qr
to open a window showing whatever your camera points at. Hold up your friendâs business card and tilt it until the QR code is recognized. At that point, the window should automatically close. At that point, your friendâs NICKname and their public key should have been automatically imported into your zone.
Assuming both of your peers are properly integrated in the GNUnet network at this time, you should thus be able to resolve your friends names. Suppose your friendâs nickname is âBobâ. Then, type
$ gnunet−gns −u test.bob
to check if your friend was as good at following instructions as you were.
Backup of
Identities and Egos
One should always backup their files, especially in these
SSD days (our team has suffered 3 SSD crashes over a span of
2 weeks). Backing up peer identity and zones is achieved by
copying the following files:
The peer identity file can be found in ~/.local/share/gnunet/private_key.ecc.
The private keys of your egos are stored in the directory ~/.local/share/gnunet/identity/egos/. They are stored in files whose filenames correspond to the zonesâ ego names. These are probably the most important files you want to backup from a GNUnet installation.
Note: All these files contain cryptographic keys and they are stored without any encryption. So it is advisable to backup encrypted copies of them.
Revocation
Now, in the situation of an attacker gaining access to the
private key of one of your egos, the attacker can create
records in the respective GNS zone and publish them as if
you published them. Anyone resolving your domain will get
these new records and when they verify they seem authentic
because the attacker has signed them with your key.
To address this potential security issue, you can pre−compute a revocation certificate corresponding to your ego. This certificate, when published on the P2P network, flags your private key as invalid, and all further resolutions or other checks involving the key will fail.
A revocation certificate is thus a useful tool when things go out of control, but at the same time it should be stored securely. Generation of the revocation certificate for a zone can be done through gnunet−revocation. For example, the following command (as unprivileged user) generates a revocation file revocation.dat for the zone zone1: gnunet−revocation −f revocation.dat −R zone1
The above command only pre−computes a revocation certificate. It does not revoke the given zone. Pre−computing a revocation certificate involves computing a proof−of−work and hence may take up to 4 to 5 days on a modern processor. Note that you can abort and resume the calculation at any time. Also, even if you did not finish the calculation, the resulting file will contain the signature, which is sufficient to complete the revocation process even without access to the private key. So instead of waiting for a few days, you can just abort with CTRL−C, backup the revocation certificate and run the calculation only if your key actually was compromised. This has the disadvantage of revocation taking longer after the incident, but the advantage of saving a significant amount of energy. So unless you believe that a key compromise will need a rapid response, we urge you to wait with generating the revocation certificate. Also, the calculation is deliberately expensive, to deter people from doing this just for fun (as the actual revocation operation is expensive for the network, not for the peer performing the revocation).
To avoid TL;DR ones from accidentally revocating their zones, we are not giving away the command, but it is uncomplicated: the actual revocation is performed by using the −p option of gnunet−revocation.
Whatâs
next?
This may seem not like much of an application yet, but you
have just been one of the first to perform a decentralized
secure name lookup (where nobody could have altered the
value supplied by your friend) in a privacy−preserving
manner (your query on the network and the corresponding
response were always encrypted). So what can you really do
with this? Well, to start with, you can publish your GnuPG
fingerprint in GNS as a âCERTâ record and
replace the public web−of−trust with its
complicated trust model with explicit names and
privacy−preserving resolution. Also, you should read
the next chapter of the tutorial and learn how to use GNS to
have a private conversation with your friend. Finally, help
us with the next GNUnet release for even more applications
using this new public key infrastructure.
Resource
Records
GNS supports the majority of the DNS records as defined in
RFC 1035. Additionally, GNS defines some new record
types the are unique to the GNS system. For example,
GNS−specific resource records are used to give
petnames for zone delegation, revoke zone keys and provide
some compatibility features.
For some DNS records, GNS does extended processing to increase their usefulness in GNS. In particular, GNS introduces special names referred to as âzone relative namesâ. Zone relative names are allowed in some resource record types (for example, in NS and CNAME records) and can also be used in links on webpages. Zone relative names end in â.+â which indicates that the name needs to be resolved relative to the current authoritative zone. The extended processing of those names will expand the â.+â with the correct delegation chain to the authoritative zone (replacing â.+â with the name of the location where the name was encountered) and hence generate a valid GNS name.
The GNS currently supports the record types as defined in GANA. In addition, GNS supports DNS record types, such as A, AAAA or TXT.
For a complete description of the records, please refer to the specification at LSD0001.
In the following, we discuss GNS records with specific behaviour or special handling in GNUnet.
VPN
GNS allows easy access to services provided by the GNUnet
Virtual Public Network. When the GNS resolver encounters a
VPN record it will contact the VPN service to try and
allocate an IPv4/v6 address (if the queries record type is
an IP address) that can be used to contact the service.
Example
I want to provide access to the VPN service âweb.gnu.â on port 80 on peer ABC012: Name: www; RRType: VPN; Value: 80 ABC012 web.gnu.
The peer ABC012 is configured to provide an exit point for the service âweb.gnu.â on port 80 to itâs server running locally on port 8080 by having the following lines in the gnunet.conf configuration file:
[web.gnunet.] TCP_REDIRECTS = 80:localhost4:8080
Synchronizing
with legacy DNS
If you want to support GNS but the master database for a
zone is only available and maintained in DNS, GNUnet
includes the gnunet−zoneimport tool to monitor a DNS
zone and automatically import records into GNS. Today, the
tool does not yet support DNS AF(X)R, as we initially used
it on the â.frâ zone which does not allow us to
perform a DNS zone transfer. Instead,
gnunet−zoneimport reads a list of DNS domain names
from stdin, issues DNS queries for each, converts the
obtained records (if possible) and stores the result in the
namestore.
The zonemaster service then takes the records from the namestore, publishes them into the DHT which makes the result available to the GNS resolver. In the GNS configuration, non−local zones can be configured to be intercepted by specifying â.tld = PUBLICKEYâ in the configuration file in the â[gns]â section.
Note that the namestore by default also populates the namecache. This pre−population is cryptographically expensive. Thus, on systems that only serve to import a large (millions of records) DNS zone and that do not have a local gns service in use, it is thus advisable to disable the namecache by setting the option âDISABLEâ to âYESâ in section â[namecache]â.
Migrating an
existing DNS zone into GNS
Ascension is a tool to migrate existing DNS zones into
GNS.
Compared to the gnunet−zoneimport tool it strictly uses AXFR or IXFR depending on whether or not there exists a SOA record for the zone. If that is the case it will take the serial as a reference point and request the zone. The server will either answer the IXFR request with a correct incremental zone or with the entire zone, which depends on the server configuration.
Before you can migrate any zone though, you need to start a local GNUnet peer. To migrate the Syrian top level domain − one of the few top level domains that support zone transfers − into GNS use the following command:
$ ascension sy. −n ns1.tld.sy. −p
The −p flag will tell GNS to put these records on the DHT so that other users may resolve these records by using the public key of the zone.
Once the zone is migrated, Ascension will output a message telling you, that it will refresh the zone after the time has elapsed. You can resolve the names in the zone directly using GNS or if you want to use it with your browser, check out the GNS manual section. Configuring the GNU Name System. To resolve the records from another system you need the respective zones PKEY. To get the zones public key, you can run the following command:
$ gnunet−identity −dqe sy
Where âsyâ is the name of the zone you want to migrate.
You can share the PKEY of the zone with your friends. They can then resolve records in the zone by doing a lookup replacing the zone label with your PKEY:
$ gnunet−gns −t SOA −u "$PKEY"
The program will continue to run as a daemon and update once the refresh time specified in the zones SOA record has elapsed.
DNSCurve style records are supported in the latest release and they are added as a PKEY record to be referred to the respective GNS public key. Key distribution is still a problem but provided someone else has a public key under a given label it can be looked up.
There is an unofficial Debian package called python3−ascension that adds a system user ascension and runs a GNUnet peer in the background.
Ascension−bind is also an unofficial Debian package that on installation checks for running DNS zones and whether or not they are transferable using DNS zone transfer (AXFR). It asks the administrator which zones to migrate into GNS and installs a systemd unit file to keep the zone up to date. If you want to migrate different zones you might want to check the unit file from the package as a guide.
re:claimID
The re:claimID Identity Provider (IdP) is a decentralized
IdP service. It allows its users to manage and authorize
third parties to access their identity attributes such as
email or shipping addresses.
It basically mimics the concepts of centralized IdPs, such as those offered by Google or Facebook. Like other IdPs, reclaimID features an (optional) OpenID Connect 1.0−compliant protocol layer that can be used for websites to integrate reclaimID as an Identity Provider with little effort.
Managing
Attributes
Before adding attributes to an identity, you must first
create an ego:
$ gnunet−identity −−create="user"
Henceforth, you can manage a new user profile of the user "user".
To add an email address to your user profile, simply use the gnunet−reclaim command line tool:
::
$ gnunet−reclaim −e "user" −a "email" −V "[email protected]"
All of your attributes can be listed using the gnunet−reclaim command line tool as well:
$ gnunet−reclaim −e "user" −D
Currently, and by default, attribute values are interpreted as plain text. In the future there might be more value types such as X.509 certificate credentials.
Managing
Credentials
Attribute values may reference a claim in a third party
attested credential. Such a credential can have a variety of
formats such as JSON−Web−Tokens or X.509
certificates. Currently, reclaimID only supports
JSON−Web−Token credentials.
To add a credential to your user profile, invoke the gnunet−reclaim command line tool as follows:
$ gnunet−reclaim −e "user"\ −−credential−name="email"\ −−credential−type="JWT"\ −−value="ey..."
All of your credentials can be listed using the gnunet−reclaim command line tool as well:
$ gnunet−reclaim −e "user" −−credentials
In order to add an attribe backed by a credential, specify the attribute value as the claim name in the credential to reference along with the credential ID:
$ gnunet−reclaim −e "user"\ −−add="email"\ −−value="verified_email"\ −−credential−id="<CREDENTIAL_ID>"
Sharing
Attributes with Third Parties
If you want to allow a third party such as a website or
friend to access to your attributes (or a subset thereof)
execute:
$ TICKET=$(gnunet−reclaim −e "user"\ −r "$RP_KEY"\ −i "attribute1,attribute2,...")
The command will return a "ticket" string. You must give $TICKET to the requesting third party.
$RP_KEY is the public key of the third party and "attribute1,attribute2,..." is a comma−separated list of attribute names, such as "email,name,...", that you want to share.
The third party may retrieve the key in string format for use in the above call using "gnunet−identity":
$ RP_KEY=$(gnunet−identity −d | grep "relyingparty" | awk '{print $3}')
The third party can then retrieve your shared identity attributes using:
$ gnunet−reclaim −e "relyingparty" −C "ticket"
Where "relyingparty" is the name for the identity behind $RP_KEY that the requesting party is using. This will retrieve and list the shared identity attributes. The above command will also work if the user is currently offline since the attributes are retrieved from GNS. Further, $TICKET can be re−used later to retrieve up−to−date attributes in case "friend" has changed the value(s). For instance, because his email address changed.
To list all given authorizations (tickets) you can execute:
$ gnunet−reclaim −e "user" −T
Revoking
Authorizations of Third Parties
If you want to revoke the access of a third party to your
attributes you can execute:
$ gnunet−reclaim −e "user" −R $TICKET
This will prevent the third party from accessing the attribute in the future. Please note that if the third party has previously accessed the attribute, there is not way in which the system could have prevented the thiry party from storing the data. As such, only access to updated data in the future can be revoked. This behaviour is _exactly the same_ as with other IdPs.
OpenID
Connect
There is an
https://openid.net/specs/openid−connect−core−1_0.html
API for use with re:claimID. However, its use is quite
complicated to setup.
https://api.reclaim/openid/authorize http://localhost:7776/openid/token http://localhost:7776/openid/userinfo http://localhost:7776/openid/login
The token endpoint is protected using HTTP basic authentication. You can authenticate using any username and the password configured under:
$ gnunet−config −s reclaim−rest−plugin −o OIDC_CLIENT_SECRET
The authorize endpoint is protected using a Cookie which can be obtained through a request against the login endpoint. This functionality is meant to be used in the context of the OpenID Connect authorization flow to collect user consent interactively. Without a Cookie, the authorize endpoint redirects to a URI configured under:
$ gnunet−config −s reclaim−rest−plugin −o ADDRESS
The token endpoint is protected using OAuth2 and expects the grant which is retrieved from the authorization endpoint according to the standard.
The userinfo endpoint is protected using OAuth2 and expects a bearer access token which is retrieved from a token request.
In order to make use of OpenID Connect flows as a user, you need to install the browser plugin:
• |
Firefox Add−on |
|||
• |
Chrome Web Store |
In order to create and register an OpenID Connect client as a relying party, you need to execute the following steps:
$ gnunet−identity −C <client_name> $ gnunet−namestore −z <client_name> −a −n "@" −t RECLAIM_OIDC_REDIRECT −V <redirect_uri> −e 1d −p $ gnunet−namestore −z <client_name> −a −n "@" −t RECLAIM_OIDC_CLIENT −V "My OIDC Client" −e 1d −p
The "client_id" for use in OpenID Connect is the public key of the client as displayed using:
$ gnunet−identity −d grep "relyingparty" | awk '{print $3}'
The RECLAIM_OIDC_REDIRECT record contains your website redirect URI. You may use any globally unique DNS or GNS URI. The RECLAIM_OIDC_CLIENT record represents the client description which whill be displayed to users in an authorization request.
Any website or relying party must use the authorization endpoint https://api.reclaim/openid/authorize in its authorization redirects, e.g.
<a href="https://api.reclaim/openid/authorize?client_id=<PKEY>\ &scope=openid email\ &redirect_uri=<redirect_uri>\ &nonce=<random>">Login</a>
This will direct the user's browser onto his local reclaimID instance. After giving consent, you will be provided with the OpenID Connect authorization code according to the specifications at your provided redirect URI.
The ID Tokens issues by the token endpoints are signed using HS512 with the shared secret configured under:
$ gnunet−config −s reclaim−rest−plugin −o JWT_SECRET
The authorization code flow optionally supports Proof Key for Code Exchange. If PKCE is used, the client does not need to authenticate against the token endpoint.
Providing
Third Party Attestation
If you are running an identity provider (IdP) service you
may be able to support providing credentials for re:claimID
users. IdPs can issue JWT credentials as long as they
support OpenID Connect and OpenID Connect
Discovery.
In order to allow users to import attributes through the re:claimID user interface, you need to register the following public OAuth2/OIDC client:
• |
client_id: reclaimid | ||
• |
client_secret: none | ||
• |
redirect_uri: https://ui.reclaim (The URI of the re:claimID webextension) | ||
• |
grant_type: authorization_code with PKCE (RFC7636) | ||
• |
scopes: all you want to offer. | ||
• |
id_token: JWT |
When your users add an attribute with name "email" which supports webfinger discovery they will be prompted with the option to retrieve the OpenID Connect ID Token through the user interface.
File−sharing
This chapter documents the GNUnet file−sharing
application. The original file−sharing implementation
for GNUnet was designed to provide anonymous
file−sharing. However, over time, we have also added
support for non−anonymous file−sharing (which
can provide better performance). Anonymous and
non−anonymous file−sharing are quite integrated
in GNUnet and, except for routing, share most of the
concepts and implementation. There are three primary
file−sharing operations: publishing, searching and
downloading. For each of these operations, the user
specifies an anonymity level. If both the publisher and the
searcher/downloader specify âno anonymityâ,
non−anonymous file−sharing is used. If either
user specifies some desired degree of anonymity, anonymous
file−sharing will be used.
After a short introduction, we will first look at the various concepts in GNUnetâs file−sharing implementation. Then, we will discuss specifics as to how they impact users that publish, search or download files.
Searching
The command gnunet−search can be used to search
for content on GNUnet. The format is:
$ gnunet−search [−t TIMEOUT] KEYWORD
The −t option specifies that the query should timeout after approximately TIMEOUT seconds. A value of zero (â0â) is interpreted as no timeout, which is the default. In this case, gnunet−search will never terminate (unless you press CTRL−C).
If multiple words are passed as keywords, they will all be considered optional. Prefix keywords with a â+â to make them mandatory.
Note that searching using:
$ gnunet−search Das Kapital
is not the same as searching for
$ gnunet−search "Das Kapital"
as the first will match files shared under the keywords âDasâ or âKapitalâ whereas the second will match files shared under the keyword âDas Kapitalâ.
Search results are printed like this:
#15: gnunet−download −o "COPYING" gnunet://fs/chk/PGK8M...3EK130.75446
The whole line is the command you would have to enter to download the file. The first argument passed to −o is the suggested filename (you may change it to whatever you like). It is followed by the key for decrypting the file, the query for searching the file, a checksum (in hexadecimal) finally the size of the file in bytes.
Downloading
In order to download a file, you need the whole line
returned by gnunet−search. You can then use the tool
gnunet−download to obtain the file:
$ gnunet−download −o <FILENAME> <GNUNET−URL>
FILENAME specifies the name of the file where GNUnet is supposed to write the result. Existing files are overwritten. If the existing file contains blocks that are identical to the desired download, those blocks will not be downloaded again (automatic resume).
If you want to download the GPL from the previous example, you do the following:
$ gnunet−download −o "COPYING" gnunet://fs/chk/PGK8M...3EK130.75446
If you ever have to abort a download, you can continue it at any time by re−issuing gnunet−download with the same filename. In that case, GNUnet will not download blocks again that are already present.
GNUnetâs file−encoding mechanism will ensure file integrity, even if the existing file was not downloaded from GNUnet in the first place.
You may want to use the −V switch to turn on verbose reporting. In this case, gnunet−download will print the current number of bytes downloaded whenever new data was received.
Publishing
The command gnunet−publish can be used to add
content to the network. The basic format of the command
is:
$ gnunet−publish [−n] [−k KEYWORDS]* [−m TYPE:VALUE] FILENAME
For example:
$ gnunet−publish −m "description:GNU License" −k gpl −k test −m "mimetype:text/plain" COPYING
The option −k is used to specify keywords for the file that should be inserted. You can supply any number of keywords, and each of the keywords will be sufficient to locate and retrieve the file. Please note that you must use the −k option more than once â one for each expression you use as a keyword for the filename.
The −m option is used to specify meta−data, such as descriptions. You can use −m multiple times. The TYPE passed must be from the list of meta−data types known to libextractor. You can obtain this list by running extract −L. Use quotes around the entire meta−data argument if the value contains spaces. The meta−data is displayed to other users when they select which files to download. The meta−data and the keywords are optional and may be inferred using GNU libextractor.
gnunet−publish has a few additional options to handle namespaces and directories. Refer to the man−page for details.
Indexing vs
Inserting
By default, GNUnet indexes a file instead of making a full
copy. This is much more efficient, but requires the file to
stay unaltered at the location where it was when it was
indexed. If you intend to move, delete or alter a file,
consider using the option −n which will force
GNUnet to make a copy of the file in the database.
Since it is much less efficient, this is strongly discouraged for large files. When GNUnet indexes a file (default), GNUnet does not create an additional encrypted copy of the file but just computes a summary (or index) of the file. That summary is approximately two percent of the size of the original file and is stored in GNUnetâs database. Whenever a request for a part of an indexed file reaches GNUnet, this part is encrypted on−demand and send out. This way, there is no need for an additional encrypted copy of the file to stay anywhere on the drive. This is different from other systems, such as Freenet, where each file that is put online must be in Freenetâs database in encrypted format, doubling the space requirements if the user wants to preserve a directly accessible copy in plaintext.
Thus indexing should be used for all files where the user will keep using this file (at the location given to gnunet−publish) and does not want to retrieve it back from GNUnet each time. If you want to remove a file that you have indexed from the local peer, use the tool gnunet−unindex to un−index the file.
The option −n may be used if the user fears that the file might be found on their drive (assuming the computer comes under the control of an adversary). When used with the −n flag, the user has a much better chance of denying knowledge of the existence of the file, even if it is still (encrypted) on the drive and the adversary is able to crack the encryption (e.g. by guessing the keyword).
Concepts
For better results with filesharing it is useful to
understand the following concepts. In addition to anonymous
routing GNUnet attempts to give users a better experience in
searching for content. GNUnet uses cryptography to safely
break content into smaller pieces that can be obtained from
different sources without allowing participants to corrupt
files. GNUnet makes it difficult for an adversary to send
back bogus search results. GNUnet enables content providers
to group related content and to establish a reputation.
Furthermore, GNUnet allows updates to certain content to be
made available. This section is supposed to introduce users
to the concepts that are used to achieve these goals.
Files
A file in GNUnet is just a sequence of bytes. Any
file−format is allowed and the maximum file size is
theoretically 2^64 − 1 bytes, except that it would
take an impractical amount of time to share such a file.
GNUnet itself never interprets the contents of shared files,
except when using GNU libextractor to obtain keywords.
Keywords
Keywords are the most simple mechanism to find files on
GNUnet. Keywords are case−sensitive and the
search string must always match exactly the keyword
used by the person providing the file. Keywords are never
transmitted in plaintext. The only way for an adversary to
determine the keyword that you used to search is to guess it
(which then allows the adversary to produce the same search
request). Since providing keywords by hand for each shared
file is tedious, GNUnet uses GNU libextractor to help
automate this process. Starting a keyword search on a slow
machine can take a little while since the keyword search
involves computing a fresh RSA key to formulate the
request.
Directories
A directory in GNUnet is a list of file identifiers with
meta data. The file identifiers provide sufficient
information about the files to allow downloading the
contents. Once a directory has been created, it cannot be
changed since it is treated just like an ordinary file by
the network. Small files (of a few kilobytes) can be inlined
in the directory, so that a separate download becomes
unnecessary.
Directories are shared just like ordinary files. If you download a directory with gnunet−download, you can use gnunet−directory to list its contents. The canonical extension for GNUnet directories when stored as files in your local file−system is ".gnd". The contents of a directory are URIs and meta data. The URIs contain all the information required by gnunet−download to retrieve the file. The meta data typically includes the mime−type, description, a filename and other meta information, and possibly even the full original file (if it was small).
Egos and
File−Sharing
When sharing files, it is sometimes desirable to build a
reputation as a source for quality information. With egos,
publishers can (cryptographically) sign files, thereby
demonstrating that various files were published by the same
entity. An ego thus allows users to link different
publication events, thereby deliberately reducing anonymity
to pseudonymity.
Egos used in GNUnet's file−sharing for such pseudonymous publishing also correspond to the egos used to identify and sign zones in the GNU Name System. However, if the same ego is used for file−sharing and for a GNS zone, this will weaken the privacy assurances provided by the anonymous file−sharing protocol.
Note that an ego is NOT bound to a GNUnet peer. There can be multiple egos for a single user, and users could (theoretically) share the private keys of an ego by copying the respective private keys.
Namespaces
A namespace is a set of files that were signed by the same
ego. Today, namespaces are implemented independently of GNS
zones, but in the future we plan to merge the two such that
a GNS zone can basically contain files using a
file−sharing specific record type.
Files (or directories) that have been signed and placed into a namespace can be updated. Updates are identified as authentic if the same secret key was used to sign the update.
Advertisements
Advertisements are used to notify other users about the
existence of a namespace. Advertisements are propagated
using the normal keyword search. When an advertisement is
received (in response to a search), the namespace is added
to the list of namespaces available in the
namespace−search dialogs of gnunet−fs−gtk
and printed by gnunet−identity. Whenever a
namespace is created, an appropriate advertisement can be
generated. The default keyword for the advertising of
namespaces is "namespace".
Anonymity
level
The anonymity level determines how hard it should be for an
adversary to determine the identity of the publisher or the
searcher/downloader. An anonymity level of zero means that
anonymity is not required. The default anonymity level of
"1" means that anonymous routing is desired, but
no particular amount of cover traffic is necessary. A
powerful adversary might thus still be able to deduce the
origin of the traffic using traffic analysis. Specifying
higher anonymity levels increases the amount of cover
traffic required.
The specific numeric value (for anonymity levels above 1) is simple: Given an anonymity level L (above 1), each request FS makes on your behalf must be hidden in L−1 equivalent requests of cover traffic (traffic your peer routes for others) in the same time−period. The time−period is twice the average delay by which GNUnet artificially delays traffic.
While higher anonymity levels may offer better privacy, they can also significantly hurt performance.
Content
Priority
Depending on the peer's configuration, GNUnet peers migrate
content between peers. Content in this sense are individual
blocks of a file, not necessarily entire files. When peers
run out of space (due to local publishing operations or due
to migration of content from other peers), blocks sometimes
need to be discarded. GNUnet first always discards expired
blocks (typically, blocks are published with an expiration
of about two years in the future; this is another option).
If there is still not enough space, GNUnet discards the
blocks with the lowest priority. The priority of a block is
decided by its popularity (in terms of requests from peers
we trust) and, in case of blocks published locally, the
base−priority that was specified by the user when the
block was published initially.
Replication
When peers migrate content to other systems, the replication
level of a block is used to decide which blocks need to be
migrated most urgently. GNUnet will always push the block
with the highest replication level into the network, and
then decrement the replication level by one. If all blocks
reach replication level zero, the selection is simply
random.
Namespace
Management
The gnunet−identity tool can be used to create
egos. By default, gnunet−identity
−−display simply lists all locally available
egos.
Creating
Egos
With the −−create=NICK option it can also
be used to create a new ego. An ego is the virtual identity
of the entity in control of a namespace or GNS zone. Anyone
can create any number of egos. The provided NICK name
automatically corresponds to a GNU Name System domain name.
Thus, henceforth name resolution for any name ending in
".NICK" will use the NICK's zone. You should avoid
using NICKs that collide with well−known DNS
names.
Currently, the IDENTITY subsystem supports two types of identity keys: ECDSA and EdDSA. By default, ECDSA identities are creates with ECDSA keys. In order to create an identity with EdDSA keys, you can use the −−eddsa flag.
Deleting
Egos
With the −D NICK option egos can be deleted.
Once the ego has been deleted it is impossible to add
content to the corresponding namespace or zone. However, the
existing GNS zone data is currently not dropped. This may
change in the future.
Deleting the pseudonym does not make the namespace or any content in it unavailable.
File−Sharing
URIs
GNUnet (currently) uses four different types of URIs for
file−sharing. They all begin with
"gnunet://fs/". This section describes the four
different URI types in detail.
For FS URIs empty KEYWORDs are not allowed. Quotes are allowed to denote whitespace between words. Keywords must contain a balanced number of double quotes. Doubles quotes can not be used in the actual keywords. This means that the string '""foo bar""' will be turned into two OR−ed keywords 'foo' and 'bar', not into '"foo bar"'.
Encoding of
hash values in URIs
Most URIs include some hash values. Hashes are encoded using
base32hex (RFC 2938).
chk−uri .. _Content−Hash−Key−_0028chk_0029:
Content Hash
Key (chk)
A chk−URI is used to (uniquely) identify a file or
directory and to allow peers to download the file. Files are
stored in GNUnet as a tree of encrypted blocks. The
chk−URI thus contains the information to download and
decrypt those blocks. A chk−URI has the format
"gnunet://fs/chk/KEYHASH.QUERYHASH.SIZE". Here,
"SIZE" is the size of the file (which allows a
peer to determine the shape of the tree), KEYHASH is the key
used to decrypt the file (also the hash of the plaintext of
the top block) and QUERYHASH is the query used to request
the top−level block (also the hash of the encrypted
block).
loc−uri .. _Location−identifiers−_0028loc_0029:
Location
identifiers (loc)
For non−anonymous file−sharing, loc−URIs
are used to specify which peer is offering the data (in
addition to specifying all of the data from a
chk−URI). Location identifiers include a digital
signature of the peer to affirm that the peer is truly the
origin of the data. The format is
"gnunet://fs/loc/KEYHASH.QUERYHASH.SIZE.PEER.SIG.EXPTIME".
Here, "PEER" is the public key of the peer (in
GNUnet format in base32hex), SIG is the RSA signature (in
GNUnet format in base32hex) and EXPTIME specifies when the
signature expires (in milliseconds after 1970).
ksk−uri .. _Keyword−queries−_0028ksk_0029:
Keyword
queries (ksk)
A keyword−URI is used to specify that the desired
operation is the search using a particular keyword. The
format is simply "gnunet://fs/ksk/KEYWORD".
Non−ASCII characters can be specified using the
typical URI−encoding (using hex values) from HTTP.
"+" can be used to specify multiple keywords
(which are then logically "OR"−ed in the
search, results matching both keywords are given a higher
rank): "gnunet://fs/ksk/KEYWORD1+KEYWORD2".
ksk−URIs must not begin or end with the plus ('+')
character. Furthermore they must not contain '++'.
sks−uri .. _Namespace−content−_0028sks_0029:
Namespace
content (sks)
Please note that the text in this subsection is outdated and
needs to be rewritten for version 0.10! This especially
concerns the terminology of Pseudonym/Ego/Identity.
Namespaces are sets of files that have been approved by some (usually pseudonymous) user −−− typically by that user publishing all of the files together. A file can be in many namespaces. A file is in a namespace if the owner of the ego (aka the namespace's private key) signs the CHK of the file cryptographically. An SKS−URI is used to search a namespace. The result is a block containing meta data, the CHK and the namespace owner's signature. The format of a sks−URI is "gnunet://fs/sks/NAMESPACE/IDENTIFIER". Here, "NAMESPACE" is the public key for the namespace. "IDENTIFIER" is a freely chosen keyword (or password!). A commonly used identifier is "root" which by convention refers to some kind of index or other entry point into the namespace.
Virtual
Public Network
Using the GNUnet Virtual Public Network (VPN) application
you can tunnel IP traffic over GNUnet. Moreover, the VPN
comes with built−in protocol translation and
DNS−ALG support, enabling IPv4−to−IPv6
protocol translation (in both directions). This chapter
documents how to use the GNUnet VPN.
The first thing to note about the GNUnet VPN is that it is a public network. All participating peers can participate and there is no secret key to control access. So unlike common virtual private networks, the GNUnet VPN is not useful as a means to provide a "private" network abstraction over the Internet. The GNUnet VPN is a virtual network in the sense that it is an overlay over the Internet, using its own routing mechanisms and can also use an internal addressing scheme. The GNUnet VPN is an Internet underlay −−− TCP/IP applications run on top of it.
The VPN is currently only supported on GNU/Linux systems. Support for operating systems that support TUN (such as FreeBSD) should be easy to add (or might not even require any coding at all −−− we just did not test this so far). Support for other operating systems would require re−writing the code to create virtual network interfaces and to intercept DNS requests.
The VPN does not provide good anonymity. While requests are routed over the GNUnet network, other peers can directly see the source and destination of each (encapsulated) IP packet. Finally, if you use the VPN to access Internet services, the peer sending the request to the Internet will be able to observe and even alter the IP traffic. We will discuss additional security implications of using the VPN later in this chapter.
Setting up
an Exit node
Any useful operation with the VPN requires the existence of
an exit node in the GNUnet Peer−to−Peer network.
Exit functionality can only be enabled on peers that have
regular Internet access. If you want to play around with the
VPN or support the network, we encourage you to setup exit
nodes. This chapter documents how to setup an exit node.
There are four types of exit functions an exit node can provide, and using the GNUnet VPN to access the Internet will only work nicely if the first three types are provided somewhere in the network. The four exit functions are:
• |
DNS: allow other peers to use your DNS resolver | ||
• |
IPv4: allow other peers to access your IPv4 Internet connection | ||
• |
IPv6: allow other peers to access your IPv6 Internet connection | ||
• |
Local service: allow other peers to access a specific TCP or UDP service your peer is providing |
By enabling "exit" in gnunet−setup and checking the respective boxes in the "exit" tab, you can easily choose which of the above exit functions you want to support.
Note, however, that by supporting the first three functions you will allow arbitrary other GNUnet users to access the Internet via your system. This is somewhat similar to running a Tor exit node. The Torproject has a nice article about what to consider if you want to do this here. We believe that generally running a DNS exit node is completely harmless.
The exit node configuration does currently not allow you to restrict the Internet traffic that leaves your system. In particular, you cannot exclude SMTP traffic (or block port 25) or limit to HTTP traffic using the GNUnet configuration. However, you can use your host firewall to restrict outbound connections from the virtual tunnel interface. This is highly recommended. In the future, we plan to offer a wider range of configuration options for exit nodes.
Note that by running an exit node GNUnet will configure your kernel to perform IP−forwarding (for IPv6) and NAT (for IPv4) so that the traffic from the virtual interface can be routed to the Internet. In order to provide an IPv6−exit, you need to have a subnet routed to your host's external network interface and assign a subrange of that subnet to the GNUnet exit's TUN interface.
When running a local service, you should make sure that the local service is (also) bound to the IP address of your EXIT interface (e.g. 169.254.86.1). It will NOT work if your local service is just bound to loopback. You may also want to create a "VPN" record in your zone of the GNU Name System to make it easy for others to access your service via a name instead of just the full service descriptor. Note that the identifier you assign the service can serve as a passphrase or shared secret, clients connecting to the service must somehow learn the service's name. VPN records in the GNU Name System can make this easier.
Fedora and
the Firewall
When using an exit node on Fedora 15, the standard firewall
can create trouble even when not really exiting the local
system! For IPv4, the standard rules seem fine. However, for
IPv6 the standard rules prohibit traffic from the network
range of the virtual interface created by the exit daemon to
the local IPv6 address of the same interface (which is
essentially loopback traffic, so you might suspect that a
standard firewall would leave this traffic alone). However,
as somehow for IPv6 the traffic is not recognized as
originating from the local system (and as the connection is
not already "established"), the firewall drops the
traffic. You should still get ICMPv6 packets back, but
that's obviously not very useful.
Possible ways to fix this include disabling the firewall (do you have a good reason for having it on?) or disabling the firewall at least for the GNUnet exit interface (or the respective IPv4/IPv6 address range). The best way to diagnose these kinds of problems in general involves setting the firewall to REJECT instead of DROP and to watch the traffic using wireshark (or tcpdump) to see if ICMP messages are generated when running some tests that should work.
Setting up
VPN node for protocol translation and tunneling
The GNUnet VPN/PT subsystem enables you to tunnel IP traffic
over the VPN to an exit node, from where it can then be
forwarded to the Internet. This section documents how to
setup VPN/PT on a node. Note that you can enable both the
VPN and an exit on the same peer. In this case, IP traffic
from your system may enter your peer's VPN and leave your
peer's exit. This can be useful as a means to do protocol
translation. For example, you might have an application that
supports only IPv4 but needs to access an IPv6−only
site. In this case, GNUnet would perform 4to6 protocol
translation between the VPN (IPv4) and the Exit (IPv6).
Similarly, 6to4 protocol translation is also possible.
However, the primary use for GNUnet would be to access an
Internet service running with an IP version that is not
supported by your ISP. In this case, your IP traffic would
be routed via GNUnet to a peer that has access to the
Internet with the desired IP version.
Setting up an entry node into the GNUnet VPN primarily requires you to enable the "VPN/PT" option in "gnunet−setup". This will launch the "gnunet−service−vpn", "gnunet−service−dns" and "gnunet−daemon−pt" processes. The "gnunet−service−vpn" will create a virtual interface which will be used as the target for your IP traffic that enters the VPN. Additionally, a second virtual interface will be created by the "gnunet−service−dns" for your DNS traffic. You will then need to specify which traffic you want to tunnel over GNUnet. If your ISP only provides you with IPv4 or IPv6−access, you may choose to tunnel the other IP protocol over the GNUnet VPN. If you do not have an ISP (and are connected to other GNUnet peers via WLAN), you can also choose to tunnel all IP traffic over GNUnet. This might also provide you with some anonymity. After you enable the respective options and restart your peer, your Internet traffic should be tunneled over the GNUnet VPN.
The GNUnet VPN uses DNS−ALG to hijack your IP traffic. Whenever an application resolves a hostname (like 'gnunet.org'), the "gnunet−daemon−pt" will instruct the "gnunet−service−dns" to intercept the request (possibly route it over GNUnet as well) and replace the normal answer with an IP in the range of the VPN's interface. "gnunet−daemon−pt" will then tell "gnunet−service−vpn" to forward all traffic it receives on the TUN interface via the VPN to the original destination.
For applications that do not use DNS, you can also manually create such a mapping using the gnunet−vpn command−line tool. Here, you specify the desired address family of the result (e.g. "−4"), and the intended target IP on the Internet (e.g. "−i 131.159.74.67") and "gnunet−vpn" will tell you which IP address in the range of your VPN tunnel was mapped.
gnunet−vpn can also be used to access "internal" services offered by GNUnet nodes. So if you happen to know a peer and a service offered by that peer, you can create an IP tunnel to that peer by specifying the peer's identity, service name and protocol (−−tcp or −−udp) and you will again receive an IP address that will terminate at the respective peer's service.
Messenger
The GNUnet Messenger subsystem allows decentralized
message−based communication inside of so called rooms.
Each room can be hosted by a variable amount of peers. Every
member of a room has the possibility to host the room on its
own peer. A peer allows any amount of members to join a
room. The amount of members in a room is not restricted.
Messages in a room will be distributed between all peers hosting the room or being internally (in context of the messenger service) connected to a hosting peer. All received or sent messages will be stored on any peer locally which is hosting the respective room or is internally connected to such a hosting peer.
The Messenger service is built on the CADET subsystem to make internal connections between peers using a reliable and encrypted transmission. Additionally the service uses a discrete padding to few different sizes. So kinds of messages and potential content can't be identified by the size of traffic from any attacker being unable to break the encryption of the transmission layer.
Another feature is additional end−to−end encryption for selected messages which uses the public key of another member (the receiver) to encrypt the message. Therefore it is ensured that only the selected member can read its content. This will also use additional padding.
Current
state
Currently there is only a simplistic CLI application
available to use the messenger service. You can use this
application with the gnunet−messenger
command.
This application was designed for testing purposes and it does not provide full functionality in the current state. It is planned to replace this CLI application in later stages with a fully featured one using a client−side library designed for messenger applications.
Entering a
room
You can enter any room by its ROOMKEY and any PEERIDENTITY
of a hosting peer. Optionally you can provide any IDENTITY
which can represent a local ego by its name.
$ gnunet−messenger [−e IDENTITY] −d PEERIDENTITY −r ROOMKEY
A PEERIDENTITY gets entered in encoded form. You can get your own peer ID by using the gnunet−peerinfo command:
$ gnunet−peerinfo −s
A ROOMKEY gets entered in readable text form. The service will then hash the entered ROOMKEY and use the result as shared secret for transmission through the CADET submodule. You can also optionally leave out the '−r' parameter and the ROOMKEY to use the zeroed hash instead.
If no IDENTITY is provided you will not send any name to others, you will be referred as "anonymous" instead and use the anonymous ego. If you provide any IDENTITY a matching ego will be used to sign your messages. If there is no matching ego you will use the anonymous ego instead. The provided IDENTITY will be distributed as your name for the service in any case.
Opening a
room
You can open any room in a similar way to entering it. You
just have to leave out the '−d' parameter and the
PEERIDENTITY of the hosting peer.
$ gnunet−messenger [−e IDENTITY] −r ROOMKEY
Providing ROOMKEY and IDENTITY is identical to entering a room. Opening a room will also make your peer to a host of this room. So others can enter the room through your peer if they have the required ROOMKEY and your peer ID.
If you want to use the zeroed hash as shared secret key for the room you can also leave it out as well:
$ gnunet−messenger
Messaging in
a room
Once joined a room by entering it or opening it you can
write text−based messages which will be distributed
between all internally conntected peers. All sent messages
will be displayed in the same way as received messages.
This relates to the internal handling of sent and received messages being mostly identical on application layer. Every handled message will be represented visually depending on its kind, content and sender. A sender can usually be identified by the encoded member ID or their name.
[17X37K] * 'anonymous' says: "hey"
Private
messaging
As referred in the introduction the service allows sending
private messages with additional end−to−end
encryption. These messages will be visually represented by
messages of the kind 'PRIVATE' in case they can't be
decrypted with your used ego. Members who can't decrypt the
message can potentially only identify its sender but they
can't identify its receiver.
[17X37K] ~ message: PRIVATE
If they can be decrypted they will appear as their secret message instead but marked visually.
[17X37K] ** 'anonymous' says: "hey"
Currently you can only activate sending such encrypted text messages instead of usual text messages by adding the '−p' parameter:
$ gnunet−messenger [−e IDENTITY] −d PEERIDENTITY −r ROOMKEY −p
Notice that you can only send such encrypted messages to members who use an ego which is not publicly known as the anonymous ego to ensure transparency. If any user could decrypt these messages they would not be private. So as receiver of such messages the IDENTITY is required and it has to match a local ego.
Advanced
Configuration
Config file format
In GNUnet realm, all components obey the same pattern to get
configuration values. According to this pattern, once the
component has been installed, the installation deploys
default values in $prefix/share/gnunet/config.d/, in
.conf files. In order to override these defaults, the
user can write a custom .conf file and either pass it
to the component at execution time, or name it
gnunet.conf and place it under
$HOME/.config/.
A config file is a text file containing sections, and each section contains its values. The right format follows:
[section1] value1 = string value2 = 23 [section2] value21 = string value22 = /path22
Throughout any configuration file, it is possible to use $−prefixed variables, like $VAR, especially when they represent filenames in in the filesystem. It is also possible to provide defaults values for those variables that are unset, by using the following syntax:
${VAR:−default}
However, there are two ways a user can set $−prefixable variables: (a) by defining them under a [paths] section
[paths] GNUNET_DEPLOYMENT_SHARED = ${HOME}/shared−data .. [section−x] path−x = ${GNUNET_DEPLOYMENT_SHARED}/x
or (b) by setting them in the environment
$ export VAR=/x
The configuration loader will give precedence to variables set under [path], though.
The utility 'gnunet−config', which gets installed along with GNUnet, serves to get and set configuration values without directly editing the .conf file. The option '−f' is particularly useful to resolve filenames, when they use several levels of $−expanded variables. See 'gnunet−config −−help'.
Note that, in this stage of development, the file $HOME/.config/gnunet.conf can contain sections for all the components. .. _The−Single_002dUser−Setup:
The
Single−User Setup
For the single−user setup, you do not need to do
anything special and can just start the GNUnet background
processes using gnunet−arm. By default, GNUnet
looks in ~/.config/gnunet.conf for a configuration
(or $XDG_CONFIG_HOME/gnunet.conf
if $XDG_CONFIG_HOME is defined). If your
configuration lives elsewhere, you need to pass the
−c FILENAME option to all GNUnet commands.
Assuming the configuration file is called ~/.config/gnunet.conf, you start your peer using the gnunet−arm command (say as user gnunet) using:
gnunet−arm −c ~/.config/gnunet.conf −s
The "−s" option here is for "start". The command should return almost instantly. If you want to stop GNUnet, you can use:
gnunet−arm −c ~/.config/gnunet.conf −e
The "−e" option here is for "end".
Note that this will only start the basic peer, no actual applications will be available. If you want to start the file−sharing service, use (after starting GNUnet):
gnunet−arm −c ~/.config/gnunet.conf −i fs
The "−i fs" option here is for "initialize" the "fs" (file−sharing) application. You can also selectively kill only file−sharing support using
gnunet−arm −c ~/.config/gnunet.conf −k fs
Assuming that you want certain services (like file−sharing) to be always automatically started whenever you start GNUnet, you can activate them by setting "IMMEDIATE_START=YES" in the respective section of the configuration file (for example, "[fs]"). Then GNUnet with file−sharing support would be started whenever you enter:
gnunet−arm −c ~/.config/gnunet.conf −s
Alternatively, you can combine the two options:
gnunet−arm −c ~/.config/gnunet.conf −s −i fs
Using gnunet−arm is also the preferred method for initializing GNUnet from init.
Finally, you should edit your crontab (using the crontab command) and insert a line
@reboot gnunet−arm −c ~/.config/gnunet.conf −s
to automatically start your peer whenever your system boots.
The
Multi−User Setup
This requires you to create a user gnunet and an
additional group gnunetdns, prior to running make
install during installation. Then, you create a
configuration file /etc/gnunet.conf which should
contain the lines:
[arm] START_SYSTEM_SERVICES = YES START_USER_SERVICES = NO
Then, perform the same steps to run GNUnet as in the per−user configuration, except as user gnunet (including the crontab installation). You may also want to run gnunet−setup to configure your peer (databases, etc.). Make sure to pass −c /etc/gnunet.conf to all commands. If you run gnunet−setup as user gnunet, you might need to change permissions on /etc/gnunet.conf so that the gnunet user can write to the file (during setup).
Afterwards, you need to perform another setup step for each normal user account from which you want to access GNUnet. First, grant the normal user ($USER) permission to the group gnunet:
# adduser $USER gnunet
Then, create a configuration file in ~/.config/gnunet.conf for the $USER with the lines:
[arm] START_SYSTEM_SERVICES = NO START_USER_SERVICES = YES
This will ensure that gnunet−arm when started by the normal user will only run services that are per−user, and otherwise rely on the system−wide services. Note that the normal user may run gnunet−setup, but the configuration would be ineffective as the system−wide services will use /etc/gnunet.conf and ignore options set by individual users.
Again, each user should then start the peer using gnunet−arm −s −−− and strongly consider adding logic to start the peer automatically to their crontab.
Afterwards, you should see two (or more, if you have more than one USER) gnunet−service−arm processes running in your system.
Access
Control for GNUnet
This chapter documents how we plan to make access control
work within the GNUnet system for a typical peer. It should
be read as a best−practice installation guide for
advanced users and builders of binary distributions. The
recommendations in this guide apply to POSIX−systems
with full support for UNIX domain sockets only.
Note that this is an advanced topic. The discussion presumes a very good understanding of users, groups and file permissions. Normal users on hosts with just a single user can just install GNUnet under their own account (and possibly allow the installer to use SUDO to grant additional permissions for special GNUnet tools that need additional rights). The discussion below largely applies to installations where multiple users share a system and to installations where the best possible security is paramount.
A typical
GNUnet system consists of components that fall into four
categories:
User interfaces
User interfaces are not security sensitive and are supposed to be run and used by normal system users. The GTK GUIs and most command−line programs fall into this category. Some command−line tools (like gnunet−transport) should be excluded as they offer low−level access that normal users should not need.
System services and support tools
System services should always run and offer services that can then be accessed by the normal users. System services do not require special permissions, but as they are not specific to a particular user, they probably should not run as a particular user. Also, there should typically only be one GNUnet peer per host. System services include the gnunet−service and gnunet−daemon programs; support tools include command−line programs such as gnunet−arm.
Privileged helpers
Some GNUnet components require root rights to open raw sockets or perform other special operations. These gnunet−helper binaries are typically installed SUID and run from services or daemons.
Critical services
Some GNUnet services (such as the DNS service) can manipulate the service in deep and possibly highly security sensitive ways. For example, the DNS service can be used to intercept and alter any DNS query originating from the local machine. Access to the APIs of these critical services and their privileged helpers must be tightly controlled.
Todo
Shorten these subsection titles
Recommendation
− Disable access to services via TCP
GNUnet services allow two types of access: via TCP socket or
via UNIX domain socket. If the service is available via TCP,
access control can only be implemented by restricting
connections to a particular range of IP addresses. This is
acceptable for non−critical services that are supposed
to be available to all users on the local system or local
network. However, as TCP is generally less efficient and it
is rarely the case that a single GNUnet peer is supposed to
serve an entire local network, the default configuration
should disable TCP access to all GNUnet services on systems
with support for UNIX domain sockets. Since GNUnet 0.9.2,
configuration files with TCP access disabled should be
generated by default. Users can re−enable TCP access
to particular services simply by specifying a non−zero
port number in the section of the respective service.
Recommendation
− Run most services as system user
"gnunet"
GNUnet's main services should be run as a separate user
"gnunet" in a special group "gnunet".
The user "gnunet" should start the peer using
"gnunet−arm −s" during system startup.
The home directory for this user should be
/var/lib/gnunet and the configuration file should be
/etc/gnunet.conf. Only the gnunet user should
have the right to access /var/lib/gnunet (mode:
700).
Recommendation
− Control access to services using group
"gnunet"
Users that should be allowed to use the GNUnet peer should
be added to the group "gnunet". Using GNUnet's
access control mechanism for UNIX domain sockets, those
services that are considered useful to ordinary users should
be made available by setting "UNIX_MATCH_GID=YES"
for those services. Again, as shipped, GNUnet provides
reasonable defaults. Permissions to access the transport and
core subsystems might additionally be granted without
necessarily causing security concerns. Some services, such
as DNS, must NOT be made accessible to the
"gnunet" group (and should thus only be accessible
to the "gnunet" user and services running with
this UID).
Recommendation
− Limit access to certain SUID binaries by group
"gnunet"
Most of GNUnet's SUID binaries should be safe even if
executed by normal users. However, it is possible to reduce
the risk a little bit more by making these binaries owned by
the group "gnunet" and restricting their execution
to user of the group "gnunet" as well (4750).
Recommendation
− Limit access to critical
gnunet−helper−dns to group
"gnunetdns"
A special group "gnunetdns" should be created for
controlling access to the
"gnunet−helper−dns". The binary should
then be owned by root and be in group "gnunetdns"
and be installed SUID and only be group−executable
(2750). Note that the group "gnunetdns" should
have no users in it at all, ever. The
"gnunet−service−dns" program should be
executed by user "gnunet" (via
gnunet−service−arm) with the binary owned by the
user "root" and the group "gnunetdns"
and be SGID (2700). This way, only
"gnunet−service−dns" can change its
group to "gnunetdns" and execute the helper, and
the helper can then run as root (as per SUID). Access to the
API offered by "gnunet−service−dns" is
in turn restricted to the user "gnunet" (not the
group!), which means that only "benign" services
can manipulate DNS queries using
"gnunet−service−dns".
Differences
between "make install" and these
recommendations
The current build system does not set all permissions
automatically based on the recommendations above. In
particular, it does not use the group "gnunet" at
all (so setting gnunet−helpers other than the
gnunet−helper−dns to be owned by group
"gnunet" must be done manually). Furthermore,
'make install' will silently fail to set the DNS binaries to
be owned by group "gnunetdns" unless that group
already exists (!). An alternative name for the
"gnunetdns" group can be specified using the
−−with−gnunetdns=GRPNAME configure
option.
Configuring
the Friend−to−Friend (F2F) mode
GNUnet knows three basic modes of operation:
• |
In standard "peer−to−peer" mode, your peer will connect to any peer. | ||
• |
In the pure "friend−to−friend" mode, your peer will ONLY connect to peers from a list of friends specified in the configuration. | ||
• |
Finally, in mixed mode, GNUnet will only connect to arbitrary peers if it has at least a specified number of connections to friends. |
When configuring any of the F2F ("friend−to−friend") modes, you first need to create a file with the peer identities of your friends. Ask your friends to run
$ gnunet−peerinfo −sq
The resulting output of this command needs to be added to your friends file, which is simply a plain text file with one line per friend with the output from the above command.
You then specify the location of your friends file in the FRIENDS option of the "topology" section.
Once you have created the friends file, you can tell GNUnet to only connect to your friends by setting the FRIENDS−ONLY option (again in the "topology" section) to YES.
If you want to run in mixed−mode, set "FRIENDS−ONLY" to NO and configure a minimum number of friends to have (before connecting to arbitrary peers) under the "MINIMUM−FRIENDS" option.
If you want to operate in normal P2P−only mode, simply set MINIMUM−FRIENDS to zero and FRIENDS_ONLY to NO. This is the default.
Configuring
the hostlist to bootstrap
After installing the software you need to get connected to
the GNUnet network. The configuration file included in your
download is already configured to connect you to the GNUnet
network. In this section the relevant configuration settings
are explained.
To get an initial connection to the GNUnet network and to get to know peers already connected to the network you can use the so called "bootstrap servers". These servers can give you a list of peers connected to the network. To use these bootstrap servers you have to configure the hostlist daemon to activate bootstrapping.
To activate bootstrapping, edit the [hostlist]−section in your configuration file. You have to set the argument −b in the options line:
[hostlist] OPTIONS = −b
Additionally you have to specify which server you want to use. The default bootstrapping server is "http://v10.gnunet.org/hostlist". [^] To set the server you have to edit the line "SERVERS" in the hostlist section. To use the default server you should set the lines to
SERVERS = http://v10.gnunet.org/hostlist [^]
To use bootstrapping your configuration file should include these lines:
[hostlist] OPTIONS = −b SERVERS = http://v10.gnunet.org/hostlist [^]
Besides using bootstrap servers you can configure your GNUnet peer to receive hostlist advertisements. Peers offering hostlists to other peers can send advertisement messages to peers that connect to them. If you configure your peer to receive these messages, your peer can download these lists and connect to the peers included. These lists are persistent, which means that they are saved to your hard disk regularly and are loaded during startup.
To activate hostlist learning you have to add the −e switch to the OPTIONS line in the hostlist section:
[hostlist] OPTIONS = −b −e
Furthermore you can specify in which file the lists are saved. To save the lists in the file hostlists.file just add the line:
HOSTLISTFILE = hostlists.file
Best practice is to activate both bootstrapping and hostlist learning. So your configuration file should include these lines:
[hostlist] OPTIONS = −b −e HTTPPORT = 8080 SERVERS = http://v10.gnunet.org/hostlist [^] HOSTLISTFILE = $SERVICEHOME/hostlists.file
Configuration
of the HOSTLIST proxy settings
The hostlist client can be configured to use a proxy to
connect to the hostlist server.
The hostlist client supports the following proxy types at the moment:
• |
HTTP and HTTP 1.0 only proxy |
|||
• |
SOCKS 4/4a/5/5 with hostname |
In addition authentication at the proxy with username and password can be configured.
To provide these options directly in the configuration, you can enter the following settings in the [hostlist] section of the configuration:
# Type of proxy server, # Valid values: HTTP, HTTP_1_0, SOCKS4, SOCKS5, SOCKS4A, SOCKS5_HOSTNAME # Default: HTTP # PROXY_TYPE = HTTP # Hostname or IP of proxy server # PROXY = # User name for proxy server # PROXY_USERNAME = # User password for proxy server # PROXY_PASSWORD =
Configuring
your peer to provide a hostlist
If you operate a peer permanently connected to GNUnet you
can configure your peer to act as a hostlist server,
providing other peers the list of peers known to him.
Your server can act as a bootstrap server and peers needing to obtain a list of peers can contact it to download this list. To download this hostlist the peer uses HTTP. For this reason you have to build your peer with libgnurl (or libcurl) and microhttpd support.
To configure your peer to act as a bootstrap server you have to add the −p option to OPTIONS in the [hostlist] section of your configuration file. Besides that you have to specify a port number for the http server. In conclusion you have to add the following lines:
[hostlist] HTTPPORT = 12980 OPTIONS = −p
If your peer acts as a bootstrap server other peers should know about that. You can advertise the hostlist your are providing to other peers. Peers connecting to your peer will get a message containing an advertisement for your hostlist and the URL where it can be downloaded. If this peer is in learning mode, it will test the hostlist and, in the case it can obtain the list successfully, it will save it for bootstrapping.
To activate hostlist advertisement on your peer, you have to set the following lines in your configuration file:
[hostlist] EXTERNAL_DNS_NAME = example.org HTTPPORT = 12981 OPTIONS = −p −a
With this configuration your peer will a act as a bootstrap server and advertise this hostlist to other peers connecting to it. The URL used to download the list will be http://example.org:12981/.
Please notice:
• |
The hostlist is not human readable, so you should not try to download it using your webbrowser. Just point your GNUnet peer to the address! | ||
• |
Advertising without providing a hostlist does not make sense and will not work. |
Configuring
the datastore
The datastore is what GNUnet uses for long−term
storage of file−sharing data. Note that
long−term does not mean 'forever' since content does
have an expiration date, and of course storage space is
finite (and hence sometimes content may have to be
discarded).
Use the QUOTA option to specify how many bytes of storage space you are willing to dedicate to GNUnet.
In addition to specifying the maximum space GNUnet is allowed to use for the datastore, you need to specify which database GNUnet should use to do so. Currently, you have the choice between sqLite, MySQL and Postgres.
Configuring
the MySQL database
This section describes how to setup the MySQL database for
GNUnet.
Note that the mysql plugin does NOT work with mysql before 4.1 since we need prepared statements. We are generally testing the code against MySQL 5.1 at this point.
Reasons for using MySQL
• |
On up−to−date hardware where mysql can be used comfortably, this module will have better performance than the other database choices (according to our tests). | ||
• |
Its often possible to recover the mysql database from internal inconsistencies. Some of the other databases do not support repair. |
Reasons for not using MySQL
• |
Memory usage (likely not an issue if you have more than 1 GB) | ||
• |
Complex manual setup |
Setup Instructions
• |
In gnunet.conf set in section DATASTORE the value for DATABASE to mysql. | ||
• |
Access mysql as root: |
$ mysql −u root −p
and issue the following commands, replacing $USER with the username that will be running gnunet−arm (so typically "gnunet"):
CREATE DATABASE gnunet; GRANT select,insert,update,delete,create,alter,drop,create temporary tables ON gnunet.* TO $USER@localhost; SET PASSWORD FOR $USER@localhost=PASSWORD('$the_password_you_like'); FLUSH PRIVILEGES;
• |
In the $HOME directory of $USER, create a .my.cnf file with the following lines |
[client] user=$USER password=$the_password_you_like
That's it. Note that .my.cnf file is a slight security risk unless its on a safe partition. The $HOME/.my.cnf can of course be a symbolic link. Luckily $USER has only privileges to mess up GNUnet's tables, which should be pretty harmless.
Testing
You should briefly try if the database connection works.
First, login as $USER. Then use:
$ mysql −u $USER mysql> use gnunet;
If you get the message
Database changed
it probably works.
If you get
ERROR 2002: Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
it may be resolvable by
ln −s /var/run/mysqld/mysqld.sock /tmp/mysql.sock
so there may be some additional trouble depending on your mysql setup.
Performance
Tuning
For GNUnet, you probably want to set the option
Todo
Code block not C, set appropriate language
innodb_flush_log_at_trx_commit = 0
for a rather dramatic boost in MySQL performance. However, this reduces the "safety" of your database as with this options you may loose transactions during a power outage. While this is totally harmless for GNUnet, the option applies to all applications using MySQL. So you should set it if (and only if) GNUnet is the only application on your system using MySQL.
Setup for
running Testcases
If you want to run the testcases, you must create a second
database "gnunetcheck" with the same username and
password. This database will then be used for testing
(make check).
Configuring
the Postgres database
This text describes how to setup the Postgres database for
GNUnet.
This Postgres plugin was developed for Postgres 8.3 but might work for earlier versions as well.
Reasons to use Postgres
• |
Easier to setup than MySQL |
|||
• |
Real database |
Reasons not to use Postgres
• |
Quite slow |
|||
• |
Still some manual setup required |
Manual setup instructions
• |
In gnunet.conf set in section DATASTORE the value for DATABASE to postgres. | ||
• |
Access Postgres to create a user: |
with Postgres 8.x, use:
# su − postgres $ createuser
and enter the name of the user running GNUnet for the role interactively. Then, when prompted, do not set it to superuser, allow the creation of databases, and do not allow the creation of new roles.
with Postgres 9.x, use:
# su − postgres $ createuser −d $GNUNET_USER
where $GNUNET_USER is the name of the user running GNUnet.
• |
As that user (so typically as user "gnunet"), create a database (or two): |
$ createdb gnunet # this way you can run "make check" $ createdb gnunetcheck
Now you should be able to start gnunet−arm.
Testing the
setup manually
You may want to try if the database connection works. First,
again login as the user who will run
gnunet−arm. Then use:
$ psql gnunet # or gnunetcheck gnunet=> \dt
If, after you have started gnunet−arm at least once, you get a gn090 table here, it probably works.
Configuring
the datacache
The datacache is what GNUnet uses for storing temporary
data. This data is expected to be wiped completely each time
GNUnet is restarted (or the system is rebooted).
You need to specify how many bytes GNUnet is allowed to use for the datacache using the QUOTA option in the section [dhtcache]. Furthermore, you need to specify which database backend should be used to store the data. Currently, you have the choice between sqLite, MySQL and Postgres.
Configuring
the file−sharing service
In order to use GNUnet for file−sharing, you first
need to make sure that the file−sharing service is
loaded. This is done by setting the START_ON_DEMAND
option in section [fs] to "YES".
Alternatively, you can run
$ gnunet−arm −i fs
to start the file−sharing service by hand.
Except for configuring the database and the datacache the only important option for file−sharing is content migration.
Content migration allows your peer to cache content from other peers as well as send out content stored on your system without explicit requests. This content replication has positive and negative impacts on both system performance and privacy.
FIXME: discuss the trade−offs. Here is some older text about it...
Setting this option to YES allows gnunetd to migrate data to the local machine. Setting this option to YES is highly recommended for efficiency. Its also the default. If you set this value to YES, GNUnet will store content on your machine that you cannot decrypt. While this may protect you from liability if the judge is sane, it may not (IANAL). If you put illegal content on your machine yourself, setting this option to YES will probably increase your chances to get away with it since you can plausibly deny that you inserted the content. Note that in either case, your anonymity would have to be broken first (which may be possible depending on the size of the GNUnet network and the strength of the adversary).
Configuring
logging
Since version 0.9.0, logging in GNUnet is controlled via the
−L and −l options. Using
−L, a log level can be specified. With log
level ERROR only serious errors are logged. The
default log level is WARNING which causes anything of
concern to be logged. Log level INFO can be used to
log anything that might be interesting information whereas
DEBUG can be used by developers to log debugging
messages (but you need to run ./configure with
−−enable−logging=verbose to get
them compiled). The −l option is used to
specify the log file.
Since most GNUnet services are managed by gnunet−arm, using the −l or −L options directly is not possible. Instead, they can be specified using the OPTIONS configuration value in the respective section for the respective service. In order to enable logging globally without editing the OPTIONS values for each service, gnunet−arm supports a GLOBAL_POSTFIX option. The value specified here is given as an extra option to all services for which the configuration does contain a service−specific OPTIONS field.
GLOBAL_POSTFIX can contain the special sequence "{}" which is replaced by the name of the service that is being started. Furthermore, GLOBAL_POSTFIX is special in that sequences starting with "$" anywhere in the string are expanded (according to options in PATHS); this expansion otherwise is only happening for filenames and then the "$" must be the first character in the option. Both of these restrictions do not apply to GLOBAL_POSTFIX. Note that specifying % anywhere in the GLOBAL_POSTFIX disables both of these features.
In summary, in order to get all services to log at level INFO to log−files called SERVICENAME−logs, the following global prefix should be used:
GLOBAL_POSTFIX = −l $SERVICEHOME/{}−logs −L INFO
Configuring
the transport service and plugins
The transport service in GNUnet is responsible to maintain
basic connectivity to other peers. Besides initiating and
keeping connections alive it is also responsible for address
validation.
The GNUnet transport supports more than one transport protocol. These protocols are configured together with the transport service.
The configuration section for the transport service itself is quite similar to all the other services
START_ON_DEMAND = YES @UNIXONLY@ PORT = 2091 HOSTNAME = localhost HOME = $SERVICEHOME CONFIG = $DEFAULTCONFIG BINARY = gnunet−service−transport #PREFIX = valgrind NEIGHBOUR_LIMIT = 50 ACCEPT_FROM = 127.0.0.1; ACCEPT_FROM6 = ::1; PLUGINS = tcp udp UNIXPATH = /tmp/gnunet−service−transport.sock
Different are the settings for the plugins to load PLUGINS. The first setting specifies which transport plugins to load.
• |
transport−unix A plugin for local only communication with UNIX domain sockets. Used for testing and available on unix systems only. Just set the port |
[transport−unix] PORT = 22086 TESTING_IGNORE_KEYS = ACCEPT_FROM;
• |
transport−tcp A plugin for communication with TCP. Set port to 0 for client mode with outbound only connections |
[transport−tcp] # Use 0 to ONLY advertise as a peer behind NAT (no port binding) PORT = 2086 ADVERTISED_PORT = 2086 TESTING_IGNORE_KEYS = ACCEPT_FROM; # Maximum number of open TCP connections allowed MAX_CONNECTIONS = 128
• |
transport−udp A plugin for communication with UDP. Supports peer discovery using broadcasts. |
[transport−udp] PORT = 2086 BROADCAST = YES BROADCAST_INTERVAL = 30 s MAX_BPS = 1000000 TESTING_IGNORE_KEYS = ACCEPT_FROM;
• |
transport−http HTTP and HTTPS support is split in two part: a client plugin initiating outbound connections and a server part accepting connections from the client. The client plugin just takes the maximum number of connections as an argument. |
[transport−http_client] MAX_CONNECTIONS = 128 TESTING_IGNORE_KEYS = ACCEPT_FROM; [transport−https_client] MAX_CONNECTIONS = 128 TESTING_IGNORE_KEYS = ACCEPT_FROM;
The server has a port configured and the maximum number of connections. The HTTPS part has two files with the certificate key and the certificate file.
The server plugin supports reverse proxies, so a external hostname can be set using the EXTERNAL_HOSTNAME setting. The webserver under this address should forward the request to the peer and the configure port.
[transport−http_server] EXTERNAL_HOSTNAME = fulcrum.net.in.tum.de/gnunet PORT = 1080 MAX_CONNECTIONS = 128 TESTING_IGNORE_KEYS = ACCEPT_FROM; [transport−https_server] PORT = 4433 CRYPTO_INIT = NORMAL KEY_FILE = https.key CERT_FILE = https.cert MAX_CONNECTIONS = 128 TESTING_IGNORE_KEYS = ACCEPT_FROM;
• |
transport−wlan |
The next section describes how to setup the WLAN plugin, so here only the settings. Just specify the interface to use:
[transport−wlan] # Name of the interface in monitor mode (typically monX) INTERFACE = mon0 # Real hardware, no testing TESTMODE = 0 TESTING_IGNORE_KEYS = ACCEPT_FROM;
Configuring
the WLAN transport plugin
The wlan transport plugin enables GNUnet to send and to
receive data on a wlan interface. It has not to be connected
to a wlan network as long as sender and receiver are on the
same channel. This enables you to get connection to GNUnet
where no internet access is possible, for example during
catastrophes or when censorship cuts you off from the
internet.
Requirements for the WLAN plugin
• |
wlan network card with monitor support and packet injection (see aircrack−ng.org) | ||
• |
Linux kernel with mac80211 stack, introduced in 2.6.22, tested with 2.6.35 and 2.6.38 | ||
• |
Wlantools to create the a monitor interface, tested with airmon−ng of the aircrack−ng package |
Configuration
There are the following options for the wlan plugin (they
should be like this in your default config file, you only
need to adjust them if the values are incorrect for your
system)
# section for the wlan transport plugin [transport−wlan] # interface to use, more information in the # "Before starting GNUnet" section of the handbook. INTERFACE = mon0 # testmode for developers: # 0 use wlan interface, #1 or 2 use loopback driver for tests 1 = server, 2 = client TESTMODE = 0
Before
starting GNUnet
Before starting GNUnet, you have to make sure that your wlan
interface is in monitor mode. One way to put the wlan
interface into monitor mode (if your interface name is
wlan0) is by executing:
sudo airmon−ng start wlan0
Here is an example what the result should look like:
Interface Chipset Driver wlan0 Intel 4965 a/b/g/n iwl4965 − [phy0] (monitor mode enabled on mon0)
The monitor interface is mon0 is the one that you have to put into the configuration file.
Limitations
and known bugs
Wlan speed is at the maximum of 1 Mbit/s because support for
choosing the wlan speed with packet injection was removed in
newer kernels. Please pester the kernel developers about
fixing this.
The interface channel depends on the wlan network that the card is connected to. If no connection has been made since the start of the computer, it is usually the first channel of the card. Peers will only find each other and communicate if they are on the same channel. Channels must be set manually, e.g. by using:
iwconfig wlan0 channel 1
Configuring
HTTP(S) reverse proxy functionality using Apache or
nginx
The HTTP plugin supports data transfer using reverse
proxies. A reverse proxy forwards the HTTP request he
receives with a certain URL to another webserver, here a
GNUnet peer.
So if you have a running Apache or nginx webserver you can configure it to be a GNUnet reverse proxy. Especially if you have a well−known website this improves censorship resistance since it looks as normal surfing behaviour.
To do so, you have to do two things:
• |
Configure your webserver to forward the GNUnet HTTP traffic | ||
• |
Configure your GNUnet peer to announce the respective address |
As an example we want to use GNUnet peer running:
• |
HTTP server plugin on gnunet.foo.org:1080 |
|||
• |
HTTPS server plugin on gnunet.foo.org:4433 |
|||
• |
A apache or nginx webserver on http://www.foo.org:80/ |
|||
• |
A apache or nginx webserver on https://www.foo.org:443/ |
And we want the webserver to accept GNUnet traffic under http://www.foo.org/bar/. The required steps are described here:
Reverse
Proxy − Configure your Apache2 HTTP webserver
First of all you need mod_proxy installed.
Edit your webserver configuration. Edit /etc/apache2/apache2.conf or the site−specific configuration file.
In the respective server config,virtual host or directory section add the following lines:
ProxyTimeout 300 ProxyRequests Off <Location /bar/ > ProxyPass http://gnunet.foo.org:1080/ ProxyPassReverse http://gnunet.foo.org:1080/ </Location>
Reverse
Proxy − Configure your Apache2 HTTPS webserver
We assume that you already have an HTTPS server running, if
not please check how to configure a HTTPS host. An
uncomplicated to use example is the example configuration
file for Apache2/HTTPD provided in
apache2/sites−available/default−ssl.
In the respective HTTPS server config,virtual host or directory section add the following lines:
SSLProxyEngine On ProxyTimeout 300 ProxyRequests Off <Location /bar/ > ProxyPass https://gnunet.foo.org:4433/ ProxyPassReverse https://gnunet.foo.org:4433/ </Location>
More information about the apache mod_proxy configuration can be found in the Apache documentation.
Reverse
Proxy − Configure your nginx HTTPS webserver
Since nginx does not support chunked encoding, you first of
all have to install the chunkin module.
To enable chunkin add:
chunkin on; error_page 411 = @my_411_error; location @my_411_error { chunkin_resume; }
Edit your webserver configuration. Edit /etc/nginx/nginx.conf or the site−specific configuration file.
In the server section add:
location /bar/ { proxy_pass http://gnunet.foo.org:1080/; proxy_buffering off; proxy_connect_timeout 5; # more than http_server proxy_read_timeout 350; # 60 default, 300s is GNUnet's idle timeout proxy_http_version 1.1; # 1.0 default proxy_next_upstream error timeout invalid_header http_500 http_503 http_502 http_504; }
Reverse
Proxy − Configure your nginx HTTP webserver
Edit your webserver configuration. Edit
/etc/nginx/nginx.conf or the site−specific
configuration file.
In the server section add:
ssl_session_timeout 6m; location /bar/ { proxy_pass https://gnunet.foo.org:4433/; proxy_buffering off; proxy_connect_timeout 5; # more than http_server proxy_read_timeout 350; # 60 default, 300s is GNUnet's idle timeout proxy_http_version 1.1; # 1.0 default proxy_next_upstream error timeout invalid_header http_500 http_503 http_502 http_504; }
Reverse
Proxy − Configure your GNUnet peer
To have your GNUnet peer announce the address, you have to
specify the EXTERNAL_HOSTNAME option in the
[transport−http_server] section:
[transport−http_server] EXTERNAL_HOSTNAME = http://www.foo.org/bar/
and/or [transport−https_server] section:
[transport−https_server] EXTERNAL_HOSTNAME = https://www.foo.org/bar/
Now restart your webserver and your peer...
Blacklisting
peers
Transport service supports to deny connecting to a specific
peer of to a specific peer with a specific transport plugin
using the blacklisting component of transport service.
With blacklisting it is possible to deny connections to
specific peers of to use a specific plugin to a
specific peer. Peers can be blacklisted using the
configuration or a blacklist client can be asked.
To blacklist peers using the configuration you have to add a section to your configuration containing the peer id of the peer to blacklist and the plugin if required.
Examples:
To blacklist connections to P565... on peer AG2P... using tcp add:
Todo
too long?
Todo
verify whether these still produce errors in pdf output
[transport−blacklist AG2PHES1BARB9IJCPAMJTFPVJ5V3A72S3F2A8SBUB8DAQ2V0O3V8G6G2JU56FHGFOHMQVKBSQFV98TCGTC3RJ1NINP82G0RC00N1520] P565723JO1C2HSN6J29TAQ22MN6CI8HTMUU55T0FUQG4CMDGGEQ8UCNBKUMB94GC8R9G4FB2SF9LDOBAJ6AMINBP4JHHDD6L7VD801G = tcp
To blacklist connections to P565... on peer AG2P... using all plugins add:
[transport−blacklist−AG2PHES1BARB9IJCPAMJTFPVJ5V3A72S3F2A8SBUB8DAQ2V0O3V8G6G2JU56FHGFOHMQVKBSQFV98TCGTC3RJ1NINP82G0RC00N1520] P565723JO1C2HSN6J29TAQ22MN6CI8HTMUU55T0FUQG4CMDGGEQ8UCNBKUMB94GC8R9G4FB2SF9LDOBAJ6AMINBP4JHHDD6L7VD801G =
You can also add a blacklist client using the blacklist API. On a blacklist check, blacklisting first checks internally if the peer is blacklisted and if not, it asks the blacklisting clients. Clients are asked if it is OK to connect to a peer ID, the plugin is omitted.
On blacklist check for (peer, plugin)
• |
Do we have a local blacklist entry for this peer and this plugin? | ||
• |
YES: disallow connection | ||
• |
Do we have a local blacklist entry for this peer and all plugins? | ||
• |
YES: disallow connection | ||
• |
Does one of the clients disallow? | ||
• |
YES: disallow connection |
Configuration
of the HTTP and HTTPS transport plugins
The client parts of the http and https transport plugins can
be configured to use a proxy to connect to the hostlist
server.
Both the HTTP and HTTPS clients support the following proxy types at the moment:
• |
HTTP 1.1 proxy |
|||
• |
SOCKS 4/4a/5/5 with hostname |
In addition authentication at the proxy with username and password can be configured.
To configure these options directly in the configuration, you can configure the following settings in the [transport−http_client] and [transport−https_client] section of the configuration:
# Type of proxy server, # Valid values: HTTP, SOCKS4, SOCKS5, SOCKS4A, SOCKS5_HOSTNAME # Default: HTTP # PROXY_TYPE = HTTP # Hostname or IP of proxy server # PROXY = # User name for proxy server # PROXY_USERNAME = # User password for proxy server # PROXY_PASSWORD =
Configuring
the GNUnet VPN
Before configuring the GNUnet VPN, please make sure that
system−wide DNS interception is configured properly as
described in the section on the GNUnet DNS setup. see
Configuring the GNU Name System, if you haven't done
so already.
The default options for the GNUnet VPN are usually sufficient to use GNUnet as a Layer 2 for your Internet connection. However, what you always have to specify is which IP protocol you want to tunnel: IPv4, IPv6 or both. Furthermore, if you tunnel both, you most likely should also tunnel all of your DNS requests. You theoretically can tunnel "only" your DNS traffic, but that usually makes little sense.
The other options as shown on the gnunet−setup tool are:
IPv4 address
for interface
This is the IPv4 address the VPN interface will get. You
should pick a 'private' IPv4 network that is not yet in use
for you system. For example, if you use
10.0.0.1/255.255.0.0 already, you might use
10.1.0.1/255.255.0.0. If you use
10.0.0.1/255.0.0.0 already, then you might use
192.168.0.1/255.255.0.0. If your system is not in a
private IP−network, using any of the above will work
fine. You should try to make the mask of the address big
enough (255.255.0.0 or, even better,
255.0.0.0) to allow more mappings of remote IP
Addresses into this range. However, even a
255.255.255.0 mask will suffice for most users.
IPv6 address
for interface
The IPv6 address the VPN interface will get. Here you can
specify any non−link−local address (the address
should not begin with fe80:). A subnet Unique Local
Unicast (fd00::/8 prefix) that you are currently not
using would be a good choice.
Configuring
the GNUnet VPN DNS
To resolve names for remote nodes, activate the DNS exit
option.
Configuring
the GNUnet VPN Exit Service
If you want to allow other users to share your Internet
connection (yes, this may be dangerous, just as running a
Tor exit node) or want to provide access to services on your
host (this should be less dangerous, as long as those
services are secure), you have to enable the GNUnet exit
daemon.
You then get to specify which exit functions you want to provide. By enabling the exit daemon, you will always automatically provide exit functions for manually configured local services (this component of the system is under development and not documented further at this time). As for those services you explicitly specify the target IP address and port, there is no significant security risk in doing so.
Furthermore, you can serve as a DNS, IPv4 or IPv6 exit to the Internet. Being a DNS exit is usually pretty harmless. However, enabling IPv4 or IPv6−exit without further precautions may enable adversaries to access your local network, send spam, attack other systems from your Internet connection and do other mischiefs that will appear to come from your machine. This may or may not get you into legal trouble. If you want to allow IPv4 or IPv6−exit functionality, you should strongly consider adding additional firewall rules manually to protect your local network and to restrict outgoing TCP traffic (e.g. by not allowing access to port 25). While we plan to improve exit−filtering in the future, you're currently on your own here. Essentially, be prepared for any kind of IP−traffic to exit the respective TUN interface (and GNUnet will enable IP−forwarding and NAT for the interface automatically).
Additional configuration options of the exit as shown by the gnunet−setup tool are:
IP Address
of external DNS resolver
If DNS traffic is to exit your machine, it will be send to
this DNS resolver. You can specify an IPv4 or IPv6
address.
IPv4 address
for Exit interface
This is the IPv4 address the Interface will get. Make the
mask of the address big enough (255.255.0.0 or, even better,
255.0.0.0) to allow more mappings of IP addresses into this
range. As for the VPN interface, any unused, private IPv4
address range will do.
IPv6 address
for Exit interface
The public IPv6 address the interface will get. If your
kernel is not a very recent kernel and you are willing to
manually enable IPv6−NAT, the IPv6 address you specify
here must be a globally routed IPv6 address of your
host.
Suppose your host has the address 2001:4ca0::1234/64, then using 2001:4ca0::1:0/112 would be fine (keep the first 64 bits, then change at least one bit in the range before the bitmask, in the example above we changed bit 111 from 0 to 1).
You may also have to configure your router to route traffic for the entire subnet (2001:4ca0::1:0/112 for example) through your computer (this should be automatic with IPv6, but obviously anything can be disabled).
Bandwidth
Configuration
You can specify how many bandwidth GNUnet is allowed to use
to receive and send data. This is important for users with
limited bandwidth or traffic volume.
Configuring
NAT
Most hosts today do not have a normal global IP address but
instead are behind a router performing Network Address
Translation (NAT) which assigns each host in the local
network a private IP address. As a result, these machines
cannot trivially receive inbound connections from the
Internet. GNUnet supports NAT traversal to enable these
machines to receive incoming connections from other peers
despite their limitations.
In an ideal world, you can press the "Attempt automatic configuration" button in gnunet−setup to automatically configure your peer correctly. Alternatively, your distribution might have already triggered this automatic configuration during the installation process. However, automatic configuration can fail to determine the optimal settings, resulting in your peer either not receiving as many connections as possible, or in the worst case it not connecting to the network at all.
To manually configure the peer, you need to know a few things about your network setup. First, determine if you are behind a NAT in the first place. This is always the case if your IP address starts with "10.*" or "192.168.*". Next, if you have control over your NAT router, you may choose to manually configure it to allow GNUnet traffic to your host. If you have configured your NAT to forward traffic on ports 2086 (and possibly 1080) to your host, you can check the "NAT ports have been opened manually" option, which corresponds to the "PUNCHED_NAT" option in the configuration file. If you did not punch your NAT box, it may still be configured to support UPnP, which allows GNUnet to automatically configure it. In that case, you need to install the "upnpc" command, enable UPnP (or PMP) on your NAT box and set the "Enable NAT traversal via UPnP or PMP" option (corresponding to "ENABLE_UPNP" in the configuration file).
Some NAT boxes can be traversed using the autonomous NAT traversal method. This requires certain GNUnet components to be installed with "SUID" privileges on your system (so if you're installing on a system you do not have administrative rights to, this will not work). If you installed as 'root', you can enable autonomous NAT traversal by checking the "Enable NAT traversal using ICMP method". The ICMP method requires a way to determine your NAT's external (global) IP address. This can be done using either UPnP, DynDNS, or by manual configuration. If you have a DynDNS name or know your external IP address, you should enter that name under "External (public) IPv4 address" (which corresponds to the "EXTERNAL_ADDRESS" option in the configuration file). If you leave the option empty, GNUnet will try to determine your external IP address automatically (which may fail, in which case autonomous NAT traversal will then not work).
Finally, if you yourself are not behind NAT but want to be able to connect to NATed peers using autonomous NAT traversal, you need to check the "Enable connecting to NATed peers using ICMP method" box.
Peer
configuration for distributors (e.g. Operating Systems)
The "GNUNET_DATA_HOME" in "[PATHS]" in
/etc/gnunet.conf should be manually set to
"/var/lib/gnunet/data/" as the default
"~/.local/share/gnunet/" is probably not that
appropriate in this case. Similarly, distributors may
consider pointing "GNUNET_RUNTIME_DIR" to
"/var/run/gnunet/" and "GNUNET_HOME" to
"/var/lib/gnunet/". Also, should a distributor
decide to override system defaults, all of these changes
should be done in a custom /etc/gnunet.conf and not
in the files in the config.d/ directory.
Given the proposed access permissions, the "gnunet−setup" tool must be run as use "gnunet" (and with option "−c /etc/gnunet.conf" so that it modifies the system configuration). As always, gnunet−setup should be run after the GNUnet peer was stopped using "gnunet−arm −e". Distributors might want to include a wrapper for gnunet−setup that allows the desktop−user to "sudo" (e.g. using gtksudo) to the "gnunet" user account and then runs "gnunet−arm −e", "gnunet−setup" and "gnunet−arm −s" in sequence.
This book is intended to be an introduction for programmers that want to extend the GNUnet framework. GNUnet is more than a simple peer−to−peer application.
For developers, GNUnet is:
• |
developed by a community that believes in the GNU philosophy | ||
• |
Free Software (Free as in Freedom), licensed under the GNU Affero General Public License | ||
• |
A set of standards, including coding conventions and architectural rules | ||
• |
A set of layered protocols, both specifying the communication between peers as well as the communication between components of a single peer | ||
• |
A set of libraries with well−defined APIs suitable for writing extensions |
In particular, the architecture specifies that a peer consists of many processes communicating via protocols. Processes can be written in almost any language. C, Java and Guile APIs exist for accessing existing services and for writing extensions. It is possible to write extensions in other languages by implementing the necessary IPC protocols.
GNUnet can be extended and improved along many possible dimensions, and anyone interested in Free Software and Freedom−enhancing Networking is welcome to join the effort. This Developer Handbook attempts to provide an initial introduction to some of the key design choices and central components of the system.
This part of the GNUnet documentation is far from complete, and we welcome informed contributions, be it in the form of new chapters, sections or insightful comments.
Contributing
Licenses of contributions
GNUnet is a GNU package. All code contributions must
thus be put under the GNU Affero Public License
(AGPL). All documentation should be put under FSF
approved licenses (see fdl).
By submitting documentation, translations, and other content to GNUnet you automatically grant the right to publish code under the GNU Public License and documentation under either or both the GNU Public License or the GNU Free Documentation License. When contributing to the GNUnet project, GNU standards and the GNU philosophy should be adhered to.
Copyright
Assignment
Todo
Link to copyright assignment.
We require a formal copyright assignment for GNUnet contributors to GNUnet e.V.; nevertheless, we do allow pseudonymous contributions. By signing the copyright agreement and submitting your code (or documentation) to us, you agree to share the rights to your code with GNUnet e.V.; GNUnet e.V. receives non−exclusive ownership rights, and in particular is allowed to dual−license the code. You retain non−exclusive rights to your contributions, so you can also share your contributions freely with other projects.
GNUnet e.V. will publish all accepted contributions under the AGPLv3 or any later version. The association may decide to publish contributions under additional licenses (dual−licensing).
We do not intentionally remove your name from your contributions; however, due to extensive editing it is not always trivial to attribute contributors properly. If you find that you significantly contributed to a file (or the project as a whole) and are not listed in the respective authors file or section, please do let us know.
Contributing
to the Reference Manual
Todo
Move section to contrib.rst?
Todo
Update contrib section to reflect move to
reStructuredText
• |
When writing documentation, please use gender−neutral wording when referring to people, such as singular âtheyâ, âtheirâ, âthemâ, and so forth. | ||
• |
Keep line length below 74 characters, except for URLs. URLs break in the PDF output when they contain linebreaks. | ||
• |
Do not use tab characters (see chapter 2.1 texinfo manual) | ||
• |
Write texts in the third person perspective. |
Contributing
testcases
In the core of GNUnet, we restrict new testcases to a small
subset of languages, in order of preference:
1. |
C |
|||
2. |
Portable Shell Scripts |
|||
3. |
Python (3.7 or later) |
We welcome efforts to remove our existing Python 2.7 scripts to replace them either with portable shell scripts or, at your choice, Python 3.7 or later.
If you contribute new python based testcases, we advise you to not repeat our past misfortunes and write the tests in a standard test framework like for example pytest.
For writing portable shell scripts, these tools are useful:
• |
Shellcheck, for static |
analysis of shell scripts.
• |
http://www.etalabs.net/sh_tricks.html, |
|||
• |
bash−dash (/bin/sh on Debian) interoperability |
•
checkbashisms, |
||||
• |
`https://wiki.ubuntu.com/DashAsBinSh`__ |
, and
• |
`https://mywiki.wooledge.org/Bashism`__ |
Style
Guide
This document contains normative rules for writing GNUnet
code and naming conventions used throughout the project.
Naming
conventions
Header files
For header files, the following suffixes should be used:
There exist a few exceptions to these rules within the codebase:
• |
gnunet_config.h and gnunet_directories.h are automatically generated. | ||
• |
gnunet_common.h, which defines fundamental routines | ||
• |
platform.h, first included. .. I have no idea what that means | ||
• |
gettext.h, an external library. |
Binaries
For binary files, the following convention should be
used:
Logging
The convention is to define a macro on a per−file
basis to manage logging:
#define LOG(kind,...) [logging_macro] (kind, "[component_name]", __VA_ARGS__)
The table below indicates the substitutions which should be made for [component_name] and [logging_macro].
Todo
Clear up terminology within the style guide (_lib, _service
mapped to appropriate software categories)
Todo
Interpret and write configuration style
Symbols
Exported symbols must be prefixed with
GNUNET_[module_name]_ and be defined in
[module_name].c. The only exceptions to this rule are
symbols defined in gnunet_common.h.
Private symbols, including structs and macros, must not be prefixed. In addition, they must not be exported in a way that linkers could use them or other libraries might see them via headers. This means that they must never be declared in src/include, and only declared or defined in C source files or headers under src/[module_name].
Tests
Test cases and performance tests should follow the naming
conventions
test_[module−under−test]_[test_description].c
and
perf_[module−under−test]_[test_description].c,
respectively.
In either case, if there is only a single test, [test_description] may be omitted.
src
subdirectories
Subdirectories of src
Coding style
Todo
Examples should follow GNU Coding Standards?
This project follows the GNU Coding Standards.
Indentation is done with two spaces per level, never with tabs. Specific (though incomplete) indentation rules are defined in an uncrustify configuration file (in contrib) and are enforced by Git hooks.
Todo
Link to uncrustify config in contrib.
C99−style struct initialisation is acceptable and generally encouraged.
Todo
Clarify whether there are cases where C99−style struct
init is discouraged?
As in all good C code, we care about symbol space pollution and thus use static to limit the scope where possible, even in the compilation unit that contains main.
Only one variable should be declared per line:
// bad int i,j; // good int i; int j;
This helps keep diffs small and forces developers to think precisely about the type of every variable.
Note that char * is different from const char* and int is different from unsigned int or uint32_t. Each variable type should be chosen with care.
libgnunetutil
libgnunetutil is the fundamental library that all GNUnet
code builds upon. Ideally, this library should contain most
of the platform dependent code (except for user interfaces
and really special needs that only few applications have).
It is also supposed to offer basic services that most if not
all GNUnet binaries require. The code of libgnunetutil is in
the src/util/ directory. The public interface to the
library is in the gnunet_util.h header.
The functions provided by libgnunetutil fall roughly into the following categories (in roughly the order of importance for new developers):
• |
logging (common_logging.c) |
|||
• |
memory allocation (common_allocation.c) |
|||
• |
endianness conversion (common_endian.c) |
|||
• |
internationalization (common_gettext.c) |
|||
• |
String manipulation (string.c) |
|||
• |
file access (disk.c) |
|||
• |
buffered disk IO (bio.c) |
|||
• |
time manipulation (time.c) |
|||
• |
configuration parsing (configuration.c) |
|||
• |
command−line handling (getopt*.c) |
|||
• |
cryptography (crypto_*.c) |
|||
• |
data structures (container_*.c) |
|||
• |
CPS−style scheduling (scheduler.c) |
|||
• |
Program initialization (program.c) |
|||
• |
Networking (network.c, client.c, server*.c, service.c) |
|||
• |
message queuing (mq.c) |
|||
• |
bandwidth calculations (bandwidth.c) |
|||
• |
Other OS−related (os*.c, plugin.c, signal.c) |
|||
• |
Pseudonym management (pseudonym.c) |
It should be noted that only developers that fully understand this entire API will be able to write good GNUnet code.
Ideally, porting GNUnet should only require porting the gnunetutil library. More testcases for the gnunetutil APIs are therefore a great way to make porting of GNUnet easier.
System
Architecture
Todo
FIXME: For those irritated by the textflow, we are missing
images here, in the short term we should add them back, in
the long term this should work without images or have images
with alt−text.
Todo
Adjust image sizes so that they are less obtrusive.
GNUnet developers like LEGOs. The blocks are indestructible, can be stacked together to construct complex buildings and it is generally easy to swap one block for a different one that has the same shape. GNUnetâs architecture is based on LEGOs:
[image: service_lego_block] [image]
This chapter documents the GNUnet LEGO system, also known as GNUnetâs system architecture.
The most common GNUnet component is a service. Services offer an API (or several, depending on what you count as "an API") which is implemented as a library. The library communicates with the main process of the service using a service−specific network protocol. The main process of the service typically doesnât fully provide everything that is needed â it has holes to be filled by APIs to other services.
A special kind of component in GNUnet are user interfaces and daemons. Like services, they have holes to be filled by APIs of other services. Unlike services, daemons do not implement their own network protocol and they have no API:
[image: daemon_lego_block] [image]
The GNUnet system provides a range of services, daemons and user interfaces, which are then combined into a layered GNUnet instance (also known as a peer).
[image: service_stack] [image]
Note that while it is generally possible to swap one service for another compatible service, there is often only one implementation. However, during development we often have a "new" version of a service in parallel with an "old" version. While the "new" version is not working, developers working on other parts of the service can continue their development by simply using the "old" service. Alternative design ideas can also be easily investigated by swapping out individual components. This is typically achieved by simply changing the name of the "BINARY" in the respective configuration section.
Key properties of GNUnet services are that they must be separate processes and that they must protect themselves by applying tight error checking against the network protocol they implement (thereby achieving a certain degree of robustness).
On the other hand, the APIs are implemented to tolerate failures of the service, isolating their host process from errors by the service. If the service process crashes, other services and daemons around it should not also fail, but instead wait for the service process to be restarted by ARM.
Todo
Missing subsystems:
• |
TOPOLOGY, FRAGMENTATION | ||
• |
VPN and support services (DNS, PT, EXIT) | ||
• |
DATASTORE (only used by FS?) | ||
• |
MULTICAST and social services (PSYC, PSYCSTORE, SOCIAL) | ||
• |
GNS support services/applications (GNSRECORD, ZONEMASTER) | ||
• |
Set−based applications (SCALARPRODUCT, SECRETSHARING, CONSENSUS, VOTING) |
Internal
dependencies
Todo
Break out into per−process information?
This section tries to give an overview of what processes a typical GNUnet peer running a particular application would consist of. All of the processes listed here should be automatically started by gnunet−arm −s. The list is given as a rough first guide to users for failure diagnostics. Ideally, end−users should never have to worry about these internal dependencies.
In terms of internal dependencies, a minimum file−sharing system consists of the following GNUnet processes (in order of dependency):
• |
gnunet−service−arm | ||
• |
gnunet−service−resolver (required by all) | ||
• |
gnunet−service−statistics (required by all) | ||
• |
gnunet−service−peerinfo | ||
• |
gnunet−service−transport (requires peerinfo) | ||
• |
gnunet−service−core (requires transport) | ||
• |
gnunet−daemon−hostlist (requires core) | ||
• |
gnunet−daemon−topology (requires hostlist, peerinfo) | ||
• |
gnunet−service−datastore | ||
• |
gnunet−service−dht (requires core) | ||
• |
gnunet−service−identity | ||
• |
gnunet−service−fs (requires identity, mesh, dht, datastore, core) |
A minimum VPN system consists of the following GNUnet processes (in order of dependency):
• |
gnunet−service−arm |
|||
• |
gnunet−service−resolver (required by all) |
|||
• |
gnunet−service−statistics (required by all) |
|||
• |
gnunet−service−peerinfo |
|||
• |
gnunet−service−transport (requires peerinfo) |
|||
• |
gnunet−service−core (requires transport) |
|||
• |
gnunet−daemon−hostlist (requires core) |
|||
• |
gnunet−service−dht (requires core) |
|||
• |
gnunet−service−mesh (requires dht, core) |
|||
• |
gnunet−service−dns (requires dht) |
|||
• |
gnunet−service−regex (requires dht) |
|||
• |
gnunet−service−vpn (requires regex, dns, mesh, dht) |
A minimum GNS system consists of the following GNUnet processes (in order of dependency):
• |
gnunet−service−arm | ||
• |
gnunet−service−resolver (required by all) | ||
• |
gnunet−service−statistics (required by all) | ||
• |
gnunet−service−peerinfo | ||
• |
gnunet−service−transport (requires peerinfo) | ||
• |
gnunet−service−core (requires transport) | ||
• |
gnunet−daemon−hostlist (requires core) | ||
• |
gnunet−service−dht (requires core) | ||
• |
gnunet−service−mesh (requires dht, core) | ||
• |
gnunet−service−dns (requires dht) | ||
• |
gnunet−service−regex (requires dht) | ||
• |
gnunet−service−vpn (requires regex, dns, mesh, dht) | ||
• |
gnunet−service−identity | ||
• |
gnunet−service−namestore (requires identity) | ||
• |
gnunet−service−gns (requires vpn, dns, dht, namestore, identity) |
Subsystem
stability
This page documents the current stability of the various
GNUnet subsystems. Stability here describes the expected
degree of compatibility with future versions of GNUnet.
For each subsystem we distinguish between compatibility on the P2P network level (communication protocol between peers), the IPC level (communication between the service and the service library) and the API level (stability of the API).
P2P compatibility is relevant in terms of which applications are likely going to be able to communicate with future versions of the network. IPC communication is relevant for the implementation of language bindings that re−implement the IPC messages. Finally, API compatibility is relevant to developers that hope to be able to avoid changes to applications built on top of the APIs of the framework.
The following table summarizes our current view of the stability of the respective protocols or APIs:
Todo
Make table automatically generated individual pages?
Here is a rough explanation of the values:
Todo
0.10.x is outdated − rewrite
âstableâ to reflect a
time−independent meaning.
âstableâ
No incompatible changes are planned at this time; for IPC/APIs, if there are incompatible changes, they will be minor and might only require minimal changes to existing code; for P2P, changes will be avoided if at all possible for the 0.10.x−series
âtestingâ
No incompatible changes are planned at this time, but the code is still known to be in flux; so while we have no concrete plans, our expectation is that there will still be minor modifications; for P2P, changes will likely be extensions that should not break existing code
âunstableâ
Changes are planned and will happen; however, they will not be totally radical and the result should still resemble what is there now; nevertheless, anticipated changes will break protocol/API compatibility
âexperimentalâ
Changes are planned and the result may look nothing like what the API/protocol looks like today
âunknownâ
Someone should think about where this subsystem headed
ân/aâ |
This subsystem does not implement a corresponding API/protocol |
Basic
Services
These services comprise a backbone of core services for
peer−to−peer applications to use.
STATISTICS
â Runtime statistics publication
In GNUnet, the STATISTICS subsystem offers a central place
for all subsystems to publish unsigned 64−bit integer
run−time statistics. Keeping this information
centrally means that there is a unified way for the user to
obtain data on all subsystems, and individual subsystems do
not have to always include a custom data export method for
performance metrics and other statistics. For example, the
TRANSPORT system uses STATISTICS to update information about
the number of directly connected peers and the bandwidth
that has been consumed by the various plugins. This
information is valuable for diagnosing connectivity and
performance issues.
Following the GNUnet service architecture, the STATISTICS subsystem is divided into an API which is exposed through the header gnunet_statistics_service.h and the STATISTICS service gnunet−service−statistics. The gnunet−statistics command−line tool can be used to obtain (and change) information about the values stored by the STATISTICS service. The STATISTICS service does not communicate with other peers.
Data is stored in the STATISTICS service in the form of tuples (subsystem, name, value, persistence). The subsystem determines to which other GNUnetâs subsystem the data belongs. name is the name through which value is associated. It uniquely identifies the record from among other records belonging to the same subsystem. In some parts of the code, the pair (subsystem, name) is called a statistic as it identifies the values stored in the STATISTCS service.The persistence flag determines if the record has to be preserved across service restarts. A record is said to be persistent if this flag is set for it; if not, the record is treated as a non−persistent record and it is lost after service restart. Persistent records are written to and read from the file statistics.data before shutdown and upon startup. The file is located in the HOME directory of the peer.
An anomaly of the STATISTICS service is that it does not terminate immediately upon receiving a shutdown signal if it has any clients connected to it. It waits for all the clients that are not monitors to close their connections before terminating itself. This is to prevent the loss of data during peer shutdown â delaying the STATISTICS service shutdown helps other services to store important data to STATISTICS during shutdown.
libgnunetstatistics
libgnunetstatistics is the library containing the API
for the STATISTICS subsystem. Any process requiring to use
STATISTICS should use this API by to open a connection to
the STATISTICS service. This is done by calling the function
GNUNET_STATISTICS_create(). This function takes the
subsystemâs name which is trying to use STATISTICS and
a configuration. All values written to STATISTICS with this
connection will be placed in the section corresponding to
the given subsystemâs name. The connection to
STATISTICS can be destroyed with the function
GNUNET_STATISTICS_destroy(). This function allows for
the connection to be destroyed immediately or upon
transferring all pending write requests to the service.
Note: STATISTICS subsystem can be disabled by setting DISABLE = YES under the [STATISTICS] section in the configuration. With such a configuration all calls to GNUNET_STATISTICS_create() return NULL as the STATISTICS subsystem is unavailable and no other functions from the API can be used.
Statistics
retrieval
Once a connection to the statistics service is obtained,
information about any other system which uses statistics can
be retrieved with the function GNUNET_STATISTICS_get(). This
function takes the connection handle, the name of the
subsystem whose information we are interested in (a
NULL value will retrieve information of all available
subsystems using STATISTICS), the name of the statistic we
are interested in (a NULL value will retrieve all
available statistics), a continuation callback which is
called when all of requested information is retrieved, an
iterator callback which is called for each parameter in the
retrieved information and a closure for the aforementioned
callbacks. The library then invokes the iterator callback
for each value matching the request.
Call to GNUNET_STATISTICS_get() is asynchronous and can be canceled with the function GNUNET_STATISTICS_get_cancel(). This is helpful when retrieving statistics takes too long and especially when we want to shutdown and cleanup everything.
Setting
statistics and updating them
So far we have seen how to retrieve statistics, here we will
learn how we can set statistics and update them so that
other subsystems can retrieve them.
A new statistic can be set using the function GNUNET_STATISTICS_set(). This function takes the name of the statistic and its value and a flag to make the statistic persistent. The value of the statistic should be of the type uint64_t. The function does not take the name of the subsystem; it is determined from the previous GNUNET_STATISTICS_create() invocation. If the given statistic is already present, its value is overwritten.
An existing statistics can be updated, i.e its value can be increased or decreased by an amount with the function GNUNET_STATISTICS_update(). The parameters to this function are similar to GNUNET_STATISTICS_set(), except that it takes the amount to be changed as a type int64_t instead of the value.
The library will combine multiple set or update operations into one message if the client performs requests at a rate that is faster than the available IPC with the STATISTICS service. Thus, the client does not have to worry about sending requests too quickly.
Watches
As interesting feature of STATISTICS lies in serving
notifications whenever a statistic of our interest is
modified. This is achieved by registering a watch through
the function GNUNET_STATISTICS_watch(). The
parameters of this function are similar to those of
GNUNET_STATISTICS_get(). Changes to the respective
statisticâs value will then cause the given iterator
callback to be called. Note: A watch can only be registered
for a specific statistic. Hence the subsystem name and the
parameter name cannot be NULL in a call to
GNUNET_STATISTICS_watch().
A registered watch will keep notifying any value changes until GNUNET_STATISTICS_watch_cancel() is called with the same parameters that are used for registering the watch.
The
STATISTICS Client−Service Protocol
Statistics retrieval
To retrieve statistics, the client transmits a message of
type GNUNET_MESSAGE_TYPE_STATISTICS_GET containing
the given subsystem name and statistic parameter to the
STATISTICS service. The service responds with a message of
type GNUNET_MESSAGE_TYPE_STATISTICS_VALUE for each of
the statistics parameters that match the client request for
the client. The end of information retrieved is signaled by
the service by sending a message of type
GNUNET_MESSAGE_TYPE_STATISTICS_END.
Setting and
updating statistics
The subsystem name, parameter name, its value and the
persistence flag are communicated to the service through the
message GNUNET_MESSAGE_TYPE_STATISTICS_SET.
When the service receives a message of type GNUNET_MESSAGE_TYPE_STATISTICS_SET, it retrieves the subsystem name and checks for a statistic parameter with matching the name given in the message. If a statistic parameter is found, the value is overwritten by the new value from the message; if not found then a new statistic parameter is created with the given name and value.
In addition to just setting an absolute value, it is possible to perform a relative update by sending a message of type GNUNET_MESSAGE_TYPE_STATISTICS_SET with an update flag (GNUNET_STATISTICS_SETFLAG_RELATIVE) signifying that the value in the message should be treated as an update value.
Watching for
updates
The function registers the watch at the service by sending a
message of type GNUNET_MESSAGE_TYPE_STATISTICS_WATCH.
The service then sends notifications through messages of
type GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE
whenever the statistic parameterâs value is
changed.
ATS â
Automatic transport selection
ATS stands for "automatic transport selection",
and the function of ATS in GNUnet is to decide on which
address (and thus transport plugin) should be used for two
peers to communicate, and what bandwidth limits should be
imposed on such an individual connection.
To help ATS make an informed decision, higher−level services inform the ATS service about their requirements and the quality of the service rendered. The ATS service also interacts with the transport service to be appraised of working addresses and to communicate its resource allocation decisions. Finally, the ATS serviceâs operation can be observed using a monitoring API.
The main logic of the ATS service only collects the available addresses, their performance characteristics and the applications requirements, but does not make the actual allocation decision. This last critical step is left to an ATS plugin, as we have implemented (currently three) different allocation strategies which differ significantly in their performance and maturity, and it is still unclear if any particular plugin is generally superior.
TRANSPORT
â Overlay transport management
This chapter documents how the GNUnet transport subsystem
works. The GNUnet transport subsystem consists of three main
components: the transport API (the interface used by the
rest of the system to access the transport service), the
transport service itself (most of the interesting functions,
such as choosing transports, happens here) and the transport
plugins. A transport plugin is a concrete implementation for
how two GNUnet peers communicate; many plugins exist, for
example for communication via TCP, UDP, HTTP, HTTPS and
others. Finally, the transport subsystem uses supporting
code, especially the NAT/UPnP library to help with tasks
such as NAT traversal.
Key tasks of the transport service include:
• |
Create our HELLO message, notify clients and neighbours if our HELLO changes (using NAT library as necessary) | ||
• |
Validate HELLOs from other peers (send PING), allow other peers to validate our HELLOâs addresses (send PONG) | ||
• |
Upon request, establish connections to other peers (using address selection from ATS subsystem) and maintain them (again using PINGs and PONGs) as long as desired | ||
• |
Accept incoming connections, give ATS service the opportunity to switch communication channels | ||
• |
Notify clients about peers that have connected to us or that have been disconnected from us | ||
• |
If a (stateful) connection goes down unexpectedly (without explicit DISCONNECT), quickly attempt to recover (without notifying clients) but do notify clients quickly if reconnecting fails | ||
• |
Send (payload) messages arriving from clients to other peers via transport plugins and receive messages from other peers, forwarding those to clients | ||
• |
Enforce inbound traffic limits (using flow−control if it is applicable); outbound traffic limits are enforced by CORE, not by us (!) | ||
• |
Enforce restrictions on P2P connection as specified by the blacklist configuration and blacklisting clients |
Note that the term "clients" in the list above really refers to the GNUnet−CORE service, as CORE is typically the only client of the transport service.
Address
validation protocol
This section documents how the GNUnet transport service
validates connections with other peers. It is a
high−level description of the protocol necessary to
understand the details of the implementation. It should be
noted that when we talk about PING and PONG messages in this
section, we refer to transport−level PING and PONG
messages, which are different from core−level PING and
PONG messages (both in implementation and function).
The goal of transport−level address validation is to minimize the chances of a successful man−in−the−middle attack against GNUnet peers on the transport level. Such an attack would not allow the adversary to decrypt the P2P transmissions, but a successful attacker could at least measure traffic volumes and latencies (raising the adversaries capabilities by those of a global passive adversary in the worst case). The scenarios we are concerned about is an attacker, Mallory, giving a HELLO to Alice that claims to be for Bob, but contains Malloryâs IP address instead of Bobs (for some transport). Mallory would then forward the traffic to Bob (by initiating a connection to Bob and claiming to be Alice). As a further complication, the scheme has to work even if say Alice is behind a NAT without traversal support and hence has no address of her own (and thus Alice must always initiate the connection to Bob).
An additional constraint is that HELLO messages do not contain a cryptographic signature since other peers must be able to edit (i.e. remove) addresses from the HELLO at any time (this was not true in GNUnet 0.8.x). A basic assumption is that each peer knows the set of possible network addresses that it might be reachable under (so for example, the external IP address of the NAT plus the LAN address(es) with the respective ports).
The solution is the following. If Alice wants to validate that a given address for Bob is valid (i.e. is actually established directly with the intended target), she sends a PING message over that connection to Bob. Note that in this case, Alice initiated the connection so only Alice knows which address was used for sure (Alice may be behind NAT, so whatever address Bob sees may not be an address Alice knows she has). Bob checks that the address given in the PING is actually one of Bobâs addresses (ie: does not belong to Mallory), and if it is, sends back a PONG (with a signature that says that Bob owns/uses the address from the PING). Alice checks the signature and is happy if it is valid and the address in the PONG is the address Alice used. This is similar to the 0.8.x protocol where the HELLO contained a signature from Bob for each address used by Bob. Here, the purpose code for the signature is GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN. After this, Alice will remember Bobâs address and consider the address valid for a while (12h in the current implementation). Note that after this exchange, Alice only considers Bobâs address to be valid, the connection itself is not considered âestablishedâ. In particular, Alice may have many addresses for Bob that Alice considers valid.
The PONG message is protected with a nonce/challenge against replay attacks (replay) and uses an expiration time for the signature (but those are almost implementation details).
NAT library .. _NAT−library:
NAT
library
The goal of the GNUnet NAT library is to provide a
general−purpose API for NAT traversal without
third−party support. So protocols that involve
contacting a third peer to help establish a connection
between two peers are outside of the scope of this API. That
does not mean that GNUnet doesnât support involving a
third peer (we can do this with the distance−vector
transport or using application−level protocols), it
just means that the NAT API is not concerned with this
possibility. The API is written so that it will work for
IPv6−NAT in the future as well as current
IPv4−NAT. Furthermore, the NAT API is always used,
even for peers that are not behind NAT â in that case,
the mapping provided is simply the identity.
NAT traversal is initiated by calling GNUNET_NAT_register. Given a set of addresses that the peer has locally bound to (TCP or UDP), the NAT library will return (via callback) a (possibly longer) list of addresses the peer might be reachable under. Internally, depending on the configuration, the NAT library will try to punch a hole (using UPnP) or just "know" that the NAT was manually punched and generate the respective external IP address (the one that should be globally visible) based on the given information.
The NAT library also supports ICMP−based NAT traversal. Here, the other peer can request connection−reversal by this peer (in this special case, the peer is even allowed to configure a port number of zero). If the NAT library detects a connection−reversal request, it returns the respective target address to the client as well. It should be noted that connection−reversal is currently only intended for TCP, so other plugins must pass NULL for the reversal callback. Naturally, the NAT library also supports requesting connection reversal from a remote peer (GNUNET_NAT_run_client).
Once initialized, the NAT handle can be used to test if a given address is possibly a valid address for this peer (GNUNET_NAT_test_address). This is used for validating our addresses when generating PONGs.
Finally, the NAT library contains an API to test if our NAT configuration is correct. Using GNUNET_NAT_test_start before binding to the respective port, the NAT library can be used to test if the configuration works. The test function act as a local client, initialize the NAT traversal and then contact a gnunet−nat−server (running by default on gnunet.org) and ask for a connection to be established. This way, it is easy to test if the current NAT configuration is valid.
Distance−Vector
plugin
The Distance Vector (DV) transport is a transport mechanism
that allows peers to act as relays for each other, thereby
connecting peers that would otherwise be unable to connect.
This gives a larger connection set to applications that may
work better with more peers to choose from (for example,
File Sharing and/or DHT).
The Distance Vector transport essentially has two functions. The first is "gossiping" connection information about more distant peers to directly connected peers. The second is taking messages intended for non−directly connected peers and encapsulating them in a DV wrapper that contains the required information for routing the message through forwarding peers. Via gossiping, optimal routes through the known DV neighborhood are discovered and utilized and the message encapsulation provides some benefits in addition to simply getting the message from the correct source to the proper destination.
The gossiping function of DV provides an up to date routing table of peers that are available up to some number of hops. We call this a fisheye view of the network (like a fish, nearby objects are known while more distant ones unknown). Gossip messages are sent only to directly connected peers, but they are sent about other knowns peers within the "fisheye distance". Whenever two peers connect, they immediately gossip to each other about their appropriate other neighbors. They also gossip about the newly connected peer to previously connected neighbors. In order to keep the routing tables up to date, disconnect notifications are propagated as gossip as well (because disconnects may not be sent/received, timeouts are also used remove stagnant routing table entries).
Routing of messages via DV is straightforward. When the DV transport is notified of a message destined for a non−direct neighbor, the appropriate forwarding peer is selected, and the base message is encapsulated in a DV message which contains information about the initial peer and the intended recipient. At each forwarding hop, the initial peer is validated (the forwarding peer ensures that it has the initial peer in its neighborhood, otherwise the message is dropped). Next the base message is re−encapsulated in a new DV message for the next hop in the forwarding chain (or delivered to the current peer, if it has arrived at the destination).
Assume a three peer network with peers Alice, Bob and Carol. Assume that
Alice <−> Bob and Bob <−> Carol
are direct (e.g. over TCP or UDP transports) connections, but that Alice cannot directly connect to Carol. This may be the case due to NAT or firewall restrictions, or perhaps based on one of the peers respective configurations. If the Distance Vector transport is enabled on all three peers, it will automatically discover (from the gossip protocol) that Alice and Carol can connect via Bob and provide a "virtual" Alice <−> Carol connection. Routing between Alice and Carol happens as follows; Alice creates a message destined for Carol and notifies the DV transport about it. The DV transport at Alice looks up Carol in the routing table and finds that the message must be sent through Bob for Carol. The message is encapsulated setting Alice as the initiator and Carol as the destination and sent to Bob. Bob receives the messages, verifies that both Alice and Carol are known to Bob, and re−wraps the message in a new DV message for Carol. The DV transport at Carol receives this message, unwraps the original message, and delivers it to Carol as though it came directly from Alice.
SMTP plugin .. _SMTP−plugin:
SMTP plugin
Todo
Update?
This section describes the new SMTP transport plugin for GNUnet as it exists in the 0.7.x and 0.8.x branch. SMTP support is currently not available in GNUnet 0.9.x. This page also describes the transport layer abstraction (as it existed in 0.7.x and 0.8.x) in more detail and gives some benchmarking results. The performance results presented are quite old and maybe outdated at this point. For the readers in the year 2019, you will notice by the mention of version 0.7, 0.8, and 0.9 that this section has to be taken with your usual grain of salt and be updated eventually.
• |
Why use SMTP for a peer−to−peer transport? |
|||
• |
SMTPHow does it work? |
|||
• |
How do I configure my peer? |
|||
• |
How do I test if it works? |
|||
• |
How fast is it? |
|||
• |
Is there any additional documentation? |
Why use SMTP
for a peer−to−peer transport?
There are many reasons why one would not want to use
SMTP:
• |
SMTP is using more bandwidth than TCP, UDP or HTTP | ||
• |
SMTP has a much higher latency. | ||
• |
SMTP requires significantly more computation (encoding and decoding time) for the peers. | ||
• |
SMTP is significantly more complicated to configure. | ||
• |
SMTP may be abused by tricking GNUnet into sending mail to non−participating third parties. |
So why would anybody want to use SMTP?
• |
SMTP can be used to contact peers behind NAT boxes (in virtual private networks). | ||
• |
SMTP can be used to circumvent policies that limit or prohibit peer−to−peer traffic by masking as "legitimate" traffic. | ||
• |
SMTP uses E−mail addresses which are independent of a specific IP, which can be useful to address peers that use dynamic IP addresses. | ||
• |
SMTP can be used to initiate a connection (e.g. initial address exchange) and peers can then negotiate the use of a more efficient protocol (e.g. TCP) for the actual communication. |
In summary, SMTP can for example be used to send a message to a peer behind a NAT box that has a dynamic IP to tell the peer to establish a TCP connection to a peer outside of the private network. Even an extraordinary overhead for this first message would be irrelevant in this type of situation.
How does it
work?
When a GNUnet peer needs to send a message to another GNUnet
peer that has advertised (only) an SMTP transport address,
GNUnet base64−encodes the message and sends it in an
E−mail to the advertised address. The advertisement
contains a filter which is placed in the E−mail
header, such that the receiving host can filter the tagged
E−mails and forward it to the GNUnet peer process. The
filter can be specified individually by each peer and be
changed over time. This makes it impossible to censor GNUnet
E−mail messages by searching for a generic filter.
How do I
configure my peer?
First, you need to configure procmail to filter your
inbound E−mail for GNUnet traffic. The GNUnet messages
must be delivered into a pipe, for example
/tmp/gnunet.smtp. You also need to define a filter
that is used by procmail to detect GNUnet messages.
You are free to choose whichever filter you like, but you
should make sure that it does not occur in your other
E−mail. In our example, we will use X−mailer:
GNUnet. The ~/.procmailrc configuration file then
looks like this:
:0: * ^X−mailer: GNUnet /tmp/gnunet.smtp # where do you want your other e−mail delivered to # (default: /var/spool/mail/) :0: /var/spool/mail/
After adding this file, first make sure that your regular E−mail still works (e.g. by sending an E−mail to yourself). Then edit the GNUnet configuration. In the section SMTP you need to specify your E−mail address under EMAIL, your mail server (for outgoing mail) under SERVER, the filter (X−mailer: GNUnet in the example) under FILTER and the name of the pipe under PIPE. The completed section could then look like this:
EMAIL = [email protected] MTU = 65000 SERVER = mail.gnu.org:25 FILTER = "X−mailer: GNUnet" PIPE = /tmp/gnunet.smtp
Todo
set highlighting for this code block properly.
Finally, you need to add smtp to the list of TRANSPORTS in the GNUNETD section. GNUnet peers will use the E−mail address that you specified to contact your peer until the advertisement times out. Thus, if you are not sure if everything works properly or if you are not planning to be online for a long time, you may want to configure this timeout to be short, e.g. just one hour. For this, set HELLOEXPIRES to 1 in the GNUNETD section.
This should be it, but you may probably want to test it first.
How do I
test if it works?
Any transport can be subjected to some rudimentary tests
using the gnunet−transport−check tool.
The tool sends a message to the local node via the transport
and checks that a valid message is received. While this test
does not involve other peers and can not check if firewalls
or other network obstacles prohibit proper operation, this
is a great testcase for the SMTP transport since it tests
pretty much nearly all of the functionality.
gnunet−transport−check should only be used without running gnunetd at the same time. By default, gnunet−transport−check tests all transports that are specified in the configuration file. But you can specifically test SMTP by giving the option −−transport=smtp.
Note that this test always checks if a transport can receive and send. While you can configure most transports to only receive or only send messages, this test will only work if you have configured the transport to send and receive messages.
How fast is
it?
We have measured the performance of the UDP, TCP and SMTP
transport layer directly and when used from an application
using the GNUnet core. Measuring just the transport layer
gives the better view of the actual overhead of the
protocol, whereas evaluating the transport from the
application puts the overhead into perspective from a
practical point of view.
The loopback measurements of the SMTP transport were performed on three different machines spanning a range of modern SMTP configurations. We used a PIII−800 running RedHat 7.3 with the Purdue Computer Science configuration which includes filters for spam. We also used a Xenon 2 GHZ with a vanilla RedHat 8.0 sendmail configuration. Furthermore, we used qmail on a PIII−1000 running Sorcerer GNU Linux (SGL). The numbers for UDP and TCP are provided using the SGL configuration. The qmail benchmark uses qmailâs internal filtering whereas the sendmail benchmarks relies on procmail to filter and deliver the mail. We used the transport layer to send a message of b bytes (excluding transport protocol headers) directly to the local machine. This way, network latency and packet loss on the wire have no impact on the timings. n messages were sent sequentially over the transport layer, sending message i+1 after the i−th message was received. All messages were sent over the same connection and the time to establish the connection was not taken into account since this overhead is minuscule in practice â as long as a connection is used for a significant number of messages.
The benchmarks show that UDP and TCP are, as expected, both significantly faster compared with any of the SMTP services. Among the SMTP implementations, there can be significant differences depending on the SMTP configuration. Filtering with an external tool like procmail that needs to re−parse its configuration for each mail can be very expensive. Applying spam filters can also significantly impact the performance of the underlying SMTP implementation. The microbenchmark shows that SMTP can be a viable solution for initiating peer−to−peer sessions: a couple of seconds to connect to a peer are probably not even going to be noticed by users. The next benchmark measures the possible throughput for a transport. Throughput can be measured by sending multiple messages in parallel and measuring packet loss. Note that not only UDP but also the TCP transport can actually loose messages since the TCP implementation drops messages if the write to the socket would block. While the SMTP protocol never drops messages itself, it is often so slow that only a fraction of the messages can be sent and received in the given time−bounds. For this benchmark we report the message loss after allowing t time for sending m messages. If messages were not sent (or received) after an overall timeout of t, they were considered lost. The benchmark was performed using two Xeon 2 GHZ machines running RedHat 8.0 with sendmail. The machines were connected with a direct 100 MBit Ethernet connection. Figures udp1200, tcp1200 and smtp−MTUs show that the throughput for messages of size 1,200 octets is 2,343 kbps, 3,310 kbps and 6 kbps for UDP, TCP and SMTP respectively. The high per−message overhead of SMTP can be improved by increasing the MTU, for example, an MTU of 12,000 octets improves the throughput to 13 kbps as figure smtp−MTUs shows. Our research paper [Transport2014] has some more details on the benchmarking results.
Bluetooth plugin .. _Bluetooth−plugin:
Bluetooth
plugin
This page describes the new Bluetooth transport plugin for
GNUnet. The plugin is still in the testing stage so
donât expect it to work perfectly. If you have any
questions or problems just post them here or ask on the IRC
channel.
• |
What do I need to use the Bluetooth plugin transport? |
|||
• |
BluetoothHow does it work? |
|||
• |
What possible errors should I be aware of? |
|||
• |
How do I configure my peer? |
|||
• |
How can I test it? |
What do I
need to use the Bluetooth plugin transport?
If you are a GNU/Linux user and you want to use the
Bluetooth transport plugin you should install the
BlueZ development libraries (if they arenât
already installed). For instructions about how to install
the libraries you should check out the BlueZ site
(http://www.bluez.org). If you donât know if
you have the necessary libraries, donât worry, just
run the GNUnet configure script and you will be able to see
a notification at the end which will warn you if you
donât have the necessary libraries.
Todo
Change to unique title?
How does it
work2?
The Bluetooth transport plugin uses virtually the same code
as the WLAN plugin and only the helper binary is different.
The helper takes a single argument, which represents the
interface name and is specified in the configuration file.
Here are the basic steps that are followed by the helper
binary used on GNU/Linux:
• |
it verifies if the name corresponds to a Bluetooth interface name | ||
• |
it verifies if the interface is up (if it is not, it tries to bring it up) | ||
• |
it tries to enable the page and inquiry scan in order to make the device discoverable and to accept incoming connection requests The above operations require root access so you should start the transport plugin with root privileges. | ||
• |
it finds an available port number and registers a SDP service which will be used to find out on which port number is the server listening on and switch the socket in listening mode | ||
• |
it sends a HELLO message with its address | ||
• |
finally it forwards traffic from the reading sockets to the STDOUT and from the STDIN to the writing socket |
Once in a while the device will make an inquiry scan to discover the nearby devices and it will send them randomly HELLO messages for peer discovery.
What
possible errors should I be aware of?
This section is dedicated for GNU/Linux users
Well there are many ways in which things could go wrong but I will try to present some tools that you could use to debug and some scenarios.
• |
bluetoothd −n −d : use this command to enable logging in the foreground and to print the logging messages | ||
• |
hciconfig: can be used to configure the Bluetooth devices. If you run it without any arguments it will print information about the state of the interfaces. So if you receive an error that the device couldnât be brought up you should try to bring it manually and to see if it works (use hciconfig −a hciX up). If you canât and the Bluetooth address has the form 00:00:00:00:00:00 it means that there is something wrong with the D−Bus daemon or with the Bluetooth daemon. Use bluetoothd tool to see the logs | ||
• |
sdptool can be used to control and interrogate SDP servers. If you encounter problems regarding the SDP server (like the SDP server is down) you should check out if the D−Bus daemon is running correctly and to see if the Bluetooth daemon started correctly(use bluetoothd tool). Also, sometimes the SDP service could work but somehow the device couldnât register its service. Use sdptool browse [dev−address] to see if the service is registered. There should be a service with the name of the interface and GNUnet as provider. | ||
• |
hcitool : another useful tool which can be used to configure the device and to send some particular commands to it. | ||
• |
hcidump : could be used for low level debugging |
Todo
Fix name/referencing now that weâre using Sphinx.
How do I
configure my peer2?
On GNU/Linux, you just have to be sure that the interface
name corresponds to the one that you want to use. Use the
hciconfig tool to check that. By default it is set to
hci0 but you can change it.
A basic configuration looks like this:
[transport−bluetooth] # Name of the interface (typically hciX) INTERFACE = hci0 # Real hardware, no testing TESTMODE = 0 TESTING_IGNORE_KEYS = ACCEPT_FROM;
In order to use the Bluetooth transport plugin when the transport service is started, you must add the plugin name to the default transport service plugins list. For example:
[transport] ... PLUGINS = dns bluetooth ...
If you want to use only the Bluetooth plugin set PLUGINS = bluetooth
On Windows, you cannot specify which device to use. The only thing that you should do is to add bluetooth on the plugins list of the transport service.
How can I
test it?
If you have two Bluetooth devices on the same machine and
you are using GNU/Linux you must:
• |
create two different file configuration (one which will use the first interface (hci0) and the other which will use the second interface (hci1)). Letâs name them peer1.conf and peer2.conf. | ||
• |
run gnunet−peerinfo −c peerX.conf −s in order to generate the peers private keys. The X must be replace with 1 or 2. | ||
• |
run gnunet−arm −c peerX.conf −s −i=transport in order to start the transport service. (Make sure that you have "bluetooth" on the transport plugins list if the Bluetooth transport service doesnât start.) | ||
• |
run gnunet−peerinfo −c peer1.conf −s to get the first peerâs ID. If you already know your peer ID (you saved it from the first command), this can be skipped. | ||
• |
run gnunet−transport −c peer2.conf −p=PEER1_ID −s to start sending data for benchmarking to the other peer. |
This scenario will try to connect the second peer to the first one and then start sending data for benchmarking.
If you have two different machines and your configuration files are good you can use the same scenario presented on the beginning of this section.
Another way to test the plugin functionality is to create your own application which will use the GNUnet framework with the Bluetooth transport service.
The
implementation of the Bluetooth transport plugin
This page describes the implementation of the Bluetooth
transport plugin.
First I want to remind you that the Bluetooth transport plugin uses virtually the same code as the WLAN plugin and only the helper binary is different. Also the scope of the helper binary from the Bluetooth transport plugin is the same as the one used for the WLAN transport plugin: it accesses the interface and then it forwards traffic in both directions between the Bluetooth interface and stdin/stdout of the process involved.
The Bluetooth plugin transport could be used both on GNU/Linux and Windows platforms.
• |
Linux functionality |
|||
• |
Pending Features |
Linux
functionality
In order to implement the plugin functionality on GNU/Linux
I used the BlueZ stack. For the communication with the other
devices I used the RFCOMM protocol. Also I used the HCI
protocol to gain some control over the device. The helper
binary takes a single argument (the name of the Bluetooth
interface) and is separated in two stages:
Todo
âTHE INITIALIZATIONâ should be in bigger letters
or stand out, not starting a new section?
THE INITIALIZATION
• |
first, it checks if we have root privileges (Remember that we need to have root privileges in order to be able to bring the interface up if it is down or to change its state.). | ||
• |
second, it verifies if the interface with the given name exists. |
If the interface with that name exists and it is a Bluetooth interface:
• |
it creates a RFCOMM socket which will be used for listening and call the open_device method |
On the open_device method:
• |
creates a HCI socket used to send control events to the device | ||
• |
searches for the device ID using the interface name | ||
• |
saves the device MAC address | ||
• |
checks if the interface is down and tries to bring it UP | ||
• |
checks if the interface is in discoverable mode and tries to make it discoverable | ||
• |
closes the HCI socket and binds the RFCOMM one | ||
• |
switches the RFCOMM socket in listening mode | ||
• |
registers the SDP service (the service will be used by the other devices to get the port on which this device is listening on) | ||
• |
drops the root privileges
If the interface is not a Bluetooth interface the helper exits with a suitable error
THE LOOP
The helper binary uses a list where it saves all the
connected neighbour devices (neighbours.devices) and
two buffers (write_pout and write_std). The
first message which is send is a control message with the
deviceâs MAC address in order to announce the peer
presence to the neighbours. Here are a short description of
what happens in the main loop:
• |
Every time when it receives something from the STDIN it processes the data and saves the message in the first buffer (write_pout). When it has something in the buffer, it gets the destination address from the buffer, searches the destination address in the list (if there is no connection with that device, it creates a new one and saves it to the list) and sends the message. | ||
• |
Every time when it receives something on the listening socket it accepts the connection and saves the socket on a list with the reading sockets. | ||
• |
Every time when it receives something from a reading socket it parses the message, verifies the CRC and saves it in the write_std buffer in order to be sent later to the STDOUT. |
So in the main loop we use the select function to wait until one of the file descriptor saved in one of the two file descriptors sets used is ready to use. The first set (rfds) represents the reading set and it could contain the list with the reading sockets, the STDIN file descriptor or the listening socket. The second set (wfds) is the writing set and it could contain the sending socket or the STDOUT file descriptor. After the select function returns, we check which file descriptor is ready to use and we do what is supposed to do on that kind of event. For example: if it is the listening socket then we accept a new connection and save the socket in the reading list; if it is the STDOUT file descriptor, then we write to STDOUT the message from the write_std buffer.
To find out on which port a device is listening on we connect to the local SDP server and search the registered service for that device.
You should be aware of the fact that if the device fails to connect to another one when trying to send a message it will attempt one more time. If it fails again, then it skips the message. Also you should know that the transport Bluetooth plugin has support forbroadcast messages.
Details
about the broadcast implementation
First I want to point out that the broadcast functionality
for the CONTROL messages is not implemented in a
conventional way. Since the inquiry scan time is too big and
it will take some time to send a message to all the
discoverable devices I decided to tackle the problem in a
different way. Here is how I did it:
• |
If it is the first time when I have to broadcast a message I make an inquiry scan and save all the devicesâ addresses to a vector. | ||
• |
After the inquiry scan ends I take the first address from the list and I try to connect to it. If it fails, I try to connect to the next one. If it succeeds, I save the socket to a list and send the message to the device. | ||
• |
When I have to broadcast another message, first I search on the list for a new device which Iâm not connected to. If there is no new device on the list I go to the beginning of the list and send the message to the old devices. After 5 cycles I make a new inquiry scan to check out if there are new discoverable devices and save them to the list. If there are no new discoverable devices I reset the cycling counter and go again through the old list and send messages to the devices saved in it. |
Therefore:
• |
every time when I have a broadcast message I look up on the list for a new device and send the message to it | ||
• |
if I reached the end of the list for 5 times and Iâm connected to all the devices from the list I make a new inquiry scan. The number of the listâs cycles after an inquiry scan could be increased by redefining the MAX_LOOPS variable | ||
• |
when there are no new devices I send messages to the old ones. |
Doing so, the broadcast control messages will reach the devices but with delay.
NOTICE: When I have to send a message to a certain device first I check on the broadcast list to see if we are connected to that device. If not we try to connect to it and in case of success we save the address and the socket on the list. If we are already connected to that device we simply use the socket.
Pending features
• |
Implement a testcase for the helper : The testcase consists of a program which emulates the plugin and uses the helper. It will simulate connections, disconnections and data transfers. |
If you have a new idea about a feature of the plugin or suggestions about how I could improve the implementation you are welcome to comment or to contact me.
WLAN
plugin
This section documents how the wlan transport plugin works.
Parts which are not implemented yet or could be better
implemented are described at the end.
[Transport2014]
https://bib.gnunet.org/date.html#paper_5fshort2014
TRANSPORT−NG
â Next−generation transport management
The current GNUnet TRANSPORT architecture is rooted in the
GNUnet 0.4 design of using plugins for the actual
transmission operations and the ATS subsystem to select a
plugin and allocate bandwidth. The following key issues have
been identified with this design:
• |
Bugs in one plugin can affect the TRANSPORT service and other plugins. There is at least one open bug that affects sockets, where the origin is difficult to pinpoint due to the large code base. | ||
• |
Relevant operating system default configurations often impose a limit of 1024 file descriptors per process. Thus, one plugin may impact other pluginâs connectivity choices. | ||
• |
Plugins are required to offer bi−directional connectivity. However, firewalls (incl. NAT boxes) and physical environments sometimes only allow uni−directional connectivity, which then currently cannot be utilized at all. | ||
• |
Distance vector routing was implemented in 209 but shortly afterwards broken and due to the complexity of implementing it as a plugin and dealing with the resource allocation consequences was never useful. | ||
• |
Most existing plugins communicate completely using cleartext, exposing metad data (message size) and making it easy to fingerprint and possibly block GNUnet traffic. | ||
• |
Various NAT traversal methods are not supported. | ||
• |
The service logic is cluttered with "manipulation" support code for TESTBED to enable faking network characteristics like lossy connections or firewewalls. | ||
• |
Bandwidth allocation is done in ATS, requiring the duplication of state and resulting in much delayed allocation decisions. As a result, often available bandwidth goes unused. Users are expected to manually configure bandwidth limits, instead of TRANSPORT using congestion control to adapt automatically. | ||
• |
TRANSPORT is difficult to test and has bad test coverage. | ||
• |
HELLOs include an absolute expiration time. Nodes with unsynchronized clocks cannot connect. | ||
• |
Displaying the contents of a HELLO requires the respective plugin as the plugin−specific data is encoded in binary. This also complicates logging. |
Design goals
of TNG
In order to address the above issues, we want to:
• |
Move plugins into separate processes which we shall call communicators. Communicators connect as clients to the transport service. | ||
• |
TRANSPORT should be able to utilize any number of communcators to the same peer at the same time. | ||
• |
TRANSPORT should be responsible for fragmentation, retransmission, flow− and congestion−control. Users should no longer have to configure bandwidth limits: TRANSPORT should detect what is available and use it. | ||
• |
Commnunicators should be allowed to be uni−directional and unreliable. TRANSPORT shall create bi−directional channels from this whenever possible. | ||
• |
DV should no longer be a plugin, but part of TRANSPORT. | ||
• |
TRANSPORT should provide communicators help communicating, for example in the case of uni−directional communicators or the need for out−of−band signalling for NAT traversal. We call this functionality backchannels. | ||
• |
Transport manipulation should be signalled to CORE on a per−message basis instead of an approximate bandwidth. | ||
• |
CORE should signal performance requirements (reliability, latency, etc.) on a per−message basis to TRANSPORT. If possible, TRANSPORT should consider those options when scheduling messages for transmission. | ||
• |
HELLOs should be in a humman−readable format with monotonic time expirations. |
The new architecture is planned as follows: [image]
TRANSPORTâs main objective is to establish bi−directional virtual links using a variety of possibly uni−directional communicators. Links undergo the following steps:
1. |
Communicator informs TRANSPORT A that a queue (direct neighbour) is available, or equivalently TRANSPORT A discovers a (DV) path to a target B. | ||
2. |
TRANSPORT A sends a challenge to the target peer, trying to confirm that the peer can receive. FIXME: This is not implemented properly for DV. Here we should really take a validated DVH and send a challenge exactly down that path! | ||
3. |
The other TRANSPORT, TRANSPORT B, receives the challenge, and sends back a response, possibly using a dierent path. If TRANSPORT B does not yet have a virtual link to A, it must try to establish a virtual link. | ||
4. |
Upon receiving the response, TRANSPORT A creates the virtual link. If the response included a challenge, TRANSPORT A must respond to this challenge as well, eectively re−creating the TCP 3−way handshake (just with longer challenge values). |
HELLO−NG
HELLOs change in three ways. First of all, communicators
encode the respective addresses in a human−readable
URL−like string. This way, we do no longer require the
communicator to print the contents of a HELLO. Second,
HELLOs no longer contain an expiration time, only a creation
time. The receiver must only compare the respective absolute
values. So given a HELLO from the same sender with a larger
creation time, then the old one is no longer valid. This
also obsoletes the need for the gnunet−hello binary to
set HELLO expiration times to never. Third, a peer no longer
generates one big HELLO that always contains all of the
addresses. Instead, each address is signed individually and
shared only over the address scopes where it makes sense to
share the address. In particular, care should be taken to
not share MACs across the Internet and confine their use to
the LAN. As each address is signed separately, having
multiple addresses valid at the same time (given the new
creation time expiration logic) requires that those
addresses must have exactly the same creation time. Whenever
that monotonic time is increased, all addresses must be
re−signed and re−distributed.
Priorities
and preferences
In the new design, TRANSPORT adopts a feature (which was
previously already available in CORE) of the MQ API to allow
applications to specify priorities and preferences per
message (or rather, per MQ envelope). The (updated) MQ API
allows applications to specify one of four priority levels
as well as desired preferences for transmission by setting
options on an envelope. These preferences currently are:
• |
GNUNET_MQ_PREF_UNRELIABLE: Disables TRANSPORT waiting for ACKS on unreliable channels like UDP. Now it is fire and forget. These messages then cannot be used for RTT estimates either. | ||
• |
GNUNET_MQ_PREF_LOW_LATENCY: Directs TRANSPORT to select the lowest−latency transmission choices possible. | ||
• |
GNUNET_MQ_PREF_CORK_ALLOWED: Allows TRANSPORT to delay transmission to group the message with other messages into a larger batch to reduce the number of packets sent. | ||
• |
GNUNET_MQ_PREF_GOODPUT: Directs TRANSPORT to select the highest goodput channel available. | ||
• |
GNUNET_MQ_PREF_OUT_OF_ORDER: Allows TRANSPORT to reorder the messages as it sees fit, otherwise TRANSPORT should attempt to preserve transmission order. |
Each MQ envelope is always able to store those options (and the priority), and in the future this uniform API will be used by TRANSPORT, CORE, CADET and possibly other subsystems that send messages (like LAKE). When CORE sets preferences and priorities, it is supposed to respect the preferences and priorities it is given from higher layers. Similarly, CADET also simply passes on the preferences and priorities of the layer above CADET. When a layer combines multiple smaller messages into one larger transmission, the GNUNET_MQ_env_combine_options() should be used to calculate options for the combined message. We note that the exact semantics of the options may differ by layer. For example, CADET will always strictly implement reliable and in−order delivery of messages, while the same options are only advisory for TRANSPORT and CORE: they should try (using ACKs on unreliable communicators, not changing the message order themselves), but if messages are lost anyway (e.g. because a TCP is dropped in the middle), or if messages are reordered (e.g. because they took different paths over the network and arrived in a different order) TRANSPORT and CORE do not have to correct this. Whether a preference is strict or loose is thus dened by the respective layer.
Communicators
The API for communicators is defined in
gnunet_transport_communication_service.h. Each
communicator must specify its (global) communication
characteristics, which for now only say whether the
communication is reliable (e.g. TCP, HTTPS) or unreliable
(e.g. UDP, WLAN). Each communicator must specify a unique
address prex, or NULL if the communicator cannot establish
outgoing connections (for example because it is only acting
as a TCP server). A communicator must tell TRANSPORT which
addresses it is reachable under. Addresses may be added or
removed at any time. A communicator may have zero addresses
(transmission only). Addresses do not have to match the
address prefix.
TRANSPORT may ask a communicator to try to connect to another address. TRANSPORT will only ask for connections where the address matches the communicatorâs address prefix that was provided when the connection was established. Communicators should then attempt to establish a connection. No response is provided to TRANSPORT service on failure. The TRANSPORT service has to ask the communicator explicitly to retry.
If a communicator succeeds in establishing an outgoing connection for transmission, or if a communicator receives an incoming bi−directional connection, the communicator must inform the TRANSPORT service that a message queue (MQ) for transmission is now available. For that MQ, the communicator must provide the peer identity claimed by the other end, a human−readable address (for debugging) and a maximum transfer unit (MTU). A MTU of zero means sending is not supported, SIZE_MAX should be used for no MTU. The communicator should also tell TRANSPORT what network type is used for the queue. The communicator may tell TRANSPORT anytime that the queue was deleted and is no longer available.
The communicator API also provides for flow control. First, communicators exhibit back−pressure on TRANSPORT: the number of messages TRANSPORT may add to a queue for transmission will be limited. So by not draining the transmission queue, back−pressure is provided to TRANSPORT. In the other direction, communicators may allow TRANSPORT to give back−pressure towards the communicator by providing a non−NULL GNUNET_TRANSPORT_MessageCompletedCallback argument to the GNUNET_TRANSPORT_communicator_receive function. In this case, TRANSPORT will only invoke this function once it has processed the message and is ready to receive more. Communicators should then limit how much traffic they receive based on this backpressure. Note that communicators do not have to provide a GNUNET_TRANSPORT_MessageCompletedCallback; for example, UDP cannot support back−pressure due to the nature of the UDP protocol. In this case, TRANSPORT will implement its own TRANSPORT−to−TRANSPORT flow control to reduce the senderâs data rate to acceptable levels.
TRANSPORT may notify a communicator about backchannel messages TRANSPORT received from other peers for this communicator. Similarly, communicators can ask TRANSPORT to try to send a backchannel message to other communicators of other peers. The semantics of the backchannel message are up to the communicators which use them. TRANSPORT may fail transmitting backchannel messages, and TRANSPORT will not attempt to retransmit them.
HOSTLIST
â HELLO bootstrapping and gossip
Peers in the GNUnet overlay network need address information
so that they can connect with other peers. GNUnet uses so
called HELLO messages to store and exchange peer addresses.
GNUnet provides several methods for peers to obtain this
information:
• |
out−of−band exchange of HELLO messages (manually, using for example gnunet−peerinfo) | ||
• |
HELLO messages shipped with GNUnet (automatic with distribution) | ||
• |
UDP neighbor discovery in LAN (IPv4 broadcast, IPv6 multicast) | ||
• |
topology gossiping (learning from other peers we already connected to), and | ||
• |
the HOSTLIST daemon covered in this section, which is particularly relevant for bootstrapping new peers. |
New peers have no existing connections (and thus cannot learn from gossip among peers), may not have other peers in their LAN and might be started with an outdated set of HELLO messages from the distribution. In this case, getting new peers to connect to the network requires either manual effort or the use of a HOSTLIST to obtain HELLOs.
HELLOs
The basic information peers require to connect to other
peers are contained in so called HELLO messages you can
think of as a business card. Besides the identity of the
peer (based on the cryptographic public key) a HELLO message
may contain address information that specifies ways to
contact a peer. By obtaining HELLO messages, a peer can
learn how to contact other peers.
Overview for
the HOSTLIST subsystem
The HOSTLIST subsystem provides a way to distribute and
obtain contact information to connect to other peers using a
simple HTTP GET request. Its implementation is split in
three parts, the main file for the daemon itself
(gnunet−daemon−hostlist.c), the HTTP
client used to download peer information
(hostlist−client.c) and the server component
used to provide this information to other peers
(hostlist−server.c). The server is basically a
small HTTP web server (based on GNU libmicrohttpd) which
provides a list of HELLOs known to the local peer for
download. The client component is basically a HTTP client
(based on libcurl) which can download hostlists from one or
more websites. The hostlist format is a binary blob
containing a sequence of HELLO messages. Note that any HTTP
server can theoretically serve a hostlist, the
built−in hostlist server makes it simply convenient to
offer this service.
Features
The HOSTLIST daemon can:
• |
provide HELLO messages with validated addresses obtained from PEERINFO to download for other peers | ||
• |
download HELLO messages and forward these message to the TRANSPORT subsystem for validation | ||
• |
advertises the URL of this peerâs hostlist address to other peers via gossip | ||
• |
automatically learn about hostlist servers from the gossip of other peers |
HOSTLIST
− Limitations
The HOSTLIST daemon does not:
• |
verify the cryptographic information in the HELLO messages | ||
• |
verify the address information in the HELLO messages |
Interacting
with the HOSTLIST daemon
The HOSTLIST subsystem is currently implemented as a daemon,
so there is no need for the user to interact with it and
therefore there is no command line tool and no API to
communicate with the daemon. In the future, we can envision
changing this to allow users to manually trigger the
download of a hostlist.
Since there is no command line interface to interact with HOSTLIST, the only way to interact with the hostlist is to use STATISTICS to obtain or modify information about the status of HOSTLIST:
$ gnunet−statistics −s hostlist
In particular, HOSTLIST includes a persistent value in statistics that specifies when the hostlist server might be queried next. As this value is exponentially increasing during runtime, developers may want to reset or manually adjust it. Note that HOSTLIST (but not STATISTICS) needs to be shutdown if changes to this value are to have any effect on the daemon (as HOSTLIST does not monitor STATISTICS for changes to the download frequency).
Hostlist
security address validation
Since information obtained from other parties cannot be
trusted without validation, we have to distinguish between
validated and not validated addresses. Before
using (and so trusting) information from other parties, this
information has to be double−checked (validated).
Address validation is not done by HOSTLIST but by the
TRANSPORT service.
The HOSTLIST component is functionally located between the PEERINFO and the TRANSPORT subsystem. When acting as a server, the daemon obtains valid (validated) peer information (HELLO messages) from the PEERINFO service and provides it to other peers. When acting as a client, it contacts the HOSTLIST servers specified in the configuration, downloads the (unvalidated) list of HELLO messages and forwards these information to the TRANSPORT server to validate the addresses.
The HOSTLIST
daemon
The hostlist daemon is the main component of the HOSTLIST
subsystem. It is started by the ARM service and (if
configured) starts the HOSTLIST client and server
components.
GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT If the daemon provides a hostlist itself it can advertise itâs own hostlist to other peers. To do so it sends a GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT message to other peers when they connect to this peer on the CORE level. This hostlist advertisement message contains the URL to access the HOSTLIST HTTP server of the sender. The daemon may also subscribe to this type of message from CORE service, and then forward these kind of message to the HOSTLIST client. The client then uses all available URLs to download peer information when necessary.
When starting, the HOSTLIST daemon first connects to the CORE subsystem and if hostlist learning is enabled, registers a CORE handler to receive this kind of messages. Next it starts (if configured) the client and server. It passes pointers to CORE connect and disconnect and receive handlers where the client and server store their functions, so the daemon can notify them about CORE events.
To clean up on shutdown, the daemon has a cleaning task, shutting down all subsystems and disconnecting from CORE.
The HOSTLIST
server
The server provides a way for other peers to obtain HELLOs.
Basically it is a small web server other peers can connect
to and download a list of HELLOs using standard HTTP; it may
also advertise the URL of the hostlist to other peers
connecting on CORE level.
The HTTP
Server
During startup, the server starts a web server listening on
the port specified with the HTTPPORT value (default 8080).
In addition it connects to the PEERINFO service to obtain
peer information. The HOSTLIST server uses the
GNUNET_PEERINFO_iterate function to request HELLO
information for all peers and adds their information to a
new hostlist if they are suitable (expired addresses and
HELLOs without addresses are both not suitable) and the
maximum size for a hostlist is not exceeded
(MAX_BYTES_PER_HOSTLISTS = 500000). When PEERINFO finishes
(with a last NULL callback), the server destroys the
previous hostlist response available for download on the web
server and replaces it with the updated hostlist. The
hostlist format is basically a sequence of HELLO messages
(as obtained from PEERINFO) without any special
tokenization. Since each HELLO message contains a size
field, the response can easily be split into separate HELLO
messages by the client.
A HOSTLIST client connecting to the HOSTLIST server will receive the hostlist as an HTTP response and the server will terminate the connection with the result code HTTP 200 OK. The connection will be closed immediately if no hostlist is available.
Advertising
the URL
The server also advertises the URL to download the hostlist
to other peers if hostlist advertisement is enabled. When a
new peer connects and has hostlist learning enabled, the
server sends a
GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT message to
this peer using the CORE service.
HOSTLIST client .. _The−HOSTLIST−client:
The HOSTLIST
client
The client provides the functionality to download the list
of HELLOs from a set of URLs. It performs a standard HTTP
request to the URLs configured and learned from
advertisement messages received from other peers. When a
HELLO is downloaded, the HOSTLIST client forwards the HELLO
to the TRANSPORT service for validation.
The client supports two modes of operation:
• |
download of HELLOs (bootstrapping) |
|||
• |
learning of URLs |
Bootstrapping
For bootstrapping, it schedules a task to download the
hostlist from the set of known URLs. The downloads are only
performed if the number of current connections is smaller
than a minimum number of connections (at the moment 4). The
interval between downloads increases exponentially; however,
the exponential growth is limited if it becomes longer than
an hour. At that point, the frequency growth is capped at
(#number of connections * 1h).
Once the decision has been taken to download HELLOs, the daemon chooses a random URL from the list of known URLs. URLs can be configured in the configuration or be learned from advertisement messages. The client uses a HTTP client library (libcurl) to initiate the download using the libcurl multi interface. Libcurl passes the data to the callback_download function which stores the data in a buffer if space is available and the maximum size for a hostlist download is not exceeded (MAX_BYTES_PER_HOSTLISTS = 500000). When a full HELLO was downloaded, the HOSTLIST client offers this HELLO message to the TRANSPORT service for validation. When the download is finished or failed, statistical information about the quality of this URL is updated.
Learning
The client also manages hostlist advertisements from other
peers. The HOSTLIST daemon forwards
GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT messages
to the client subsystem, which extracts the URL from the
message. Next, a test of the newly obtained URL is performed
by triggering a download from the new URL. If the URL works
correctly, it is added to the list of working URLs.
The size of the list of URLs is restricted, so if an additional server is added and the list is full, the URL with the worst quality ranking (determined through successful downloads and number of HELLOs e.g.) is discarded. During shutdown the list of URLs is saved to a file for persistence and loaded on startup. URLs from the configuration file are never discarded.
Usage
To start HOSTLIST by default, it has to be added to the
DEFAULTSERVICES section for the ARM services. This is done
in the default configuration.
For more information on how to configure the HOSTLIST subsystem see the installation handbook: Configuring the hostlist to bootstrap Configuring your peer to provide a hostlist
PEERINFO
â Persistent HELLO storage
The PEERINFO subsystem is used to store verified (validated)
information about known peers in a persistent way. It
obtains these addresses for example from TRANSPORT service
which is in charge of address validation. Validation means
that the information in the HELLO message are checked by
connecting to the addresses and performing a cryptographic
handshake to authenticate the peer instance stating to be
reachable with these addresses. Peerinfo does not validate
the HELLO messages itself but only stores them and gives
them to interested clients.
As future work, we think about moving from storing just HELLO messages to providing a generic persistent per−peer information store. More and more subsystems tend to need to store per−peer information in persistent way. To not duplicate this functionality we plan to provide a PEERSTORE service providing this functionality.
PEERINFO − Features
• |
Persistent storage |
|||
• |
Client notification mechanism on update |
|||
• |
Periodic clean up for expired information |
|||
• |
Differentiation between public and friend−only HELLO |
PEERINFO − Limitations
• |
Does not perform HELLO validation |
DeveloperPeer
Information
The PEERINFO subsystem stores these information in the form
of HELLO messages you can think of as business cards. These
HELLO messages contain the public key of a peer and the
addresses a peer can be reached under. The addresses include
an expiration date describing how long they are valid. This
information is updated regularly by the TRANSPORT service by
revalidating the address. If an address is expired and not
renewed, it can be removed from the HELLO message.
Some peer do not want to have their HELLO messages distributed to other peers, especially when GNUnetâs friend−to−friend modus is enabled. To prevent this undesired distribution. PEERINFO distinguishes between public and friend−only HELLO messages. Public HELLO messages can be freely distributed to other (possibly unknown) peers (for example using the hostlist, gossiping, broadcasting), whereas friend−only HELLO messages may not be distributed to other peers. Friend−only HELLO messages have an additional flag friend_only set internally. For public HELLO message this flag is not set. PEERINFO does and cannot not check if a client is allowed to obtain a specific HELLO type.
The HELLO messages can be managed using the GNUnet HELLO library. Other GNUnet systems can obtain these information from PEERINFO and use it for their purposes. Clients are for example the HOSTLIST component providing these information to other peers in form of a hostlist or the TRANSPORT subsystem using these information to maintain connections to other peers.
Startup
During startup the PEERINFO services loads persistent HELLOs
from disk. First PEERINFO parses the directory configured in
the HOSTS value of the PEERINFO configuration section
to store PEERINFO information. For all files found in this
directory valid HELLO messages are extracted. In addition it
loads HELLO messages shipped with the GNUnet distribution.
These HELLOs are used to simplify network bootstrapping by
providing valid peer information with the distribution. The
use of these HELLOs can be prevented by setting the
USE_INCLUDED_HELLOS in the PEERINFO
configuration section to NO. Files containing invalid
information are removed.
Managing
Information
The PEERINFO services stores information about known PEERS
and a single HELLO message for every peer. A peer does not
need to have a HELLO if no information are available. HELLO
information from different sources, for example a HELLO
obtained from a remote HOSTLIST and a second HELLO stored on
disk, are combined and merged into one single HELLO message
per peer which will be given to clients. During this merge
process the HELLO is immediately written to disk to ensure
persistence.
PEERINFO in addition periodically scans the directory where information are stored for empty HELLO messages with expired TRANSPORT addresses. This periodic task scans all files in the directory and recreates the HELLO messages it finds. Expired TRANSPORT addresses are removed from the HELLO and if the HELLO does not contain any valid addresses, it is discarded and removed from the disk.
Obtaining
Information
When a client requests information from PEERINFO, PEERINFO
performs a lookup for the respective peer or all peers if
desired and transmits this information to the client. The
client can specify if friend−only HELLOs have to be
included or not and PEERINFO filters the respective HELLO
messages before transmitting information.
To notify clients about changes to PEERINFO information, PEERINFO maintains a list of clients interested in this notifications. Such a notification occurs if a HELLO for a peer was updated (due to a merge for example) or a new peer was added.
The PEERINFO
Client−Service Protocol
To connect and disconnect to and from the PEERINFO Service
PEERINFO utilizes the util client/server infrastructure, so
no special messages types are used here.
To add information for a peer, the plain HELLO message is transmitted to the service without any wrapping. All pieces of information required are stored within the HELLO message. The PEERINFO service provides a message handler accepting and processing these HELLO messages.
When obtaining PEERINFO information using the iterate functionality specific messages are used. To obtain information for all peers, a struct ListAllPeersMessage with message type GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL and a flag include_friend_only to indicate if friend−only HELLO messages should be included are transmitted. If information for a specific peer is required a struct ListAllPeersMessage with GNUNET_MESSAGE_TYPE_PEERINFO_GET containing the peer identity is used.
For both variants the PEERINFO service replies for each HELLO message it wants to transmit with a struct ListAllPeersMessage with type GNUNET_MESSAGE_TYPE_PEERINFO_INFO containing the plain HELLO. The final message is struct GNUNET_MessageHeader with type GNUNET_MESSAGE_TYPE_PEERINFO_INFO. If the client receives this message, it can proceed with the next request if any is pending.
libgnunetpeerinfo
The PEERINFO API consists mainly of three different
functionalities:
• |
maintaining a connection to the service |
|||
• |
adding new information to the PEERINFO service |
|||
• |
retrieving information from the PEERINFO service |
Connecting
to the PEERINFO Service
To connect to the PEERINFO service the function
GNUNET_PEERINFO_connect is used, taking a
configuration handle as an argument, and to disconnect from
PEERINFO the function GNUNET_PEERINFO_disconnect,
taking the PEERINFO handle returned from the connect
function has to be called.
Adding
Information to the PEERINFO Service
GNUNET_PEERINFO_add_peer adds a new peer to the PEERINFO
subsystem storage. This function takes the PEERINFO handle
as an argument, the HELLO message to store and a
continuation with a closure to be called with the result of
the operation. The GNUNET_PEERINFO_add_peer returns a
handle to this operation allowing to cancel the operation
with the respective cancel function
GNUNET_PEERINFO_add_peer_cancel. To retrieve
information from PEERINFO you can iterate over all
information stored with PEERINFO or you can tell PEERINFO to
notify if new peer information are available.
Obtaining
Information from the PEERINFO Service
To iterate over information in PEERINFO you use
GNUNET_PEERINFO_iterate. This function expects the
PEERINFO handle, a flag if HELLO messages intended for
friend only mode should be included, a timeout how long the
operation should take and a callback with a callback closure
to be called for the results. If you want to obtain
information for a specific peer, you can specify the peer
identity, if this identity is NULL, information for all
peers are returned. The function returns a handle to allow
to cancel the operation using
GNUNET_PEERINFO_iterate_cancel.
To get notified when peer information changes, you can use GNUNET_PEERINFO_notify. This function expects a configuration handle and a flag if friend−only HELLO messages should be included. The PEERINFO service will notify you about every change and the callback function will be called to notify you about changes. The function returns a handle to cancel notifications with GNUNET_PEERINFO_notify_cancel.
PEERSTORE
â Extensible local persistent data storage
GNUnetâs PEERSTORE subsystem offers persistent
per−peer storage for other GNUnet subsystems. GNUnet
subsystems can use PEERSTORE to persistently store and
retrieve arbitrary data. Each data record stored with
PEERSTORE contains the following fields:
• |
subsystem: Name of the subsystem responsible for the record. | ||
• |
peerid: Identity of the peer this record is related to. | ||
• |
key: a key string identifying the record. | ||
• |
value: binary record value. | ||
• |
expiry: record expiry date. |
Functionality
Subsystems can store any type of value under a (subsystem,
peerid, key) combination. A "replace" flag set
during store operations forces the PEERSTORE to replace any
old values stored under the same (subsystem, peerid, key)
combination with the new value. Additionally, an expiry date
is set after which the record is *possibly* deleted by
PEERSTORE.
Subsystems can iterate over all values stored under any of the following combination of fields:
• |
(subsystem) |
|||
• |
(subsystem, peerid) |
|||
• |
(subsystem, key) |
|||
• |
(subsystem, peerid, key) |
Subsystems can also request to be notified about any new values stored under a (subsystem, peerid, key) combination by sending a "watch" request to PEERSTORE.
Architecture
PEERSTORE implements the following components:
• |
PEERSTORE service: Handles store, iterate and watch operations. | ||
• |
PEERSTORE API: API to be used by other subsystems to communicate and issue commands to the PEERSTORE service. | ||
• |
PEERSTORE plugins: Handles the persistent storage. At the moment, only an "sqlite" plugin is implemented. |
libgnunetpeerstore
libgnunetpeerstore is the library containing the PEERSTORE
API. Subsystems wishing to communicate with the PEERSTORE
service use this API to open a connection to PEERSTORE. This
is done by calling GNUNET_PEERSTORE_connect which
returns a handle to the newly created connection. This
handle has to be used with any further calls to the API.
To store a new record, the function GNUNET_PEERSTORE_store is to be used which requires the record fields and a continuation function that will be called by the API after the STORE request is sent to the PEERSTORE service. Note that calling the continuation function does not mean that the record is successfully stored, only that the STORE request has been successfully sent to the PEERSTORE service. GNUNET_PEERSTORE_store_cancel can be called to cancel the STORE request only before the continuation function has been called.
To iterate over stored records, the function GNUNET_PEERSTORE_iterate is to be used. peerid and key can be set to NULL. An iterator callback function will be called with each matching record found and a NULL record at the end to signal the end of result set. GNUNET_PEERSTORE_iterate_cancel can be used to cancel the ITERATE request before the iterator callback is called with a NULL record.
To be notified with new values stored under a (subsystem, peerid, key) combination, the function GNUNET_PEERSTORE_watch is to be used. This will register the watcher with the PEERSTORE service, any new records matching the given combination will trigger the callback function passed to GNUNET_PEERSTORE_watch. This continues until GNUNET_PEERSTORE_watch_cancel is called or the connection to the service is destroyed.
After the connection is no longer needed, the function GNUNET_PEERSTORE_disconnect can be called to disconnect from the PEERSTORE service. Any pending ITERATE or WATCH requests will be destroyed. If the sync_first flag is set to GNUNET_YES, the API will delay the disconnection until all pending STORE requests are sent to the PEERSTORE service, otherwise, the pending STORE requests will be destroyed as well.
CORE â
GNUnet link layer
The CORE subsystem in GNUnet is responsible for securing
link−layer communications between nodes in the GNUnet
overlay network. CORE builds on the TRANSPORT subsystem
which provides for the actual, insecure, unreliable
link−layer communication (for example, via UDP or
WLAN), and then adds fundamental security to the
connections:
• |
confidentiality with so−called perfect forward secrecy; we use ECDHE (Elliptic−curve DiffieâHellman) powered by Curve25519 (Curve25519) for the key exchange and then use symmetric encryption, encrypting with both AES−256 (AES−256) and Twofish (Twofish) | ||
• |
authentication is achieved by signing the ephemeral keys using Ed25519 (Ed25519), a deterministic variant of ECDSA (ECDSA) | ||
• |
integrity protection (using SHA−512 (SHA−512) to do encrypt−then−MAC (encrypt−then−MAC)) | ||
• |
Replay (replay) protection (using nonces, timestamps, challenge−response, message counters and ephemeral keys) | ||
• |
liveness (keep−alive messages, timeout) |
Limitations
CORE does not perform routing; using CORE it is only
possible to communicate with peers that happen to already be
"directly" connected with each other. CORE also
does not have an API to allow applications to establish such
"direct" connections â for this,
applications can ask TRANSPORT, but TRANSPORT might not be
able to establish a "direct" connection. The
TOPOLOGY subsystem is responsible for trying to keep a few
"direct" connections open at all times.
Applications that need to talk to particular peers should
use the CADET subsystem, as it can establish arbitrary
"indirect" connections.
Because CORE does not perform routing, CORE must only be used directly by applications that either perform their own routing logic (such as anonymous file−sharing) or that do not require routing, for example because they are based on flooding the network. CORE communication is unreliable and delivery is possibly out−of−order. Applications that require reliable communication should use the CADET service. Each application can only queue one message per target peer with the CORE service at any time; messages cannot be larger than approximately 63 kilobytes. If messages are small, CORE may group multiple messages (possibly from different applications) prior to encryption. If permitted by the application (using the cork option), CORE may delay transmissions to facilitate grouping of multiple small messages. If cork is not enabled, CORE will transmit the message as soon as TRANSPORT allows it (TRANSPORT is responsible for limiting bandwidth and congestion control). CORE does not allow flow control; applications are expected to process messages at line−speed. If flow control is needed, applications should use the CADET service.
When is a
peer "connected"?
In addition to the security features mentioned above, CORE
also provides one additional key feature to applications
using it, and that is a limited form of
protocol−compatibility checking. CORE distinguishes
between TRANSPORT−level connections (which enable
communication with other peers) and application−level
connections. Applications using the CORE API will
(typically) learn about application−level connections
from CORE, and not about TRANSPORT−level connections.
When a typical application uses CORE, it will specify a set
of message types (from gnunet_protocols.h) that it
understands. CORE will then notify the application about
connections it has with other peers if and only if those
applications registered an intersecting set of message types
with their CORE service. Thus, it is quite possible that
CORE only exposes a subset of the established direct
connections to a particular application â and
different applications running above CORE might see
different sets of connections at the same time.
A special case are applications that do not register a handler for any message type. CORE assumes that these applications merely want to monitor connections (or "all" messages via other callbacks) and will notify those applications about all connections. This is used, for example, by the gnunet−core command−line tool to display the active connections. Note that it is also possible that the TRANSPORT service has more active connections than the CORE service, as the CORE service first has to perform a key exchange with connecting peers before exchanging information about supported message types and notifying applications about the new connection.
libgnunetcore
The CORE API (defined in gnunet_core_service.h) is
the basic messaging API used by P2P applications built using
GNUnet. It provides applications the ability to send and
receive encrypted messages to the peerâs
"directly" connected neighbours.
As CORE connections are generally "direct" connections, applications must not assume that they can connect to arbitrary peers this way, as "direct" connections may not always be possible. Applications using CORE are notified about which peers are connected. Creating new "direct" connections must be done using the TRANSPORT API.
The CORE API provides unreliable, out−of−order delivery. While the implementation tries to ensure timely, in−order delivery, both message losses and reordering are not detected and must be tolerated by the application. Most important, the core will NOT perform retransmission if messages could not be delivered.
Note that CORE allows applications to queue one message per connected peer. The rate at which each connection operates is influenced by the preferences expressed by local application as well as restrictions imposed by the other peer. Local applications can express their preferences for particular connections using the "performance" API of the ATS service.
Applications that require more sophisticated transmission capabilities such as TCP−like behavior, or if you intend to send messages to arbitrary remote peers, should use the CADET API.
The typical use of the CORE API is to connect to the CORE service using GNUNET_CORE_connect, process events from the CORE service (such as peers connecting, peers disconnecting and incoming messages) and send messages to connected peers using GNUNET_CORE_notify_transmit_ready. Note that applications must cancel pending transmission requests if they receive a disconnect event for a peer that had a transmission pending; furthermore, queuing more than one transmission request per peer per application using the service is not permitted.
The CORE API also allows applications to monitor all communications of the peer prior to encryption (for outgoing messages) or after decryption (for incoming messages). This can be useful for debugging, diagnostics or to establish the presence of cover traffic (for anonymity). As monitoring applications are often not interested in the payload, the monitoring callbacks can be configured to only provide the message headers (including the message type and size) instead of copying the full data stream to the monitoring client.
The init callback of the GNUNET_CORE_connect function is called with the hash of the public key of the peer. This public key is used to identify the peer globally in the GNUnet network. Applications are encouraged to check that the provided hash matches the hash that they are using (as theoretically the application may be using a different configuration file with a different private key, which would result in hard to find bugs).
As with most service APIs, the CORE API isolates applications from crashes of the CORE service. If the CORE service crashes, the application will see disconnect events for all existing connections. Once the connections are re−established, the applications will be receive matching connect events.
core client−service protocol .. _The−CORE−Client_002dService−Protocol:
The CORE
Client−Service Protocol
This section describes the protocol between an application
using the CORE service (the client) and the CORE service
process itself.
Setup2
When a client connects to the CORE service, it first sends a
InitMessage which specifies options for the
connection and a set of message type values which are
supported by the application. The options bitmask specifies
which events the client would like to be notified about. The
options include:
GNUNET_CORE_OPTION_NOTHING
No notifications
GNUNET_CORE_OPTION_STATUS_CHANGE
Peers connecting and disconnecting
GNUNET_CORE_OPTION_FULL_INBOUND
All inbound messages (after decryption) with full payload
GNUNET_CORE_OPTION_HDR_INBOUND
Just the MessageHeader of all inbound messages
GNUNET_CORE_OPTION_FULL_OUTBOUND
All outbound messages (prior to encryption) with full payload
GNUNET_CORE_OPTION_HDR_OUTBOUND
Just the MessageHeader of all outbound messages
Typical applications will only monitor for connection status changes.
The CORE service responds to the InitMessage with an InitReplyMessage which contains the peerâs identity. Afterwards, both CORE and the client can send messages.
Notifications
The CORE will send ConnectNotifyMessages and
DisconnectNotifyMessages whenever peers connect or
disconnect from the CORE (assuming their type maps overlap
with the message types registered by the client). When the
CORE receives a message that matches the set of message
types specified during the InitMessage (or if
monitoring is enabled in for inbound messages in the
options), it sends a NotifyTrafficMessage with the
peer identity of the sender and the decrypted payload. The
same message format (except with
GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND for the
message type) is used to notify clients monitoring outbound
messages; here, the peer identity given is that of the
receiver.
Sending
When a client wants to transmit a message, it first requests
a transmission slot by sending a SendMessageRequest
which specifies the priority, deadline and size of the
message. Note that these values may be ignored by CORE. When
CORE is ready for the message, it answers with a
SendMessageReady response. The client can then
transmit the payload with a SendMessage message. Note
that the actual message size in the SendMessage is
allowed to be smaller than the size in the original request.
A client may at any time send a fresh
SendMessageRequest, which then superceeds the
previous SendMessageRequest, which is then no longer
valid. The client can tell which SendMessageRequest
the CORE serviceâs SendMessageReady message is
for as all of these messages contain a "unique"
request ID (based on a counter incremented by the client for
each request).
CORE Peer−to−Peer Protocol .. _The−CORE−Peer_002dto_002dPeer−Protocol:
The CORE
Peer−to−Peer Protocol
EphemeralKeyMessage creation ..
_Creating−the−EphemeralKeyMessage:
Creating the
EphemeralKeyMessage
When the CORE service starts, each peer creates a fresh
ephemeral (ECC) public−private key pair and signs the
corresponding EphemeralKeyMessage with its
long−term key (which we usually call the peerâs
identity; the hash of the public long term key is what
results in a struct GNUNET_PeerIdentity in all GNUnet
APIs. The ephemeral key is ONLY used for an ECDHE
(Elliptic−curve DiffieâHellman) exchange
by the CORE service to establish symmetric session keys. A
peer will use the same EphemeralKeyMessage for all
peers for REKEY_FREQUENCY, which is usually 12 hours.
After that time, it will create a fresh ephemeral key
(forgetting the old one) and broadcast the new
EphemeralKeyMessage to all connected peers, resulting
in fresh symmetric session keys. Note that peers
independently decide on when to discard ephemeral keys; it
is not a protocol violation to discard keys more often.
Ephemeral keys are also never stored to disk; restarting a
peer will thus always create a fresh ephemeral key. The use
of ephemeral keys is what provides forward
secrecy.
Just before transmission, the EphemeralKeyMessage is patched to reflect the current sender_status, which specifies the current state of the connection from the point of view of the sender. The possible values are:
• |
KX_STATE_DOWN Initial value, never used on the network | ||
• |
KX_STATE_KEY_SENT We sent our ephemeral key, do not know the key of the other peer | ||
• |
KX_STATE_KEY_RECEIVED This peer has received a valid ephemeral key of the other peer, but we are waiting for the other peer to confirm itâs authenticity (ability to decode) via challenge−response. | ||
• |
KX_STATE_UP The connection is fully up from the point of view of the sender (now performing keep−alive) | ||
• |
KX_STATE_REKEY_SENT The sender has initiated a rekeying operation; the other peer has so far failed to confirm a working connection using the new ephemeral key |
Establishing
a connection
Peers begin their interaction by sending a
EphemeralKeyMessage to the other peer once the
TRANSPORT service notifies the CORE service about the
connection. A peer receiving an EphemeralKeyMessage
with a status indicating that the sender does not have the
receiverâs ephemeral key, the receiverâs
EphemeralKeyMessage is sent in response.
Additionally, if the receiver has not yet confirmed the
authenticity of the sender, it also sends an
(encrypted)PingMessage with a challenge (and the
identity of the target) to the other peer. Peers receiving a
PingMessage respond with an (encrypted)
PongMessage which includes the challenge. Peers
receiving a PongMessage check the challenge, and if
it matches set the connection to KX_STATE_UP.
Encryption
and Decryption
All functions related to the key exchange and
encryption/decryption of messages can be found in
gnunet−service−core_kx.c (except for the
cryptographic primitives, which are in
util/crypto*.c). Given the key material from ECDHE, a
Key derivation function (Key derivation function) is
used to derive two pairs of encryption and decryption keys
for AES−256 and TwoFish, as well as initialization
vectors and authentication keys (for HMAC (HMAC)).
The HMAC is computed over the encrypted payload. Encrypted
messages include an iv_seed and the HMAC in the header.
Each encrypted message in the CORE service includes a sequence number and a timestamp in the encrypted payload. The CORE service remembers the largest observed sequence number and a bit−mask which represents which of the previous 32 sequence numbers were already used. Messages with sequence numbers lower than the largest observed sequence number minus 32 are discarded. Messages with a timestamp that is less than REKEY_TOLERANCE off (5 minutes) are also discarded. This of course means that system clocks need to be reasonably synchronized for peers to be able to communicate. Additionally, as the ephemeral key changes every 12 hours, a peer would not even be able to decrypt messages older than 12 hours.
Type
maps
Once an encrypted connection has been established, peers
begin to exchange type maps. Type maps are used to allow the
CORE service to determine which (encrypted) connections
should be shown to which applications. A type map is an
array of 65536 bits representing the different types of
messages understood by applications using the CORE service.
Each CORE service maintains this map, simply by setting the
respective bit for each message type supported by any of the
applications using the CORE service. Note that bits for
message types embedded in higher−level protocols (such
as MESH) will not be included in these type maps.
Typically, the type map of a peer will be sparse. Thus, the CORE service attempts to compress its type map using gzip−style compression ("deflate") prior to transmission. However, if the compression fails to compact the map, the map may also be transmitted without compression (resulting in GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP or GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP messages respectively). Upon receiving a type map, the respective CORE service notifies applications about the connection to the other peer if they support any message type indicated in the type map (or no message type at all). If the CORE service experience a connect or disconnect event from an application, it updates its type map (setting or unsetting the respective bits) and notifies its neighbours about the change. The CORE services of the neighbours then in turn generate connect and disconnect events for the peer that sent the type map for their respective applications. As CORE messages may be lost, the CORE service confirms receiving a type map by sending back a GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP. If such a confirmation (with the correct hash of the type map) is not received, the sender will retransmit the type map (with exponential back−off).
NSE â
Network size estimation
NSE stands for Network Size Estimation. The NSE subsystem
provides other subsystems and users with a rough estimate of
the number of peers currently participating in the GNUnet
overlay. The computed value is not a precise number as
producing a precise number in a decentralized, efficient and
secure way is impossible. While NSEâs estimate is
inherently imprecise, NSE also gives the expected range. For
a peer that has been running in a stable network for a
while, the real network size will typically (99.7% of the
time) be in the range of [2/3 estimate, 3/2 estimate]. We
will now give an overview of the algorithm used to calculate
the estimate; all of the details can be found in this
technical report.
Todo
link to the report.
Motivation
Some subsystems, like DHT, need to know the size of the
GNUnet network to optimize some parameters of their own
protocol. The decentralized nature of GNUnet makes efficient
and securely counting the exact number of peers infeasible.
Although there are several decentralized algorithms to count
the number of peers in a system, so far there is none to do
so securely. Other protocols may allow any malicious peer to
manipulate the final result or to take advantage of the
system to perform Denial of Service (DoS) attacks against
the network. GNUnetâs NSE protocol avoids these
drawbacks.
NSE security .. _Security:
Security
The NSE subsystem is designed to be resilient against these
attacks. It uses proofs of work to prevent one peer
from impersonating a large number of participants, which
would otherwise allow an adversary to artificially inflate
the estimate. The DoS protection comes from the
time−based nature of the protocol: the estimates are
calculated periodically and out−of−time traffic
is either ignored or stored for later retransmission by
benign peers. In particular, peers cannot trigger global
network communication at will.
Principle
The algorithm calculates the estimate by finding the
globally closest peer ID to a random, time−based
value.
The idea is that the closer the ID is to the random value, the more "densely packed" the ID space is, and therefore, more peers are in the network.
Example
Suppose all peers have IDs between 0 and 100 (our ID space),
and the random value is 42. If the closest peer has the ID
70 we can imagine that the average "distance"
between peers is around 30 and therefore the are around 3
peers in the whole ID space. On the other hand, if the
closest peer has the ID 44, we can imagine that the space is
rather packed with peers, maybe as much as 50 of them.
Naturally, we could have been rather unlucky, and there is
only one peer and happens to have the ID 44. Thus, the
current estimate is calculated as the average over multiple
rounds, and not just a single sample.
Algorithm
Given that example, one can imagine that the job of the
subsystem is to efficiently communicate the ID of the
closest peer to the target value to all the other peers, who
will calculate the estimate from it.
Target
value
The target value itself is generated by hashing the current
time, rounded down to an agreed value. If the rounding
amount is 1h (default) and the time is 12:34:56, the time to
hash would be 12:00:00. The process is repeated each
rounding amount (in this example would be every hour). Every
repetition is called a round.
Timing
The NSE subsystem has some timing control to avoid everybody
broadcasting its ID all at one. Once each peer has the
target random value, it compares its own ID to the target
and calculates the hypothetical size of the network if that
peer were to be the closest. Then it compares the
hypothetical size with the estimate from the previous
rounds. For each value there is an associated point in the
period, letâs call it "broadcast time". If
its own hypothetical estimate is the same as the previous
global estimate, its "broadcast time" will be in
the middle of the round. If its bigger it will be earlier
and if its smaller (the most likely case) it will be later.
This ensures that the peers closest to the target value
start broadcasting their ID the first.
Controlled
Flooding
When a peer receives a value, first it verifies that it is
closer than the closest value it had so far, otherwise it
answers the incoming message with a message containing the
better value. Then it checks a proof of work that must be
included in the incoming message, to ensure that the other
peerâs ID is not made up (otherwise a malicious peer
could claim to have an ID of exactly the target value every
round). Once validated, it compares the broadcast time of
the received value with the current time and if itâs
not too early, sends the received value to its neighbors.
Otherwise it stores the value until the correct broadcast
time comes. This prevents unnecessary traffic of
sub−optimal values, since a better value can come
before the broadcast time, rendering the previous one
obsolete and saving the traffic that would have been used to
broadcast it to the neighbors.
Calculating
the estimate
Once the closest ID has been spread across the network each
peer gets the exact distance between this ID and the target
value of the round and calculates the estimate with a
mathematical formula described in the tech report. The
estimate generated with this method for a single round is
not very precise. Remember the case of the example, where
the only peer is the ID 44 and we happen to generate the
target value 42, thinking there are 50 peers in the network.
Therefore, the NSE subsystem remembers the last 64 estimates
and calculates an average over them, giving a result of
which usually has one bit of uncertainty (the real size
could be half of the estimate or twice as much). Note that
the actual network size is calculated in powers of two of
the raw input, thus one bit of uncertainty means a factor of
two in the size estimate.
libgnunetnse
The NSE subsystem has the simplest API of all services, with
only two calls: GNUNET_NSE_connect and
GNUNET_NSE_disconnect.
The connect call gets a callback function as a parameter and this function is called each time the network agrees on an estimate. This usually is once per round, with some exceptions: if the closest peer has a late local clock and starts spreading its ID after everyone else agreed on a value, the callback might be activated twice in a round, the second value being always bigger than the first. The default round time is set to 1 hour.
The disconnect call disconnects from the NSE subsystem and the callback is no longer called with new estimates.
Results
The callback provides two values: the average and the
standard deviation of the last 64 rounds. The values
provided by the callback function are logarithmic, this
means that the real estimate numbers can be obtained by
calculating 2 to the power of the given value (2average).
From a statistics point of view this means that:
• |
68% of the time the real size is included in the interval [(2average−stddev), 2] | ||
• |
95% of the time the real size is included in the interval [(2average−2*stddev, 2^average+2*stddev] | ||
• |
99.7% of the time the real size is included in the interval [(2average−3*stddev, 2average+3*stddev] |
The expected standard variation for 64 rounds in a network of stable size is 0.2. Thus, we can say that normally:
• |
68% of the time the real size is in the range [−13%, +15%] | ||
• |
95% of the time the real size is in the range [−24%, +32%] | ||
• |
99.7% of the time the real size is in the range [−34%, +52%] |
As said in the introduction, we can be quite sure that usually the real size is between one third and three times the estimate. This can of course vary with network conditions. Thus, applications may want to also consider the provided standard deviation value, not only the average (in particular, if the standard variation is very high, the average maybe meaningless: the network size is changing rapidly).
Examples
Letâs close with a couple examples.
Average: 10, std dev: 1 Here the estimate would be
2^10 = 1024 peers. (The range in which we can be 95% sure is: [2^8, 2^12] = [256, 4096]. We can be very (>99.7%) sure that the network is not a hundred peers and absolutely sure that it is not a million peers, but somewhere around a thousand.)
Average 22, std dev: 0.2 Here the estimate would be
2^22 = 4 Million peers. (The range in which we can be 99.7% sure is: [2^21.4, 2^22.6] = [2.8M, 6.3M]. We can be sure that the network size is around four million, with absolutely way of it being 1 million.)
To put this in perspective, if someone remembers the LHC Higgs boson results, were announced with "5 sigma" and "6 sigma" certainties. In this case a 5 sigma minimum would be 2 million and a 6 sigma minimum, 1.8 million.
The NSE
Client−Service Protocol
As with the API, the client−service protocol is very
simple, only has 2 different messages, defined in
src/nse/nse.h:
• |
GNUNET_MESSAGE_TYPE_NSE_START This message has no parameters and is sent from the client to the service upon connection. | ||
• |
GNUNET_MESSAGE_TYPE_NSE_ESTIMATE This message is sent from the service to the client for every new estimate and upon connection. Contains a timestamp for the estimate, the average and the standard deviation for the respective round. |
When the GNUNET_NSE_disconnect API call is executed, the client simply disconnects from the service, with no message involved.
NSE Peer−to−Peer Protocol .. _The−NSE−Peer_002dto_002dPeer−Protocol:
The NSE
Peer−to−Peer Protocol
GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD The NSE subsystem only has
one message in the P2P protocol, the
GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD message.
This message key contents are the timestamp to identify the round (differences in system clocks may cause some peers to send messages way too early or way too late, so the timestamp allows other peers to identify such messages easily), the proof of work used to make it difficult to mount a Sybil attack, and the public key, which is used to verify the signature on the message.
Every peer stores a message for the previous, current and next round. The messages for the previous and current round are given to peers that connect to us. The message for the next round is simply stored until our system clock advances to the next round. The message for the current round is what we are flooding the network with right now. At the beginning of each round the peer does the following:
• |
calculates its own distance to the target value | ||
• |
creates, signs and stores the message for the current round (unless it has a better message in the "next round" slot which came early in the previous round) | ||
• |
calculates, based on the stored round message (own or received) when to start flooding it to its neighbors |
Upon receiving a message the peer checks the validity of the message (round, proof of work, signature). The next action depends on the contents of the incoming message:
• |
if the message is worse than the current stored message, the peer sends the current message back immediately, to stop the other peer from spreading suboptimal results | ||
• |
if the message is better than the current stored message, the peer stores the new message and calculates the new target time to start spreading it to its neighbors (excluding the one the message came from) | ||
• |
if the message is for the previous round, it is compared to the message stored in the "previous round slot", which may then be updated | ||
• |
if the message is for the next round, it is compared to the message stored in the "next round slot", which again may then be updated |
Finally, when it comes to send the stored message for the current round to the neighbors there is a random delay added for each neighbor, to avoid traffic spikes and minimize cross−messages.
DHT â
Distributed Hash Table
GNUnet includes a generic distributed hash table that can be
used by developers building P2P applications in the
framework. This section documents high−level features
and how developers are expected to use the DHT. We have a
research paper detailing how the DHT works. Also,
Nateâs thesis includes a detailed description and
performance analysis (in chapter 6). [R5N2011]
Todo
Confirm: Are âNateâs thesisâ and the
âresearch paperâ separate entities?
Key features of GNUnetâs DHT include:
• |
stores key−value pairs with values up to (approximately) 63k in size | ||
• |
works with many underlay network topologies (small−world, random graph), underlay does not need to be a full mesh / clique | ||
• |
support for extended queries (more than just a simple âkeyâ), filtering duplicate replies within the network (bloomfilter) and content validation (for details, please read the subsection on the block library) | ||
• |
can (optionally) return paths taken by the PUT and GET operations to the application | ||
• |
provides content replication to handle churn |
GNUnetâs DHT is randomized and unreliable. Unreliable means that there is no strict guarantee that a value stored in the DHT is always found â values are only found with high probability. While this is somewhat true in all P2P DHTs, GNUnet developers should be particularly wary of this fact (this will help you write secure, fault−tolerant code). Thus, when writing any application using the DHT, you should always consider the possibility that a value stored in the DHT by you or some other peer might simply not be returned, or returned with a significant delay. Your application logic must be written to tolerate this (naturally, some loss of performance or quality of service is expected in this case).
Block
library and plugins
What is a Block?
Blocks are small (< 63k) pieces of data stored under a
key (struct GNUNET_HashCode). Blocks have a type (enum
GNUNET_BlockType) which defines their data format. Blocks
are used in GNUnet as units of static data exchanged between
peers and stored (or cached) locally. Uses of blocks include
file−sharing (the files are broken up into blocks),
the VPN (DNS information is stored in blocks) and the DHT
(all information in the DHT and meta−information for
the maintenance of the DHT are both stored using blocks).
The block subsystem provides a few common functions that
must be available for any type of block.
libgnunetblock API .. _The−API−of−libgnunetblock:
The API of
libgnunetblock
The block library requires for each (family of) block
type(s) a block plugin (implementing
gnunet_block_plugin.h) that provides basic functions
that are needed by the DHT (and possibly other subsystems)
to manage the block. These block plugins are typically
implemented within their respective subsystems. The main
block library is then used to locate, load and query the
appropriate block plugin. Which plugin is appropriate is
determined by the block type (which is just a 32−bit
integer). Block plugins contain code that specifies which
block types are supported by a given plugin. The block
library loads all block plugins that are installed at the
local peer and forwards the application request to the
respective plugin.
The central functions of the block APIs (plugin and main library) are to allow the mapping of blocks to their respective key (if possible) and the ability to check that a block is well−formed and matches a given request (again, if possible). This way, GNUnet can avoid storing invalid blocks, storing blocks under the wrong key and forwarding blocks in response to a query that they do not answer.
One key function of block plugins is that it allows GNUnet to detect duplicate replies (via the Bloom filter). All plugins MUST support detecting duplicate replies (by adding the current response to the Bloom filter and rejecting it if it is encountered again). If a plugin fails to do this, responses may loop in the network.
Queries
The query format for any block in GNUnet consists of four
main components. First, the type of the desired block must
be specified. Second, the query must contain a hash code.
The hash code is used for lookups in hash tables and
databases and must not be unique for the block (however, if
possible a unique hash should be used as this would be best
for performance). Third, an optional Bloom filter can be
specified to exclude known results; replies that hash to the
bits set in the Bloom filter are considered invalid.
False−positives can be eliminated by sending the same
query again with a different Bloom filter mutator value,
which parametrizes the hash function that is used. Finally,
an optional application−specific "eXtended
query" (xquery) can be specified to further constrain
the results. It is entirely up to the type−specific
plugin to determine whether or not a given block matches a
query (type, hash, Bloom filter, and xquery). Naturally, not
all xqueryâs are valid and some types of blocks may
not support Bloom filters either, so the plugin also needs
to check if the query is valid in the first place.
Depending on the results from the plugin, the DHT will then discard the (invalid) query, forward the query, discard the (invalid) reply, cache the (valid) reply, and/or forward the (valid and non−duplicate) reply.
Sample
Code
The source code in plugin_block_test.c is a good
starting point for new block plugins â it does the
minimal work by implementing a plugin that performs no
validation at all. The respective Makefile.am shows
how to build and install a block plugin.
Conclusion2
In conclusion, GNUnet subsystems that want to use the DHT
need to define a block format and write a plugin to match
queries and replies. For testing, the
GNUNET_BLOCK_TYPE_TEST block type can be used; it
accepts any query as valid and any reply as matching any
query. This type is also used for the DHT command line
tools. However, it should NOT be used for normal
applications due to the lack of error checking that results
from this primitive implementation.
libgnunetdht libgnunetdht âââââââââââââââ−
The DHT API itself is pretty simple and offers the usual GET and PUT functions that work as expected. The specified block type refers to the block library which allows the DHT to run application−specific logic for data stored in the network.
GET
When using GET, the main consideration for developers (other
than the block library) should be that after issuing a GET,
the DHT will continuously cause (small amounts of) network
traffic until the operation is explicitly canceled. So GET
does not simply send out a single network request once;
instead, the DHT will continue to search for data. This is
needed to achieve good success rates and also handles the
case where the respective PUT operation happens after the
GET operation was started. Developers should not cancel an
existing GET operation and then explicitly re−start it
to trigger a new round of network requests; this is simply
inefficient, especially as the internal automated version
can be more efficient, for example by filtering results in
the network that have already been returned.
If an application that performs a GET request has a set of replies that it already knows and would like to filter, it can call GNUNET_DHT_get_filter_known_results with an array of hashes over the respective blocks to tell the DHT that these results are not desired (any more). This way, the DHT will filter the respective blocks using the block library in the network, which may result in a significant reduction in bandwidth consumption.
PUT
Todo
inconsistent use of âmustâ above itâs
written âMUSTâ
In contrast to GET operations, developers must manually re−run PUT operations periodically (if they intend the content to continue to be available). Content stored in the DHT expires or might be lost due to churn. Furthermore, GNUnetâs DHT typically requires multiple rounds of PUT operations before a key−value pair is consistently available to all peers (the DHT randomizes paths and thus storage locations, and only after multiple rounds of PUTs there will be a sufficient number of replicas in large DHTs). An explicit PUT operation using the DHT API will only cause network traffic once, so in order to ensure basic availability and resistance to churn (and adversaries), PUTs must be repeated. While the exact frequency depends on the application, a rule of thumb is that there should be at least a dozen PUT operations within the content lifetime. Content in the DHT typically expires after one day, so DHT PUT operations should be repeated at least every 1−2 hours.
MONITOR
The DHT API also allows applications to monitor messages
crossing the local DHT service. The types of messages used
by the DHT are GET, PUT and RESULT messages. Using the
monitoring API, applications can choose to monitor these
requests, possibly limiting themselves to requests for a
particular block type.
The monitoring API is not only useful for diagnostics, it can also be used to trigger application operations based on PUT operations. For example, an application may use PUTs to distribute work requests to other peers. The workers would then monitor for PUTs that give them work, instead of looking for work using GET operations. This can be beneficial, especially if the workers have no good way to guess the keys under which work would be stored. Naturally, additional protocols might be needed to ensure that the desired number of workers will process the distributed workload.
DHT Routing
Options
There are two important options for GET and PUT requests:
GNUNET_DHT_RO_DEMULITPLEX_EVERYWHERE This option means that
all
peers should process the request, even if their peer ID is not closest to the key. For a PUT request, this means that all peers that a request traverses may make a copy of the data. Similarly for a GET request, all peers will check their local database for a result. Setting this option can thus significantly improve caching and reduce bandwidth consumption â at the expense of a larger DHT database. If in doubt, we recommend that this option should be used.
GNUNET_DHT_RO_RECORD_ROUTE This option instructs the DHT to record
the path that a GET or a PUT request is taking through the overlay network. The resulting paths are then returned to the application with the respective result. This allows the receiver of a result to construct a path to the originator of the data, which might then be used for routing. Naturally, setting this option requires additional bandwidth and disk space, so applications should only set this if the paths are needed by the application logic.
GNUNET_DHT_RO_FIND_PEER This option is an internal option used by
the DHTâs peer discovery mechanism and should not be used by applications.
GNUNET_DHT_RO_BART This option is currently not implemented. It may
in the future offer performance improvements for clique topologies.
The DHT
Client−Service Protocol
PUTting data into the DHT
To store (PUT) data into the DHT, the client sends a
struct GNUNET_DHT_ClientPutMessage to the service.
This message specifies the block type, routing options, the
desired replication level, the expiration time, key, value
and a 64−bit unique ID for the operation. The service
responds with a struct
GNUNET_DHT_ClientPutConfirmationMessage with the same
64−bit unique ID. Note that the service sends the
confirmation as soon as it has locally processed the PUT
request. The PUT may still be propagating through the
network at this time.
In the future, we may want to change this to provide (limited) feedback to the client, for example if we detect that the PUT operation had no effect because the same key−value pair was already stored in the DHT. However, changing this would also require additional state and messages in the P2P interaction.
GETting data
from the DHT
To retrieve (GET) data from the DHT, the client sends a
struct GNUNET_DHT_ClientGetMessage to the service.
The message specifies routing options, a replication level
(for replicating the GET, not the content), the desired
block type, the key, the (optional) extended query and
unique 64−bit request ID.
Additionally, the client may send any number of struct GNUNET_DHT_ClientGetResultSeenMessages to notify the service about results that the client is already aware of. These messages consist of the key, the unique 64−bit ID of the request, and an arbitrary number of hash codes over the blocks that the client is already aware of. As messages are restricted to 64k, a client that already knows more than about a thousand blocks may need to send several of these messages. Naturally, the client should transmit these messages as quickly as possible after the original GET request such that the DHT can filter those results in the network early on. Naturally, as these messages are sent after the original request, it is conceivable that the DHT service may return blocks that match those already known to the client anyway.
In response to a GET request, the service will send struct GNUNET_DHT_ClientResultMessages to the client. These messages contain the block type, expiration, key, unique ID of the request and of course the value (a block). Depending on the options set for the respective operations, the replies may also contain the path the GET and/or the PUT took through the network.
A client can stop receiving replies either by disconnecting or by sending a struct GNUNET_DHT_ClientGetStopMessage which must contain the key and the 64−bit unique ID of the original request. Using an explicit "stop" message is more common as this allows a client to run many concurrent GET operations over the same connection with the DHT service â and to stop them individually.
Monitoring
the DHT
To begin monitoring, the client sends a struct
GNUNET_DHT_MonitorStartStop message to the DHT service.
In this message, flags can be set to enable (or disable)
monitoring of GET, PUT and RESULT messages that pass through
a peer. The message can also restrict monitoring to a
particular block type or a particular key. Once monitoring
is enabled, the DHT service will notify the client about any
matching event using struct
GNUNET_DHT_MonitorGetMessages for GET events, struct
GNUNET_DHT_MonitorPutMessage for PUT events and
struct GNUNET_DHT_MonitorGetRespMessage for RESULTs.
Each of these messages contains all of the information about
the event.
The DHT
Peer−to−Peer Protocol
Routing GETs or PUTs
When routing GETs or PUTs, the DHT service selects a
suitable subset of neighbours for forwarding. The exact
number of neighbours can be zero or more and depends on the
hop counter of the query (initially zero) in relation to the
(log of) the network size estimate, the desired replication
level and the peerâs connectivity. Depending on the
hop counter and our network size estimate, the selection of
the peers maybe randomized or by proximity to the key.
Furthermore, requests include a set of peers that a request
has already traversed; those peers are also excluded from
the selection.
PUTting data
into the DHT
To PUT data into the DHT, the service sends a struct
PeerPutMessage of type
GNUNET_MESSAGE_TYPE_DHT_P2P_PUT to the respective
neighbour. In addition to the usual information about the
content (type, routing options, desired replication level
for the content, expiration time, key and value), the
message contains a fixed−size Bloom filter with
information about which peers (may) have already seen this
request. This Bloom filter is used to ensure that DHT
messages never loop back to a peer that has already
processed the request. Additionally, the message includes
the current hop counter and, depending on the routing
options, the message may include the full path that the
message has taken so far. The Bloom filter should already
contain the identity of the previous hop; however, the path
should not include the identity of the previous hop and the
receiver should append the identity of the sender to the
path, not its own identity (this is done to reduce
bandwidth).
GETting data
from the DHT
A peer can search the DHT by sending struct
PeerGetMessages of type
GNUNET_MESSAGE_TYPE_DHT_P2P_GET to other peers. In
addition to the usual information about the request (type,
routing options, desired replication level for the request,
the key and the extended query), a GET request also contains
a hop counter, a Bloom filter over the peers that have
processed the request already and depending on the routing
options the full path traversed by the GET. Finally, a GET
request includes a variable−size second Bloom filter
and a so−called Bloom filter mutator value which
together indicate which replies the sender has already seen.
During the lookup, each block that matches they block type,
key and extended query is additionally subjected to a test
against this Bloom filter. The block plugin is expected to
take the hash of the block and combine it with the mutator
value and check if the result is not yet in the Bloom
filter. The originator of the query will from time to time
modify the mutator to (eventually) allow
false−positives filtered by the Bloom filter to be
returned.
Peers that
receive a GET request perform a local lookup (depending on
their proximity to the key and the query options) and
forward the request to other peers. They then remember the
request (including the Bloom filter for blocking duplicate
results) and when they obtain a matching, non−filtered
response a struct PeerResultMessage of type
GNUNET_MESSAGE_TYPE_DHT_P2P_RESULT is forwarded to
the previous hop. Whenever a result is forwarded, the block
plugin is used to update the Bloom filter accordingly, to
ensure that the same result is never forwarded more than
once. The DHT service may also cache forwarded results
locally if the "CACHE_RESULTS" option is set to
"YES" in the configuration.
[R5N2011]
https://bib.gnunet.org/date.html#R5N
REGEX
â Service discovery using regular expressions
Using the REGEX subsystem, you can discover peers that offer
a particular service using regular expressions. The peers
that offer a service specify it using a regular expressions.
Peers that want to patronize a service search using a
string. The REGEX subsystem will then use the DHT to return
a set of matching offerers to the patrons.
For the technical details, we have Maxâs defense talk and Maxâs Masterâs thesis.
NOTE:
An additional publication is under preparation and available to team members (in Git).
Todo
Missing links to Maxâs talk and Masterâs
thesis
How to run
the regex profiler
The gnunet−regex−profiler can be used to profile
the usage of mesh/regex for a given set of regular
expressions and strings. Mesh/regex allows you to announce
your peer ID under a certain regex and search for peers
matching a particular regex using a string. See
szengel2012ms for a full introduction.
First of all, the regex profiler uses GNUnet testbed, thus all the implications for testbed also apply to the regex profiler (for example you need password−less ssh login to the machines listed in your hosts file).
Configuration
Moreover, an appropriate configuration file is needed. In the following paragraph the important details are highlighted.
Announcing of the regular expressions is done by the gnunet−daemon−regexprofiler, therefore you have to make sure it is started, by adding it to the START_ON_DEMAND set of ARM:
[regexprofiler] START_ON_DEMAND = YES
Furthermore you have to specify the location of the binary:
[regexprofiler] # Location of the gnunet−daemon−regexprofiler binary. BINARY = /home/szengel/gnunet/src/mesh/.libs/gnunet−daemon−regexprofiler # Regex prefix that will be applied to all regular expressions and # search string. REGEX_PREFIX = "GNVPN−0001−PAD"
When running the profiler with a large scale deployment, you probably want to reduce the workload of each peer. Use the following options to do this.
[dht] # Force network size estimation FORCE_NSE = 1 [dhtcache] DATABASE = heap # Disable RC−file for Bloom filter? (for benchmarking with limited IO # availability) DISABLE_BF_RC = YES # Disable Bloom filter entirely DISABLE_BF = YES [nse] # Minimize proof−of−work CPU consumption by NSE WORKBITS = 1
Options
To finally run the profiler some options and the input data need to be specified on the command line.
gnunet−regex−profiler −c config−file −d log−file −n num−links \ −p path−compression−length −s search−delay −t matching−timeout \ −a num−search−strings hosts−file policy−dir search−strings−file
Where...
• |
... config−file means the configuration file created earlier. | ||
• |
... log−file is the file where to write statistics output. | ||
• |
... num−links indicates the number of random links between started peers. | ||
• |
... path−compression−length is the maximum path compression length in the DFA. | ||
• |
... search−delay time to wait between peers finished linking and starting to match strings. | ||
• |
... matching−timeout timeout after which to cancel the searching. | ||
• |
... num−search−strings number of strings in the search−strings−file. | ||
• |
... the hosts−file should contain a list of hosts for the testbed, one per line in the following format: |
•
user@host_ip:port | |||
• |
... the policy−dir is a folder containing text files containing one or more regular expressions. A peer is started for each file in that folder and the regular expressions in the corresponding file are announced by this peer. | ||
• |
... the search−strings−file is a text file containing search strings, one in each line. |
You can create regular expressions and search strings for every AS in the Internet using the attached scripts. You need one of the CAIDA routeviews prefix2as data files for this. Run
create_regex.py <filename> <output path>
to create the regular expressions and
create_strings.py <input path> <outfile>
to create a search strings file from the previously created regular expressions.
CADET
â Confidential Ad−hoc Decentralized
End−to−end Transport
The CADET subsystem in GNUnet is responsible for secure
end−to−end communications between nodes in the
GNUnet overlay network. CADET builds on the CORE subsystem,
which provides for the link−layer communication, by
adding routing, forwarding, and additional security to the
connections. CADET offers the same cryptographic services as
CORE, but on an end−to−end level. This is done
so peers retransmitting traffic on behalf of other peers
cannot access the payload data.
• |
CADET provides confidentiality with so−called perfect forward secrecy; we use ECDHE powered by Curve25519 for the key exchange and then use symmetric encryption, encrypting with both AES−256 and Twofish | ||
• |
authentication is achieved by signing the ephemeral keys using Ed25519, a deterministic variant of ECDSA | ||
• |
integrity protection (using SHA−512 to do encrypt−then−MAC, although only 256 bits are sent to reduce overhead) | ||
• |
replay protection (using nonces, timestamps, challenge−response, message counters and ephemeral keys) | ||
• |
liveness (keep−alive messages, timeout) |
Additional to the CORE−like security benefits, CADET offers other properties that make it a more universal service than CORE.
• |
CADET can establish channels to arbitrary peers in GNUnet. If a peer is not immediately reachable, CADET will find a path through the network and ask other peers to retransmit the traffic on its behalf. | ||
• |
CADET offers (optional) reliability mechanisms. In a reliable channel traffic is guaranteed to arrive complete, unchanged and in−order. | ||
• |
CADET takes care of flow and congestion control mechanisms, not allowing the sender to send more traffic than the receiver or the network are able to process. |
libgnunetcadet
The CADET API (defined in gnunet_cadet_service.h) is
the messaging API used by P2P applications built using
GNUnet. It provides applications the ability to send and
receive encrypted messages to any peer participating in
GNUnet. The API is heavily based on the CORE API.
CADET delivers messages to other peers in "channels". A channel is a permanent connection defined by a destination peer (identified by its public key) and a port number. Internally, CADET tunnels all channels towards a destination peer using one session key and relays the data on multiple "connections", independent from the channels.
Each channel has optional parameters, the most important being the reliability flag. Should a message get lost on TRANSPORT/CORE level, if a channel is created with as reliable, CADET will retransmit the lost message and deliver it in order to the destination application.
GNUNET_CADET_connect
To communicate with other peers using CADET, it is necessary to first connect to the service using GNUNET_CADET_connect. This function takes several parameters in form of callbacks, to allow the client to react to various events, like incoming channels or channels that terminate, as well as specify a list of ports the client wishes to listen to (at the moment it is not possible to start listening on further ports once connected, but nothing prevents a client to connect several times to CADET, even do one connection per listening port). The function returns a handle which has to be used for any further interaction with the service.
GNUNET_CADET_channel_create
To connect to a remote peer, a client has to call the GNUNET_CADET_channel_create function. The most important parameters given are the remote peerâs identity (it public key) and a port, which specifies which application on the remote peer to connect to, similar to TCP/UDP ports. CADET will then find the peer in the GNUnet network and establish the proper low−level connections and do the necessary key exchanges to assure and authenticated, secure and verified communication. Similar to GNUNET_CADET_connect,GNUNET_CADET_create_channel returns a handle to interact with the created channel.
GNUNET_CADET_notify_transmit_ready
For every message the client wants to send to the remote application, GNUNET_CADET_notify_transmit_ready must be called, indicating the channel on which the message should be sent and the size of the message (but not the message itself!). Once CADET is ready to send the message, the provided callback will fire, and the message contents are provided to this callback.
Please note the CADET does not provide an explicit notification of when a channel is connected. In loosely connected networks, like big wireless mesh networks, this can take several seconds, even minutes in the worst case. To be alerted when a channel is online, a client can call GNUNET_CADET_notify_transmit_ready immediately after GNUNET_CADET_create_channel. When the callback is activated, it means that the channel is online. The callback can give 0 bytes to CADET if no message is to be sent, this is OK.
GNUNET_CADET_notify_transmit_cancel
If a transmission was requested but before the callback fires it is no longer needed, it can be canceled with GNUNET_CADET_notify_transmit_ready_cancel, which uses the handle given back by GNUNET_CADET_notify_transmit_ready. As in the case of CORE, only one message can be requested at a time: a client must not call GNUNET_CADET_notify_transmit_ready again until the callback is called or the request is canceled.
GNUNET_CADET_channel_destroy
When a channel is no longer needed, a client can call GNUNET_CADET_channel_destroy to get rid of it. Note that CADET will try to transmit all pending traffic before notifying the remote peer of the destruction of the channel, including retransmitting lost messages if the channel was reliable.
Incoming channels, channels being closed by the remote peer, and traffic on any incoming or outgoing channels are given to the client when CADET executes the callbacks given to it at the time of GNUNET_CADET_connect.
GNUNET_CADET_disconnect
Finally, when an application no longer wants to use CADET, it should call GNUNET_CADET_disconnect, but first all channels and pending transmissions must be closed (otherwise CADET will complain).
RPS â
Random peer sampling
In literature, Random Peer Sampling (RPS) refers to the
problem of reliably [1] drawing random samples from an
unstructured p2p network.
Doing so in a reliable manner is not only hard because of inherent problems but also because of possible malicious peers that could try to bias the selection.
It is useful for all kind of gossip protocols that require the selection of random peers in the whole network like gathering statistics, spreading and aggregating information in the network, load balancing and overlay topology management.
The approach chosen in the RPS service implementation in GNUnet follows the Brahms design.
The current state is "work in progress". There are a lot of things that need to be done, primarily finishing the experimental evaluation and a re−design of the API.
The abstract idea is to subscribe to connect to/start the RPS service and request random peers that will be returned when they represent a random selection from the whole network with high probability.
An additional feature to the original Brahms−design is the selection of sub−groups: The GNUnet implementation of RPS enables clients to ask for random peers from a group that is defined by a common shared secret. (The secret could of course also be public, depending on the use−case.)
Another addition to the original protocol was made: The sampler mechanism that was introduced in Brahms was slightly adapted and used to actually sample the peers and returned to the client. This is necessary as the original design only keeps peers connected to random other peers in the network. In order to return random peers to client requests independently random, they cannot be drawn from the connected peers. The adapted sampler makes sure that each request for random peers is independent from the others.
Brahms
The high−level concept of Brahms is two−fold:
Combining push−pull gossip with locally fixing a
assumed bias using cryptographic min−wise
permutations. The central data structure is the view −
a peerâs current local sample. This view is used to
select peers to push to and pull from. This simple mechanism
can be biased easily. For this reason Brahms
âfixesâ the bias by using the so−called
sampler. A data structure that takes a list of elements as
input and outputs a random one of them independently of the
frequency in the input set. Both an element that was put
into the sampler a single time and an element that was put
into it a million times have the same probability of being
the output. This is achieved with exploiting min−wise
independent permutations. In the RPS service we use HMACs:
On the initialisation of a sampler element, a key is chosen
at random. On each input the HMAC with the random key is
computed. The sampler element keeps the element with the
minimal HMAC.
In order to fix the bias in the view, a fraction of the elements in the view are sampled through the sampler from the random stream of peer IDs.
According to the theoretical analysis of Bortnikov et al. this suffices to keep the network connected and having random peers in the view.
[1] |
"Reliable" in this context means having no bias, neither spatial, nor temporal, nor through malicious activity. |
Peer−to−Peer
Set Operations
Many applications
SET â
Peer to peer set operations (Deprecated)
NOTE:
The SET subsystem is in process of being replaced by the SETU and SETI subsystems, which provide basically the same functionality, just using two different subsystems. SETI and SETU should be used for new code.
The SET service implements efficient set operations between two peers over a CADET tunnel. Currently, set union and set intersection are the only supported operations. Elements of a set consist of an element type and arbitrary binary data. The size of an elementâs data is limited to around 62 KB.
Local
Sets
Sets created by a local client can be modified and reused
for multiple operations. As each set operation requires
potentially expensive special auxiliary data to be computed
for each element of a set, a set can only participate in one
type of set operation (either union or intersection). The
type of a set is determined upon its creation. If a the
elements of a set are needed for an operation of a different
type, all of the setâs element must be copied to a new
set of appropriate type.
Set
Modifications
Even when set operations are active, one can add to and
remove elements from a set. However, these changes will only
be visible to operations that have been created after the
changes have taken place. That is, every set operation only
sees a snapshot of the set from the time the operation was
started. This mechanism is not implemented by copying
the whole set, but by attaching generation
information to each element and operation.
Set
Operations
Set operations can be started in two ways: Either by
accepting an operation request from a remote peer, or by
requesting a set operation from a remote peer. Set
operations are uniquely identified by the involved
peers, an application id and the operation
type.
The client is notified of incoming set operations by set listeners. A set listener listens for incoming operations of a specific operation type and application id. Once notified of an incoming set request, the client can accept the set request (providing a local set for the operation) or reject it.
Result
Elements
The SET service has three result modes that determine
how an operationâs result set is delivered to the
client:
• |
Full Result Set. All elements of set resulting from the set operation are returned to the client. | ||
• |
Added Elements. Only elements that result from the operation and are not already in the local peerâs set are returned. Note that for some operations (like set intersection) this result mode will never return any elements. This can be useful if only the remove peer is actually interested in the result of the set operation. | ||
• |
Removed Elements. Only elements that are in the local peerâs initial set but not in the operationâs result set are returned. Note that for some operations (like set union) this result mode will never return any elements. This can be useful if only the remove peer is actually interested in the result of the set operation. |
libgnunetset
Sets
New sets are created with GNUNET_SET_create. Both the
local peerâs configuration (as each set has its own
client connection) and the operation type must be specified.
The set exists until either the client calls
GNUNET_SET_destroy or the clientâs connection
to the service is disrupted. In the latter case, the client
is notified by the return value of functions dealing with
sets. This return value must always be checked.
Elements are added and removed with GNUNET_SET_add_element and GNUNET_SET_remove_element.
Listeners
Listeners are created with GNUNET_SET_listen. Each
time time a remote peer suggests a set operation with an
application id and operation type matching a listener, the
listenerâs callback is invoked. The client then must
synchronously call either GNUNET_SET_accept or
GNUNET_SET_reject. Note that the operation will not
be started until the client calls GNUNET_SET_commit
(see Section "Supplying a Set").
Operations
Operations to be initiated by the local peer are created
with GNUNET_SET_prepare. Note that the operation will
not be started until the client calls
GNUNET_SET_commit (see Section "Supplying a
Set").
Supplying a
Set
To create symmetry between the two ways of starting a set
operation (accepting and initiating it), the operation
handles returned by GNUNET_SET_accept and
GNUNET_SET_prepare do not yet have a set to operate
on, thus they can not do any work yet.
The client must call GNUNET_SET_commit to specify a set to use for an operation. GNUNET_SET_commit may only be called once per set operation.
The Result
Callback
Clients must specify both a result mode and a result
callback with GNUNET_SET_accept and
GNUNET_SET_prepare. The result callback with a status
indicating either that an element was received, or the
operation failed or succeeded. The interpretation of the
received element depends on the result mode. The callback
needs to know which result mode it is used in, as the
arguments do not indicate if an element is part of the full
result set, or if it is in the difference between the
original set and the final set.
The SET
Client−Service Protocol
Creating Sets
For each set of a client, there exists a client connection
to the service. Sets are created by sending the
GNUNET_SERVICE_SET_CREATE message over a new client
connection. Multiple operations for one set are multiplexed
over one client connection, using a request id supplied by
the client.
Listeners
Each listener also requires a separate client connection. By
sending the GNUNET_SERVICE_SET_LISTEN message, the
client notifies the service of the application id and
operation type it is interested in. A client rejects an
incoming request by sending GNUNET_SERVICE_SET_REJECT
on the listenerâs client connection. In contrast, when
accepting an incoming request, a
GNUNET_SERVICE_SET_ACCEPT message must be sent over
the set that is supplied for the set operation.
Initiating
Operations
Operations with remote peers are initiated by sending a
GNUNET_SERVICE_SET_EVALUATE message to the service.
The client connection that this message is sent by
determines the set to use.
Modifying
Sets
Sets are modified with the GNUNET_SERVICE_SET_ADD and
GNUNET_SERVICE_SET_REMOVE messages.
Results and
Operation Status
The service notifies the client of result elements and
success/failure of a set operation with the
GNUNET_SERVICE_SET_RESULT message.
Iterating
Sets
All elements of a set can be requested by sending
GNUNET_SERVICE_SET_ITER_REQUEST. The server responds
with GNUNET_SERVICE_SET_ITER_ELEMENT and eventually
terminates the iteration with
GNUNET_SERVICE_SET_ITER_DONE. After each received
element, the client must send
GNUNET_SERVICE_SET_ITER_ACK. Note that only one set
iteration may be active for a set at any given time.
The SET
Intersection Peer−to−Peer Protocol
The intersection protocol operates over CADET and starts
with a GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST being
sent by the peer initiating the operation to the peer
listening for inbound requests. It includes the number of
elements of the initiating peer, which is used to decide
which side will send a Bloom filter first.
The listening peer checks if the operation type and application identifier are acceptable for its current state. If not, it responds with a GNUNET_MESSAGE_TYPE_SET_RESULT and a status of GNUNET_SET_STATUS_FAILURE (and terminates the CADET channel).
If the application accepts the request, the listener sends back a GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO if it has more elements in the set than the client. Otherwise, it immediately starts with the Bloom filter exchange. If the initiator receives a GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO response, it beings the Bloom filter exchange, unless the set size is indicated to be zero, in which case the intersection is considered finished after just the initial handshake.
The Bloom
filter exchange
In this phase, each peer transmits a Bloom filter over the
remaining keys of the local set to the other peer using a
GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF message.
This message additionally includes the number of elements
left in the senderâs set, as well as the XOR over all
of the keys in that set.
The number of bits âkâ set per element in the Bloom filter is calculated based on the relative size of the two sets. Furthermore, the size of the Bloom filter is calculated based on âkâ and the number of elements in the set to maximize the amount of data filtered per byte transmitted on the wire (while avoiding an excessively high number of iterations).
The receiver of the message removes all elements from its local set that do not pass the Bloom filter test. It then checks if the set size of the sender and the XOR over the keys match what is left of its own set. If they do, it sends a GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE back to indicate that the latest set is the final result. Otherwise, the receiver starts another Bloom filter exchange, except this time as the sender.
Salt
Bloomfilter operations are probabilistic: With some
non−zero probability the test may incorrectly say an
element is in the set, even though it is not.
To mitigate this problem, the intersection protocol iterates exchanging Bloom filters using a different random 32−bit salt in each iteration (the salt is also included in the message). With different salts, set operations may fail for different elements. Merging the results from the executions, the probability of failure drops to zero.
The iterations terminate once both peers have established that they have sets of the same size, and where the XOR over all keys computes the same 512−bit value (leaving a failure probability of 2 −511 ).
The SET
Union Peer−to−Peer Protocol
The SET union protocol is based on Eppsteinâs
efficient set reconciliation without prior context. You
should read this paper first if you want to understand the
protocol.
Todo
Link to Eppsteinâs paper!
The union protocol operates over CADET and starts with a GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST being sent by the peer initiating the operation to the peer listening for inbound requests. It includes the number of elements of the initiating peer, which is currently not used.
The listening peer checks if the operation type and application identifier are acceptable for its current state. If not, it responds with a GNUNET_MESSAGE_TYPE_SET_RESULT and a status of GNUNET_SET_STATUS_FAILURE (and terminates the CADET channel).
If the application accepts the request, it sends back a strata estimator using a message of type GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE. The initiator evaluates the strata estimator and initiates the exchange of invertible Bloom filters, sending a GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF.
During the IBF exchange, if the receiver cannot invert the Bloom filter or detects a cycle, it sends a larger IBF in response (up to a defined maximum limit; if that limit is reached, the operation fails). Elements decoded while processing the IBF are transmitted to the other peer using GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS, or requested from the other peer using GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENT_REQUESTS messages, depending on the sign observed during decoding of the IBF. Peers respond to a GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENT_REQUESTS message with the respective element in a GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS message. If the IBF fully decodes, the peer responds with a GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE message instead of another GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF.
All Bloom filter operations use a salt to mingle keys before hashing them into buckets, such that future iterations have a fresh chance of succeeding if they failed due to collisions before.
SETI â
Peer to peer set intersections
The SETI service implements efficient set intersection
between two peers over a CADET tunnel. Elements of a set
consist of an element type and arbitrary binary
data. The size of an elementâs data is limited
to around 62 KB.
Intersection
Sets
Sets created by a local client can be modified (by adding
additional elements) and reused for multiple operations. If
elements are to be removed, a fresh set must be created by
the client.
Set
Intersection Modifications
Even when set operations are active, one can add elements to
a set. However, these changes will only be visible to
operations that have been created after the changes have
taken place. That is, every set operation only sees a
snapshot of the set from the time the operation was started.
This mechanism is not implemented by copying the
whole set, but by attaching generation information to
each element and operation.
Set
Intersection Operations
Set operations can be started in two ways: Either by
accepting an operation request from a remote peer, or by
requesting a set operation from a remote peer. Set
operations are uniquely identified by the involved
peers, an application id and the operation
type.
The client is notified of incoming set operations by set listeners. A set listener listens for incoming operations of a specific operation type and application id. Once notified of an incoming set request, the client can accept the set request (providing a local set for the operation) or reject it.
Intersection
Result Elements
The SET service has two result modes that determine
how an operationâs result set is delivered to the
client:
• |
Return intersection. All elements of set resulting from the set intersection are returned to the client. | ||
• |
Removed Elements. Only elements that are in the local peerâs initial set but not in the intersection are returned. |
libgnunetseti
Intersection Set API
New sets are created with GNUNET_SETI_create. Only
the local peerâs configuration (as each set has its
own client connection) must be provided. The set exists
until either the client calls GNUNET_SET_destroy or
the clientâs connection to the service is disrupted.
In the latter case, the client is notified by the return
value of functions dealing with sets. This return value must
always be checked.
Elements are added with GNUNET_SET_add_element.
Intersection
Listeners
Listeners are created with GNUNET_SET_listen. Each
time time a remote peer suggests a set operation with an
application id and operation type matching a listener, the
listenerâs callback is invoked. The client then must
synchronously call either GNUNET_SET_accept or
GNUNET_SET_reject. Note that the operation will not
be started until the client calls GNUNET_SET_commit
(see Section "Supplying a Set").
Intersection
Operations
Operations to be initiated by the local peer are created
with GNUNET_SET_prepare. Note that the operation will
not be started until the client calls
GNUNET_SET_commit (see Section "Supplying a
Set").
Supplying a
Set for Intersection
To create symmetry between the two ways of starting a set
operation (accepting and initiating it), the operation
handles returned by GNUNET_SET_accept and
GNUNET_SET_prepare do not yet have a set to operate
on, thus they can not do any work yet.
The client must call GNUNET_SET_commit to specify a set to use for an operation. GNUNET_SET_commit may only be called once per set operation.
The
Intersection Result Callback
Clients must specify both a result mode and a result
callback with GNUNET_SET_accept and
GNUNET_SET_prepare. The result callback with a status
indicating either that an element was received, or the
operation failed or succeeded. The interpretation of the
received element depends on the result mode. The callback
needs to know which result mode it is used in, as the
arguments do not indicate if an element is part of the full
result set, or if it is in the difference between the
original set and the final set.
The SETI
Client−Service Protocol
Creating Intersection Sets
For each set of a client, there exists a client connection
to the service. Sets are created by sending the
GNUNET_SERVICE_SETI_CREATE message over a new client
connection. Multiple operations for one set are multiplexed
over one client connection, using a request id supplied by
the client.
Listeners
for Intersection
Each listener also requires a separate client connection. By
sending the GNUNET_SERVICE_SETI_LISTEN message, the
client notifies the service of the application id and
operation type it is interested in. A client rejects an
incoming request by sending
GNUNET_SERVICE_SETI_REJECT on the listenerâs
client connection. In contrast, when accepting an incoming
request, a GNUNET_SERVICE_SETI_ACCEPT message must be
sent over the set that is supplied for the set
operation.
Initiating
Intersection Operations
Operations with remote peers are initiated by sending a
GNUNET_SERVICE_SETI_EVALUATE message to the service.
The client connection that this message is sent by
determines the set to use.
Modifying
Intersection Sets
Sets are modified with the GNUNET_SERVICE_SETI_ADD
message.
Intersection
Results and Operation Status
The service notifies the client of result elements and
success/failure of a set operation with the
GNUNET_SERVICE_SETI_RESULT message.
The SETI
Intersection Peer−to−Peer Protocol
The intersection protocol operates over CADET and starts
with a GNUNET_MESSAGE_TYPE_SETI_P2P_OPERATION_REQUEST being
sent by the peer initiating the operation to the peer
listening for inbound requests. It includes the number of
elements of the initiating peer, which is used to decide
which side will send a Bloom filter first.
The listening peer checks if the operation type and application identifier are acceptable for its current state. If not, it responds with a GNUNET_MESSAGE_TYPE_SETI_RESULT and a status of GNUNET_SETI_STATUS_FAILURE (and terminates the CADET channel).
If the application accepts the request, the listener sends back a GNUNET_MESSAGE_TYPE_SETI_P2P_ELEMENT_INFO if it has more elements in the set than the client. Otherwise, it immediately starts with the Bloom filter exchange. If the initiator receives a GNUNET_MESSAGE_TYPE_SETI_P2P_ELEMENT_INFO response, it beings the Bloom filter exchange, unless the set size is indicated to be zero, in which case the intersection is considered finished after just the initial handshake.
The Bloom
filter exchange in SETI
In this phase, each peer transmits a Bloom filter over the
remaining keys of the local set to the other peer using a
GNUNET_MESSAGE_TYPE_SETI_P2P_BF message. This message
additionally includes the number of elements left in the
senderâs set, as well as the XOR over all of the keys
in that set.
The number of bits âkâ set per element in the Bloom filter is calculated based on the relative size of the two sets. Furthermore, the size of the Bloom filter is calculated based on âkâ and the number of elements in the set to maximize the amount of data filtered per byte transmitted on the wire (while avoiding an excessively high number of iterations).
The receiver of the message removes all elements from its local set that do not pass the Bloom filter test. It then checks if the set size of the sender and the XOR over the keys match what is left of its own set. If they do, it sends a GNUNET_MESSAGE_TYPE_SETI_P2P_DONE back to indicate that the latest set is the final result. Otherwise, the receiver starts another Bloom filter exchange, except this time as the sender.
Intersection
Salt
Bloom filter operations are probabilistic: With some
non−zero probability the test may incorrectly say an
element is in the set, even though it is not.
To mitigate this problem, the intersection protocol iterates exchanging Bloom filters using a different random 32−bit salt in each iteration (the salt is also included in the message). With different salts, set operations may fail for different elements. Merging the results from the executions, the probability of failure drops to zero.
The iterations terminate once both peers have established that they have sets of the same size, and where the XOR over all keys computes the same 512−bit value (leaving a failure probability of 2−511).
SETU â
Peer to peer set unions
The SETU service implements efficient set union operations
between two peers over a CADET tunnel. Elements of a set
consist of an element type and arbitrary binary
data. The size of an elementâs data is limited
to around 62 KB.
Union
Sets
Sets created by a local client can be modified (by adding
additional elements) and reused for multiple operations. If
elements are to be removed, a fresh set must be created by
the client.
Set Union
Modifications
Even when set operations are active, one can add elements to
a set. However, these changes will only be visible to
operations that have been created after the changes have
taken place. That is, every set operation only sees a
snapshot of the set from the time the operation was started.
This mechanism is not implemented by copying the
whole set, but by attaching generation information to
each element and operation.
Set Union
Operations
Set operations can be started in two ways: Either by
accepting an operation request from a remote peer, or by
requesting a set operation from a remote peer. Set
operations are uniquely identified by the involved
peers, an application id and the operation
type.
The client is notified of incoming set operations by set listeners. A set listener listens for incoming operations of a specific operation type and application id. Once notified of an incoming set request, the client can accept the set request (providing a local set for the operation) or reject it.
Union Result
Elements
The SET service has three result modes that determine
how an operationâs result set is delivered to the
client:
• |
Locally added Elements. Elements that are in the union but not already in the local peerâs set are returned. | ||
• |
Remote added Elements. Additionally, notify the client if the remote peer lacked some elements and thus also return to the local client those elements that we are sending to the remote peer to be added to its union. Obtaining these elements requires setting the GNUNET_SETU_OPTION_SYMMETRIC option. |
libgnunetsetu
Union Set API
New sets are created with GNUNET_SETU_create. Only
the local peerâs configuration (as each set has its
own client connection) must be provided. The set exists
until either the client calls GNUNET_SETU_destroy or
the clientâs connection to the service is disrupted.
In the latter case, the client is notified by the return
value of functions dealing with sets. This return value must
always be checked.
Elements are added with GNUNET_SETU_add_element.
Union
Listeners
Listeners are created with GNUNET_SETU_listen. Each
time time a remote peer suggests a set operation with an
application id and operation type matching a listener, the
listenerâs callback is invoked. The client then must
synchronously call either GNUNET_SETU_accept or
GNUNET_SETU_reject. Note that the operation will not
be started until the client calls GNUNET_SETU_commit
(see Section "Supplying a Set").
Union
Operations
Operations to be initiated by the local peer are created
with GNUNET_SETU_prepare. Note that the operation
will not be started until the client calls
GNUNET_SETU_commit (see Section "Supplying a
Set").
Supplying a
Set for Union
To create symmetry between the two ways of starting a set
operation (accepting and initiating it), the operation
handles returned by GNUNET_SETU_accept and
GNUNET_SETU_prepare do not yet have a set to operate
on, thus they can not do any work yet.
The client must call GNUNET_SETU_commit to specify a set to use for an operation. GNUNET_SETU_commit may only be called once per set operation.
The Union
Result Callback
Clients must specify both a result mode and a result
callback with GNUNET_SETU_accept and
GNUNET_SETU_prepare. The result callback with a
status indicating either that an element was received,
transmitted to the other peer (if this information was
requested), or if the operation failed or ultimately
succeeded.
The SETU
Client−Service Protocol
Creating Union Sets
For each set of a client, there exists a client connection
to the service. Sets are created by sending the
GNUNET_SERVICE_SETU_CREATE message over a new client
connection. Multiple operations for one set are multiplexed
over one client connection, using a request id supplied by
the client.
Listeners
for Union
Each listener also requires a separate client connection. By
sending the GNUNET_SERVICE_SETU_LISTEN message, the
client notifies the service of the application id and
operation type it is interested in. A client rejects an
incoming request by sending
GNUNET_SERVICE_SETU_REJECT on the listenerâs
client connection. In contrast, when accepting an incoming
request, a GNUNET_SERVICE_SETU_ACCEPT message must be
sent over the set that is supplied for the set
operation.
Initiating
Union Operations
Operations with remote peers are initiated by sending a
GNUNET_SERVICE_SETU_EVALUATE message to the service.
The client connection that this message is sent by
determines the set to use.
Modifying
Union Sets
Sets are modified with the GNUNET_SERVICE_SETU_ADD
message.
Union
Results and Operation Status
The service notifies the client of result elements and
success/failure of a set operation with the
GNUNET_SERVICE_SETU_RESULT message.
The SETU
Union Peer−to−Peer Protocol
The SET union protocol is based on Eppsteinâs
efficient set reconciliation without prior context. You
should read this paper first if you want to understand the
protocol.
Todo
Link to Eppsteinâs paper!
The union protocol operates over CADET and starts with a GNUNET_MESSAGE_TYPE_SETU_P2P_OPERATION_REQUEST being sent by the peer initiating the operation to the peer listening for inbound requests. It includes the number of elements of the initiating peer, which is currently not used.
The listening peer checks if the operation type and application identifier are acceptable for its current state. If not, it responds with a GNUNET_MESSAGE_TYPE_SETU_RESULT and a status of GNUNET_SETU_STATUS_FAILURE (and terminates the CADET channel).
If the application accepts the request, it sends back a strata estimator using a message of type GNUNET_MESSAGE_TYPE_SETU_P2P_SE. The initiator evaluates the strata estimator and initiates the exchange of invertible Bloom filters, sending a GNUNET_MESSAGE_TYPE_SETU_P2P_IBF.
During the IBF exchange, if the receiver cannot invert the Bloom filter or detects a cycle, it sends a larger IBF in response (up to a defined maximum limit; if that limit is reached, the operation fails). Elements decoded while processing the IBF are transmitted to the other peer using GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENTS, or requested from the other peer using GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENT_REQUESTS messages, depending on the sign observed during decoding of the IBF. Peers respond to a GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENT_REQUESTS message with the respective element in a GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENTS message. If the IBF fully decodes, the peer responds with a GNUNET_MESSAGE_TYPE_SETU_P2P_DONE message instead of another GNUNET_MESSAGE_TYPE_SETU_P2P_IBF.
All Bloom filter operations use a salt to mingle keys before hashing them into buckets, such that future iterations have a fresh chance of succeeding if they failed due to collisions before.
VPN and VPN
Support
GNS and GNS Support
The GNU Name System is a secure and
censorship−resistant alternative to the Domain Name
System (DNS) in common use for resolving domain names.
GNS â
the GNU Name System
The GNU Name System (GNS) is a decentralized database that
enables users to securely resolve names to values. Names can
be used to identify other users (for example, in social
networking), or network services (for example, VPN services
running at a peer in GNUnet, or purely IP−based
services on the Internet). Users interact with GNS by typing
in a hostname that ends in a top−level domain that is
configured in the âGNSâ section, matches an
identity of the user or ends in a Base32−encoded
public key.
Videos giving an overview of most of the GNS and the motivations behind it is available here and here. The remainder of this chapter targets developers that are familiar with high level concepts of GNS as presented in these talks.
Todo
Link to videos and GNS talks?
GNS−aware applications should use the GNS resolver to obtain the respective records that are stored under that name in GNS. Each record consists of a type, value, expiration time and flags.
The type specifies the format of the value. Types below 65536 correspond to DNS record types, larger values are used for GNS−specific records. Applications can define new GNS record types by reserving a number and implementing a plugin (which mostly needs to convert the binary value representation to a human−readable text format and vice−versa). The expiration time specifies how long the record is to be valid. The GNS API ensures that applications are only given non−expired values. The flags are typically irrelevant for applications, as GNS uses them internally to control visibility and validity of records.
Records are stored along with a signature. The signature is generated using the private key of the authoritative zone. This allows any GNS resolver to verify the correctness of a name−value mapping.
Internally, GNS uses the NAMECACHE to cache information obtained from other users, the NAMESTORE to store information specific to the local users, and the DHT to exchange data between users. A plugin API is used to enable applications to define new GNS record types.
libgnunetgns
The GNS API itself is extremely simple. Clients first
connect to the GNS service using GNUNET_GNS_connect.
They can then perform lookups using GNUNET_GNS_lookup
or cancel pending lookups using
GNUNET_GNS_lookup_cancel. Once finished, clients
disconnect using GNUNET_GNS_disconnect.
Looking up
records
GNUNET_GNS_lookup takes a number of arguments:
handle This is simply the GNS connection handle from
GNUNET_GNS_connect.
name The client needs to specify the name to
be resolved. This can be any valid DNS or GNS hostname.
zone The client
needs to specify the public key of the GNS zone against which the resolution should be done. Note that a key must be provided, the client should look up plausible values using its configuration, the identity service and by attempting to interpret the TLD as a base32−encoded public key.
type This is the desired GNS or DNS record type
to look for. While all records for the given name will be returned, this can be important if the client wants to resolve record types that themselves delegate resolution, such as CNAME, PKEY or GNS2DNS. Resolving a record of any of these types will only work if the respective record type is specified in the request, as the GNS resolver will otherwise follow the delegation and return the records from the respective destination, instead of the delegating record.
only_cached This argument should typically be set to
GNUNET_NO. Setting it to GNUNET_YES disables resolution via the overlay network.
shorten_zone_key If GNS encounters new names during resolution,
their respective zones can automatically be learned and added to the "shorten zone". If this is desired, clients must pass the private key of the shorten zone. If NULL is passed, shortening is disabled.
proc This argument identifies
the function to call with the result. It is given proc_cls, the number of records found (possibly zero) and the array of the records as arguments. proc will only be called once. After proc,> has been called, the lookup must no longer be canceled.
proc_cls The closure for proc.
Accessing
the records
The libgnunetgnsrecord library provides an API to
manipulate the GNS record array that is given to proc. In
particular, it offers functions such as converting record
values to human−readable strings (and back). However,
most libgnunetgnsrecord functions are not interesting
to GNS client applications.
For DNS records, the libgnunetdnsparser library provides functions for parsing (and serializing) common types of DNS records.
Creating
records
Creating GNS records is typically done by building the
respective record information (possibly with the help of
libgnunetgnsrecord and libgnunetdnsparser) and
then using the libgnunetnamestore to publish the
information. The GNS API is not involved in this
operation.
Future
work
In the future, we want to expand libgnunetgns to
allow applications to observe shortening operations
performed during GNS resolution, for example so that users
can receive visual feedback when this happens.
libgnunetgnsrecord
The libgnunetgnsrecord library is used to manipulate
GNS records (in plaintext or in their encrypted format).
Applications mostly interact with libgnunetgnsrecord
by using the functions to convert GNS record values to
strings or vice−versa, or to lookup a GNS record type
number by name (or vice−versa). The library also
provides various other functions that are mostly used
internally within GNS, such as converting keys to names,
checking for expiration, encrypting GNS records to GNS
blocks, verifying GNS block signatures and decrypting GNS
records from GNS blocks.
We will now discuss the four commonly used functions of the API. libgnunetgnsrecord does not perform these operations itself, but instead uses plugins to perform the operation. GNUnet includes plugins to support common DNS record types as well as standard GNS record types.
Value
handling
GNUNET_GNSRECORD_value_to_string can be used to convert
the (binary) representation of a GNS record value to a human
readable, 0−terminated UTF−8 string. NULL is
returned if the specified record type is not supported by
any available plugin.
GNUNET_GNSRECORD_string_to_value can be used to try to convert a human readable string to the respective (binary) representation of a GNS record value.
Type
handling
GNUNET_GNSRECORD_typename_to_number can be used to
obtain the numeric value associated with a given typename.
For example, given the typename "A" (for DNS A
reocrds), the function will return the number 1. A list of
common DNS record types is here. Note that not all
DNS record types are supported by GNUnet GNSRECORD plugins
at this time.
GNUNET_GNSRECORD_number_to_typename can be used to obtain the typename associated with a given numeric value. For example, given the type number 1, the function will return the typename "A".
GNS
plugins
Adding a new GNS record type typically involves writing (or
extending) a GNSRECORD plugin. The plugin needs to implement
the gnunet_gnsrecord_plugin.h API which provides
basic functions that are needed by GNSRECORD to convert
typenames and values of the respective record type to
strings (and back). These gnsrecord plugins are typically
implemented within their respective subsystems. Examples for
such plugins can be found in the GNSRECORD, GNS and
CONVERSATION subsystems.
The libgnunetgnsrecord library is then used to locate, load and query the appropriate gnsrecord plugin. Which plugin is appropriate is determined by the record type (which is just a 32−bit integer). The libgnunetgnsrecord library loads all block plugins that are installed at the local peer and forwards the application request to the plugins. If the record type is not supported by the plugin, it should simply return an error code.
The central functions of the block APIs (plugin and main library) are the same four functions for converting between values and strings, and typenames and numbers documented in the previous subsection.
The GNS
Client−Service Protocol
The GNS client−service protocol consists of two simple
messages, the LOOKUP message and the
LOOKUP_RESULT. Each LOOKUP message contains a
unique 32−bit identifier, which will be included in
the corresponding response. Thus, clients can send many
lookup requests in parallel and receive responses
out−of−order. A LOOKUP request also
includes the public key of the GNS zone, the desired record
type and fields specifying whether shortening is enabled or
networking is disabled. Finally, the LOOKUP message
includes the name to be resolved.
The response includes the number of records and the records themselves in the format created by GNUNET_GNSRECORD_records_serialize. They can thus be deserialized using GNUNET_GNSRECORD_records_deserialize.
Hijacking
the DNS−Traffic using
gnunet−service−dns
This section documents how the
gnunet−service−dns (and the
gnunet−helper−dns) intercepts DNS queries from
the local system. This is merely one method for how we can
obtain GNS queries. It is also possible to change
resolv.conf to point to a machine running
gnunet−dns2gns or to modify libcâs name
system switch (NSS) configuration to include a GNS
resolution plugin. The method described in this chapter is
more of a last−ditch catch−all approach.
gnunet−service−dns enables intercepting DNS traffic using policy based routing. We MARK every outgoing DNS−packet if it was not sent by our application. Using a second routing table in the Linux kernel these marked packets are then routed through our virtual network interface and can thus be captured unchanged.
Our application then reads the query and decides how to handle it. If the query can be addressed via GNS, it is passed to gnunet−service−gns and resolved internally using GNS. In the future, a reverse query for an address of the configured virtual network could be answered with records kept about previous forward queries. Queries that are not hijacked by some application using the DNS service will be sent to the original recipient. The answer to the query will always be sent back through the virtual interface with the original nameserver as source address.
Network
Setup Details
The DNS interceptor adds the following rules to the Linux
kernel:
iptables −t mangle −I OUTPUT 1 −p udp −−sport $LOCALPORT −−dport 53 \ −j ACCEPT iptables −t mangle −I OUTPUT 2 −p udp −−dport 53 −j MARK \ −−set−mark 3 ip rule add fwmark 3 table2 ip route add default via \ $VIRTUALDNS table2
Todo
FIXME: Rewrite to reflect display which is no longer content
by line due to the < 74 characters limit.
Line 1 makes sure that all packets coming from a port our application opened beforehand ($LOCALPORT) will be routed normally. Line 2 marks every other packet to a DNS−Server with mark 3 (chosen arbitrarily). The third line adds a routing policy based on this mark 3 via the routing table.
Importing
DNS Zones into GNS
This section discusses the challenges and problems faced
when writing the Ascension tool. It also takes a look at
possible improvements in the future.
Consider the following diagram that shows the workflow of Ascension:
[image: ascension] [image]
Further the interaction between components of GNUnet are shown in the diagram below:
DNS Conversion .. _Conversions−between−DNS−and−GNS:
Conversions
between DNS and GNS
The differences between the two name systems lies in the
details and is not always transparent. For instance an SRV
record is converted to a BOX record which is unique to
GNS.
This is done by converting to a BOX record from an existing SRV record:
# SRV # _service._proto.name. TTL class SRV priority weight port target _sip._tcp.example.com. 14000 IN SRV 0 0 5060 www.example.com. # BOX # TTL BOX flags port protocol recordtype priority weight port target 14000 BOX n 5060 6 33 0 0 5060 www.example.com
Other records that need to undergo such transformation is the MX record type, as well as the SOA record type.
Transformation of a SOA record into GNS works as described in the following example. Very important to note are the rname and mname keys.
# BIND syntax for a clean SOA record IN SOA master.example.com. hostmaster.example.com. ( 2017030300 ; serial 3600 ; refresh 1800 ; retry 604800 ; expire 600 ) ; ttl # Recordline for adding the record $ gnunet−namestore −z example.com −a −n −t SOA −V \ rname=master.example.com mname=hostmaster.example.com \ 2017030300,3600,1800,604800,600 −e 7200s
The transformation of MX records is done in a simple way.
# mail.example.com. 3600 IN MX 10 mail.example.com. $ gnunet−namestore −z example.com −n mail −R 3600 MX n 10,mail
Finally, one of the biggest struggling points were the NS records that are found in top level domain zones. The intended behaviour for those is to add GNS2DNS records for those so that gnunet−gns can resolve records for those domains on its own. Those require the values from DNS GLUE records, provided they are within the same zone.
The following two examples show one record with a GLUE record and the other one does not have a GLUE record. This takes place in the âcomâ TLD.
# ns1.example.com 86400 IN A 127.0.0.1 # example.com 86400 IN NS ns1.example.com. $ gnunet−namestore −z com −n example −R 86400 GNS2DNS n \ [email protected] # example.com 86400 IN NS ns1.example.org. $ gnunet−namestore −z com −n example −R 86400 GNS2DNS n \ [email protected]
As you can see, one of the GNS2DNS records has an IP address listed and the other one a DNS name. For the first one there is a GLUE record to do the translation directly and the second one will issue another DNS query to figure out the IP of ns1.example.org.
A solution was found by creating a hierarchical zone structure in GNS and linking the zones using PKEY records to one another. This allows the resolution of the name servers to work within GNS while not taking control over unwanted zones.
Currently the following record types are supported:
• |
A |
|||
• |
AAAA |
|||
• |
CNAME |
|||
• |
MX |
|||
• |
NS |
|||
• |
SRV |
|||
• |
TXT |
This is not due to technical limitations but rather a practical ones. The problem occurs with DNSSEC enabled DNS zones. As records within those zones are signed periodically, and every new signature is an update to the zone, there are many revisions of zones. This results in a problem with bigger zones as there are lots of records that have been signed again but no major changes. Also trying to add records that are unknown that require a different format take time as they cause a CLI call of the namestore. Furthermore certain record types need transformation into a GNS compatible format which, depending on the record type, takes more time.
Further a blacklist was added to drop for instance DNSSEC related records. Also if a record type is neither in the white list nor the blacklist it is considered as a loss of data and a message is shown to the user. This helps with transparency and also with contributing, as the not supported record types can then be added accordingly.
DNS Zone
Size
Another very big problem exists with very large zones. When
migrating a small zone the delay between adding of records
and their expiry is negligible. However when working with
big zones that easily have more than a few million records
this delay becomes a problem.
Records will start to expire well before the zone has finished migrating. This is usually not a problem but can cause a high CPU load when a peer is restarted and the records have expired.
A good solution has not been found yet. One of the idea that floated around was that the records should be added with the s (shadow) flag to keep the records resolvable even if they expired. However this would introduce the problem of how to detect if a record has been removed from the zone and would require deletion of said record(s).
Another problem that still persists is how to refresh records. Expired records are still displayed when calling gnunet−namestore but do not resolve with gnunet−gns. Zonemaster will sign the expired records again and make sure that the records are still valid. With a recent change this was fixed as gnunet−gns to improve the suffix lookup which allows for a fast lookup even with thousands of local egos.
Currently the pace of adding records in general is around 10 records per second. Crypto is the upper limit for adding of records. The performance of your machine can be tested with the perf_crypto_* tools. There is still a big discrepancy between the pace of Ascension and the theoretical limit.
A performance metric for measuring improvements has not yet been implemented in Ascension.
Performance
The performance when migrating a zone using the Ascension
tool is limited by a handful of factors. First of all
ascension is written in Python3 and calls the CLI tools of
GNUnet. This is comparable to a fork and exec call which
costs a few CPU cycles. Furthermore all the records that are
added to the same label are signed using the zones private
key. This signing operation is very resource heavy and was
optimized during development by adding the
â−Râ (Recordline) option to
gnunet−namestore which allows to specify multiple
records using the CLI tool. Assuming that in a TLD zone
every domain has at least two name servers this halves the
amount of signatures needed.
Another improvement that could be made is with the addition of multiple threads or using asynchronous subprocesses when opening the GNUnet CLI tools. This could be implemented by simply creating more workers in the program but performance improvements were not tested.
Ascension was tested using different hardware and database backends. Performance differences between SQLite and postgresql are marginal and almost non existent. What did make a huge impact on record adding performance was the storage medium. On a traditional mechanical hard drive adding of records were slow compared to a solid state disk.
In conclusion there are many bottlenecks still around in the program, namely the single threaded implementation and inefficient, sequential calls of gnunet−namestore. In the future a solution that uses the C API would be cleaner and better.
Registering
names using the FCFS daemon
This section describes FCFSD, a daemon used to associate
names with PKEY records following a âFirst Come, First
Servedâ policy. This policy means that a certain name
can not be registered again if someone registered it
already.
The daemon can be started by using gnunet−namestore−fcfsd, which will start a simple HTTP server on localhost, using a port specified by the HTTPORT value in its configuration.
Communication is performed by sending GET or POST requests to specific paths (âendpointsâ), as described in the following sections.
The daemon will always respond with data structured using the JSON format. The fields to be expected will be listed for each endpoint.
The only exceptions are for the ârootâ endpoint (i.e. /) which will return a HTML document, and two other HTML documents which will be served when certain errors are encountered, like when requesting an unknown endpoint.
FCFSD GET requests .. _Obtaining−information−from−the−daemon:
Obtaining
information from the daemon
To query the daemon, a GET request must be sent to these
endpoints, placing parameters in the address as per the HTTP
specification, like so:
GET /endpoint?param1=value¶m2=value
Each endpoint
will be described using its name (/endpoint in the
example above), followed by the name of each parameter (like
param1 and param2.)
Endpoint: /search name
This endpoint is used to query about the state of <name>, that is, whether it is available for registration or not.
The response JSON will contain two fields:
• |
error |
|||
• |
free |
error can be either the string "true" or the string "false": when "true", it means there was an error within the daemon and the name could not be searched at all.
free can be either the string "true" or the string "false": when "true", the requested name can be registered.
FCFSD POST requests .. _Submitting−data−to−the−daemon:
Submitting
data to the daemon
To send data to the daemon, a POST request must be sent to
these endpoints, placing the data to submit in the body of
the request, structured using the JSON format, like so:
POST /endpoint Content−Type: application/json ... {"param1": value1, "param2": value2, ...}
Each endpoint
will be described using its name (/endpoint in the
example above), followed by the name of each JSON field
(like param1 and param2.)
Endpoint: /register name key
This endpoint is used to register a new association between <name> and <key>.
For this operation to succeed, both <NAME> and <KEY> must not be registered already.
The response JSON will contain two fields:
• |
error |
|||
• |
message |
error can be either the string "true" or the string "false": when "true", it means the name could not be registered. Clients can get the reason of the failure from the HTTP response code or from the message field.
message is a string which can be used by clients to let users know the result of the operation. It might be localized to the daemon operatorâs locale.
Customizing
the HTML output
In some situations, the daemon will serve HTML documents
instead of JSON values. It is possible to configure the
daemon to serve custom documents instead of the ones
provided with GNUnet, by setting the HTMLDIR value in
its configuration to a directory path.
Within the provided path, the daemon will search for these three files:
• |
fcfsd−index.html |
|||
• |
fcfsd−notfound.html |
|||
• |
fcfsd−forbidden.html |
The fcfsd−index.html file is the daemonâs âhomepageâ: operators might want to provide information about the service here, or provide a form with which it is possible to register a name.
The fcfsd−notfound.html file is used primarily to let users know they tried to access an unknown endpoint.
The fcfsd−forbidden.html file is served to users when they try to access an endpoint they should not access. For example, sending an invalid request might result in this page being served.
NAMECACHE
â DHT caching of GNS results
The NAMECACHE subsystem is responsible for caching
(encrypted) resolution results of the GNU Name System (GNS).
GNS makes zone information available to other users via the
DHT. However, as accessing the DHT for every lookup is
expensive (and as the DHTâs local cache is lost
whenever the peer is restarted), GNS uses the NAMECACHE as a
more persistent cache for DHT lookups. Thus, instead of
always looking up every name in the DHT, GNS first checks if
the result is already available locally in the NAMECACHE.
Only if there is no result in the NAMECACHE, GNS queries the
DHT. The NAMECACHE stores data in the same (encrypted)
format as the DHT. It thus makes no sense to iterate over
all items in the NAMECACHE â the NAMECACHE does not
have a way to provide the keys required to decrypt the
entries.
Blocks in the NAMECACHE share the same expiration mechanism as blocks in the DHT â the block expires wheneever any of the records in the (encrypted) block expires. The expiration time of the block is the only information stored in plaintext. The NAMECACHE service internally performs all of the required work to expire blocks, clients do not have to worry about this. Also, given that NAMECACHE stores only GNS blocks that local users requested, there is no configuration option to limit the size of the NAMECACHE. It is assumed to be always small enough (a few MB) to fit on the drive.
The NAMECACHE supports the use of different database backends via a plugin API.
libgnunetnamecache
The NAMECACHE API consists of five simple functions. First,
there is GNUNET_NAMECACHE_connect to connect to the
NAMECACHE service. This returns the handle required for all
other operations on the NAMECACHE. Using
GNUNET_NAMECACHE_block_cache clients can insert a
block into the cache. GNUNET_NAMECACHE_lookup_block
can be used to lookup blocks that were stored in the
NAMECACHE. Both operations can be canceled using
GNUNET_NAMECACHE_cancel. Note that canceling a
GNUNET_NAMECACHE_block_cache operation can result in
the block being stored in the NAMECACHE â or not.
Cancellation primarily ensures that the continuation
function with the result of the operation will no longer be
invoked. Finally, GNUNET_NAMECACHE_disconnect closes
the connection to the NAMECACHE.
The maximum size of a block that can be stored in the NAMECACHE is GNUNET_NAMECACHE_MAX_VALUE_SIZE, which is defined to be 63 kB.
The
NAMECACHE Client−Service Protocol
All messages in the NAMECACHE IPC protocol start with the
struct GNUNET_NAMECACHE_Header which adds a request
ID (32−bit integer) to the standard message header.
The request ID is used to match requests with the respective
responses from the NAMECACHE, as they are allowed to happen
out−of−order.
Lookup
The struct LookupBlockMessage is used to lookup a
block stored in the cache. It contains the query hash. The
NAMECACHE always responds with a struct
LookupBlockResponseMessage. If the NAMECACHE has no
response, it sets the expiration time in the response to
zero. Otherwise, the response is expected to contain the
expiration time, the ECDSA signature, the derived key and
the (variable−size) encrypted data of the block.
Store
The struct BlockCacheMessage is used to cache a block
in the NAMECACHE. It has the same structure as the struct
LookupBlockResponseMessage. The service responds with a
struct BlockCacheResponseMessage which contains the
result of the operation (success or failure). In the future,
we might want to make it possible to provide an error
message as well.
The
NAMECACHE Plugin API
The NAMECACHE plugin API consists of two functions,
cache_block to store a block in the database, and
lookup_block to lookup a block in the database.
Lookup2
The lookup_block function is expected to return at
most one block to the iterator, and return GNUNET_NO
if there were no non−expired results. If there are
multiple non−expired results in the cache, the lookup
is supposed to return the result with the largest expiration
time.
Store2
The cache_block function is expected to try to store
the block in the database, and return GNUNET_SYSERR
if this was not possible for any reason. Furthermore,
cache_block is expected to implicitly perform cache
maintenance and purge blocks from the cache that have
expired. Note that cache_block might encounter the
case where the database already has another block stored
under the same key. In this case, the plugin must ensure
that the block with the larger expiration time is preserved.
Obviously, this can done either by simply adding new blocks
and selecting for the most recent expiration time during
lookup, or by checking which block is more recent during the
store operation.
NAMESTORE
â Storage of local GNS zones
The NAMESTORE subsystem provides persistent storage for
local GNS zone information. All local GNS zone information
are managed by NAMESTORE. It provides both the functionality
to administer local GNS information (e.g. delete and add
records) as well as to retrieve GNS information (e.g to list
name information in a client). NAMESTORE does only manage
the persistent storage of zone information belonging to the
user running the service: GNS information from other users
obtained from the DHT are stored by the NAMECACHE
subsystem.
NAMESTORE uses a plugin−based database backend to store GNS information with good performance. Here sqlite, MySQL and PostgreSQL are supported database backends. NAMESTORE clients interact with the IDENTITY subsystem to obtain cryptographic information about zones based on egos as described with the IDENTITY subsystem, but internally NAMESTORE refers to zones using the ECDSA private key. In addition, it collaborates with the NAMECACHE subsystem and stores zone information when local information are modified in the GNS cache to increase look−up performance for local information.
NAMESTORE provides functionality to look−up and store records, to iterate over a specific or all zones and to monitor zones for changes. NAMESTORE functionality can be accessed using the NAMESTORE api or the NAMESTORE command line tool.
libgnunetnamestore
To interact with NAMESTORE clients first connect to the
NAMESTORE service using the GNUNET_NAMESTORE_connect
passing a configuration handle. As a result they obtain a
NAMESTORE handle, they can use for operations, or NULL is
returned if the connection failed.
To disconnect from NAMESTORE, clients use GNUNET_NAMESTORE_disconnect and specify the handle to disconnect.
NAMESTORE internally uses the ECDSA private key to refer to zones. These private keys can be obtained from the IDENTITY subsystem. Here egos can be used to refer to zones or the default ego assigned to the GNS subsystem can be used to obtained the master zoneâs private key.
Editing Zone
Information
NAMESTORE provides functions to lookup records stored under
a label in a zone and to store records under a label in a
zone.
To store (and delete) records, the client uses the GNUNET_NAMESTORE_records_store function and has to provide namestore handle to use, the private key of the zone, the label to store the records under, the records and number of records plus an callback function. After the operation is performed NAMESTORE will call the provided callback function with the result GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate), GNUNET_NO if content was already there or not found GNUNET_YES (or other positive value) on success plus an additional error message.
Records are deleted by using the store command with 0 records to store. It is important to note, that records are not merged when records exist with the label. So a client has first to retrieve records, merge with existing records and then store the result.
To perform a lookup operation, the client uses the GNUNET_NAMESTORE_records_store function. Here it has to pass the namestore handle, the private key of the zone and the label. It also has to provide a callback function which will be called with the result of the lookup operation: the zone for the records, the label, and the records including the number of records included.
A special operation is used to set the preferred nickname for a zone. This nickname is stored with the zone and is automatically merged with all labels and records stored in a zone. Here the client uses the GNUNET_NAMESTORE_set_nick function and passes the private key of the zone, the nickname as string plus a the callback with the result of the operation.
Iterating
Zone Information
A client can iterate over all information in a zone or all
zones managed by NAMESTORE. Here a client uses the
GNUNET_NAMESTORE_zone_iteration_start function and
passes the namestore handle, the zone to iterate over and a
callback function to call with the result. To iterate over
all the zones, it is possible to pass NULL for the zone. A
GNUNET_NAMESTORE_ZoneIterator handle is returned to
be used to continue iteration.
NAMESTORE calls the callback for every result and expects the client to call GNUNET_NAMESTORE_zone_iterator_next to continue to iterate or GNUNET_NAMESTORE_zone_iterator_stop to interrupt the iteration. When NAMESTORE reached the last item it will call the callback with a NULL value to indicate.
Monitoring
Zone Information
Clients can also monitor zones to be notified about changes.
Here the clients uses the
GNUNET_NAMESTORE_zone_monitor_start function and
passes the private key of the zone and and a callback
function to call with updates for a zone. The client can
specify to obtain zone information first by iterating over
the zone and specify a synchronization callback to be called
when the client and the namestore are synced.
On an update, NAMESTORE will call the callback with the private key of the zone, the label and the records and their number.
To stop monitoring, the client calls GNUNET_NAMESTORE_zone_monitor_stop and passes the handle obtained from the function to start the monitoring.
Applications
FS â File sharing over GNUnet
This chapter describes the details of how the
file−sharing service works. As with all services, it
is split into an API (libgnunetfs), the service process
(gnunet−service−fs) and user interface(s). The
file−sharing service uses the datastore service to
store blocks and the DHT (and indirectly datacache) for
lookups for non−anonymous file−sharing.
Furthermore, the file−sharing service uses the block
library (and the block fs plugin) for validation of DHT
operations.
In contrast to many other services, libgnunetfs is rather complex since the client library includes a large number of high−level abstractions; this is necessary since the FS service itself largely only operates on the block level. The FS library is responsible for providing a file−based abstraction to applications, including directories, meta data, keyword search, verification, and so on.
The method used by GNUnet to break large files into blocks and to use keyword search is called the "Encoding for Censorship Resistant Sharing" (ECRS). ECRS is largely implemented in the fs library; block validation is also reflected in the block FS plugin and the FS service. ECRS on−demand encoding is implemented in the FS service.
NOTE:
The documentation in this chapter is quite incomplete.
ECRS â
Encoding for Censorship−Resistant Sharing
When GNUnet shares files, it uses a content encoding that is
called ECRS, the Encoding for Censorship−Resistant
Sharing. Most of ECRS is described in the (so far
unpublished) research paper attached to this page. ECRS
obsoletes the previous ESED and ESED II encodings which were
used in GNUnet before version 0.7.0. The rest of this page
assumes that the reader is familiar with the attached paper.
What follows is a description of some minor extensions that
GNUnet makes over what is described in the paper. The reason
why these extensions are not in the paper is that we felt
that they were obvious or trivial extensions to the original
scheme and thus did not warrant space in the research
report.
Todo
Find missing link to file system paper.
Namespace
Advertisements
Todo
FIXME: all zeroses −> ?
An SBlock with identifier all zeros is a signed advertisement for a namespace. This special SBlock contains metadata describing the content of the namespace. Instead of the name of the identifier for a potential update, it contains the identifier for the root of the namespace. The URI should always be empty. The SBlock is signed with the content providerâs RSA private key (just like any other SBlock). Peers can search for SBlocks in order to find out more about a namespace.
KSBlocks
GNUnet implements KSBlocks which are KBlocks
that, instead of encrypting a CHK and metadata, encrypt an
SBlock instead. In other words, KSBlocks
enable GNUnet to find SBlocks using the global
keyword search. Usually the encrypted SBlock is a
namespace advertisement. The rationale behind
KSBlocks and SBlocks is to enable peers to
discover namespaces via keyword searches, and, to associate
useful information with namespaces. When GNUnet finds
KSBlocks during a normal keyword search, it adds the
information to an internal list of discovered namespaces.
Users looking for interesting namespaces can then inspect
this list, reducing the need for out−of−band
discovery of namespaces. Naturally, namespaces (or more
specifically, namespace advertisements) can also be
referenced from directories, but KSBlocks should make
it easier to advertise namespaces for the owner of the
pseudonym since they eliminate the need to first create a
directory.
Collections are also advertised using KSBlocks.
File−sharing
persistence directory structure
This section documents how the file−sharing library
implements persistence of file−sharing operations and
specifically the resulting directory structure. This code is
only active if the GNUNET_FS_FLAGS_PERSISTENCE flag
was set when calling GNUNET_FS_start. In this case,
the file−sharing library will try hard to ensure that
all major operations (searching, downloading, publishing,
unindexing) are persistent, that is, can live longer than
the process itself. More specifically, an operation is
supposed to live until it is explicitly stopped.
If GNUNET_FS_stop is called before an operation has been stopped, a SUSPEND event is generated and then when the process calls GNUNET_FS_start next time, a RESUME event is generated. Additionally, even if an application crashes (segfault, SIGKILL, system crash) and hence GNUNET_FS_stop is never called and no SUSPEND events are generated, operations are still resumed (with RESUME events). This is implemented by constantly writing the current state of the file−sharing operations to disk. Specifically, the current state is always written to disk whenever anything significant changes (the exception are block−wise progress in publishing and unindexing, since those operations would be slowed down significantly and can be resumed cheaply even without detailed accounting). Note that if the process crashes (or is killed) during a serialization operation, FS does not guarantee that this specific operation is recoverable (no strict transactional semantics, again for performance reasons). However, all other unrelated operations should resume nicely.
Since we need to serialize the state continuously and want to recover as much as possible even after crashing during a serialization operation, we do not use one large file for serialization. Instead, several directories are used for the various operations. When GNUNET_FS_start executes, the master directories are scanned for files describing operations to resume. Sometimes, these operations can refer to related operations in child directories which may also be resumed at this point. Note that corrupted files are cleaned up automatically. However, dangling files in child directories (those that are not referenced by files from the master directories) are not automatically removed.
Persistence data is kept in a directory that begins with the "STATE_DIR" prefix from the configuration file (by default, "$SERVICEHOME/persistence/") followed by the name of the client as given to GNUNET_FS_start (for example, "gnunet−gtk") followed by the actual name of the master or child directory.
The names for the master directories follow the names of the operations:
• |
"search" |
|||
• |
"download" |
|||
• |
"publish" |
|||
• |
"unindex" |
Each of the master directories contains names (chosen at random) for each active top−level (master) operation. Note that a download that is associated with a search result is not a top−level operation.
In contrast to the master directories, the child directories are only consulted when another operation refers to them. For each search, a subdirectory (named after the master search synchronization file) contains the search results. Search results can have an associated download, which is then stored in the general "download−child" directory. Downloads can be recursive, in which case children are stored in subdirectories mirroring the structure of the recursive download (either starting in the master "download" directory or in the "download−child" directory depending on how the download was initiated). For publishing operations, the "publish−file" directory contains information about the individual files and directories that are part of the publication. However, this directory structure is flat and does not mirror the structure of the publishing operation. Note that unindex operations cannot have associated child operations.
IDENTITY
â Ego management
Identities of "users" in GNUnet are called egos.
Egos can be used as pseudonyms ("fake names") or
be tied to an organization (for example, "GNU") or
even the actual identity of a human. GNUnet users are
expected to have many egos. They might have one tied to
their real identity, some for organizations they manage, and
more for different domains where they want to operate under
a pseudonym.
The IDENTITY service allows users to manage their egos. The identity service manages the private keys egos of the local user; it does not manage identities of other users (public keys). Public keys for other users need names to become manageable. GNUnet uses the GNU Name System (GNS) to give names to other users and manage their public keys securely. This chapter is about the IDENTITY service, which is about the management of private keys.
On the network, an ego corresponds to an ECDSA key (over Curve25519, using RFC 6979, as required by GNS). Thus, users can perform actions under a particular ego by using (signing with) a particular private key. Other users can then confirm that the action was really performed by that ego by checking the signature against the respective public key.
The IDENTITY service allows users to associate a human−readable name with each ego. This way, users can use names that will remind them of the purpose of a particular ego. The IDENTITY service will store the respective private keys and allows applications to access key information by name. Users can change the name that is locally (!) associated with an ego. Egos can also be deleted, which means that the private key will be removed and it thus will not be possible to perform actions with that ego in the future.
Additionally, the IDENTITY subsystem can associate service functions with egos. For example, GNS requires the ego that should be used for the shorten zone. GNS will ask IDENTITY for an ego for the "gns−short" service. The IDENTITY service has a mapping of such service strings to the name of the ego that the user wants to use for this service, for example "my−short−zone−ego".
Finally, the IDENTITY API provides access to a special ego, the anonymous ego. The anonymous ego is special in that its private key is not really private, but fixed and known to everyone. Thus, anyone can perform actions as anonymous. This can be useful as with this trick, code does not have to contain a special case to distinguish between anonymous and pseudonymous egos.
libgnunetidentity
Connecting to the service
First, typical clients connect to the identity service using
GNUNET_IDENTITY_connect. This function takes a
callback as a parameter. If the given callback parameter is
non−null, it will be invoked to notify the application
about the current state of the identities in the system.
• |
First, it will be invoked on all known egos at the time of the connection. For each ego, a handle to the ego and the userâs name for the ego will be passed to the callback. Furthermore, a void ** context argument will be provided which gives the client the opportunity to associate some state with the ego. | ||
• |
Second, the callback will be invoked with NULL for the ego, the name and the context. This signals that the (initial) iteration over all egos has completed. | ||
• |
Then, the callback will be invoked whenever something changes about an ego. If an ego is renamed, the callback is invoked with the ego handle of the ego that was renamed, and the new name. If an ego is deleted, the callback is invoked with the ego handle and a name of NULL. In the deletion case, the application should also release resources stored in the context. | ||
• |
When the application destroys the connection to the identity service using GNUNET_IDENTITY_disconnect, the callback is again invoked with the ego and a name of NULL (equivalent to deletion of the egos). This should again be used to clean up the per−ego context. |
The ego handle passed to the callback remains valid until the callback is invoked with a name of NULL, so it is safe to store a reference to the egoâs handle.
Operations
on Egos
Given an ego handle, the main operations are to get its
associated private key using
GNUNET_IDENTITY_ego_get_private_key or its associated
public key using
GNUNET_IDENTITY_ego_get_public_key.
The other operations on egos are pretty straightforward. Using GNUNET_IDENTITY_create, an application can request the creation of an ego by specifying the desired name. The operation will fail if that name is already in use. Using GNUNET_IDENTITY_rename the name of an existing ego can be changed. Finally, egos can be deleted using GNUNET_IDENTITY_delete. All of these operations will trigger updates to the callback given to the GNUNET_IDENTITY_connect function of all applications that are connected with the identity service at the time. GNUNET_IDENTITY_cancel can be used to cancel the operations before the respective continuations would be called. It is not guaranteed that the operation will not be completed anyway, only the continuation will no longer be called.
The
anonymous Ego
A special way to obtain an ego handle is to call
GNUNET_IDENTITY_ego_get_anonymous, which returns an
ego for the "anonymous" user â anyone knows
and can get the private key for this user, so it is suitable
for operations that are supposed to be anonymous but require
signatures (for example, to avoid a special path in the
code). The anonymous ego is always valid and accessing it
does not require a connection to the identity service.
Convenience
API to lookup a single ego
As applications commonly simply have to lookup a single ego,
there is a convenience API to do just that. Use
GNUNET_IDENTITY_ego_lookup to lookup a single ego by
name. Note that this is the userâs name for the ego,
not the service function. The resulting ego will be returned
via a callback and will only be valid during that callback.
The operation can be canceled via
GNUNET_IDENTITY_ego_lookup_cancel (cancellation is
only legal before the callback is invoked).
Associating
egos with service functions
The GNUNET_IDENTITY_set function is used to associate
a particular ego with a service function. The name used by
the service and the ego are given as arguments. Afterwards,
the service can use its name to lookup the associated ego
using GNUNET_IDENTITY_get.
The IDENTITY
Client−Service Protocol
A client connecting to the identity service first sends a
message with type GNUNET_MESSAGE_TYPE_IDENTITY_START
to the service. After that, the client will receive
information about changes to the egos by receiving messages
of type GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE. Those
messages contain the private key of the ego and the
userâs name of the ego (or zero bytes for the name to
indicate that the ego was deleted). A special bit
end_of_list is used to indicate the end of the
initial iteration over the identity serviceâs
egos.
The client can trigger changes to the egos by sending CREATE, RENAME or DELETE messages. The CREATE message contains the private key and the desired name. The RENAME message contains the old name and the new name. The DELETE message only needs to include the name of the ego to delete. The service responds to each of these messages with a RESULT_CODE message which indicates success or error of the operation, and possibly a human−readable error message.
Finally, the client can bind the name of a service function to an ego by sending a SET_DEFAULT message with the name of the service function and the private key of the ego. Such bindings can then be resolved using a GET_DEFAULT message, which includes the name of the service function. The identity service will respond to a GET_DEFAULT request with a SET_DEFAULT message containing the respective information, or with a RESULT_CODE to indicate an error.
REVOCATION
â Ego key revocation
The REVOCATION subsystem is responsible for key revocation
of Egos. If a user learns that their private key has been
compromised or has lost it, they can use the REVOCATION
system to inform all of the other users that their private
key is no longer valid. The subsystem thus includes ways to
query for the validity of keys and to propagate revocation
messages.
Dissemination
When a revocation is performed, the revocation is first of
all disseminated by flooding the overlay network. The goal
is to reach every peer, so that when a peer needs to check
if a key has been revoked, this will be purely a local
operation where the peer looks at its local revocation list.
Flooding the network is also the most robust form of key
revocation â an adversary would have to control a
separator of the overlay graph to restrict the propagation
of the revocation message. Flooding is also very easy to
implement â peers that receive a revocation message
for a key that they have never seen before simply pass the
message to all of their neighbours.
Flooding can only distribute the revocation message to peers that are online. In order to notify peers that join the network later, the revocation service performs efficient set reconciliation over the sets of known revocation messages whenever two peers (that both support REVOCATION dissemination) connect. The SET service is used to perform this operation efficiently.
Revocation
Message Design Requirements
However, flooding is also quite costly, creating O(|E|)
messages on a network with |E| edges. Thus, revocation
messages are required to contain a
proof−of−work, the result of an expensive
computation (which, however, is cheap to verify). Only peers
that have expended the CPU time necessary to provide this
proof will be able to flood the network with the revocation
message. This ensures that an attacker cannot simply flood
the network with millions of revocation messages. The
proof−of−work required by GNUnet is set to take
days on a typical PC to compute; if the ability to quickly
revoke a key is needed, users have the option to
pre−compute revocation messages to store
off−line and use instantly after their key has
expired.
Revocation messages must also be signed by the private key that is being revoked. Thus, they can only be created while the private key is in the possession of the respective user. This is another reason to create a revocation message ahead of time and store it in a secure location.
libgnunetrevocation
The REVOCATION API consists of two parts, to query and to
issue revocations.
Querying for
revoked keys
GNUNET_REVOCATION_query is used to check if a given
ECDSA public key has been revoked. The given callback will
be invoked with the result of the check. The query can be
canceled using GNUNET_REVOCATION_query_cancel on the
return value.
Preparing
revocations
It is often desirable to create a revocation record
ahead−of−time and store it in an off−line
location to be used later in an emergency. This is
particularly true for GNUnet revocations, where performing
the revocation operation itself is computationally expensive
and thus is likely to take some time. Thus, if users want
the ability to perform revocations quickly in an emergency,
they must pre−compute the revocation message. The
revocation API enables this with two functions that are used
to compute the revocation message, but not trigger the
actual revocation operation.
GNUNET_REVOCATION_check_pow should be used to calculate the proof−of−work required in the revocation message. This function takes the public key, the required number of bits for the proof of work (which in GNUnet is a network−wide constant) and finally a proof−of−work number as arguments. The function then checks if the given proof−of−work number is a valid proof of work for the given public key. Clients preparing a revocation are expected to call this function repeatedly (typically with a monotonically increasing sequence of numbers of the proof−of−work number) until a given number satisfies the check. That number should then be saved for later use in the revocation operation.
GNUNET_REVOCATION_sign_revocation is used to generate the signature that is required in a revocation message. It takes the private key that (possibly in the future) is to be revoked and returns the signature. The signature can again be saved to disk for later use, which will then allow performing a revocation even without access to the private key.
Issuing
revocations
Given a ECDSA public key, the signature from
GNUNET_REVOCATION_sign and the
proof−of−work, GNUNET_REVOCATION_revoke
can be used to perform the actual revocation. The given
callback is called upon completion of the operation.
GNUNET_REVOCATION_revoke_cancel can be used to stop
the library from calling the continuation; however, in that
case it is undefined whether or not the revocation operation
will be executed.
The
REVOCATION Client−Service Protocol
The REVOCATION protocol consists of four simple
messages.
A QueryMessage containing a public ECDSA key is used to check if a particular key has been revoked. The service responds with a QueryResponseMessage which simply contains a bit that says if the given public key is still valid, or if it has been revoked.
The second possible interaction is for a client to revoke a key by passing a RevokeMessage to the service. The RevokeMessage contains the ECDSA public key to be revoked, a signature by the corresponding private key and the proof−of−work. The service responds with a RevocationResponseMessage which can be used to indicate that the RevokeMessage was invalid (e.g. the proof of work is incorrect), or otherwise to indicate that the revocation has been processed successfully.
The
REVOCATION Peer−to−Peer Protocol
Revocation uses two disjoint ways to spread revocation
information among peers. First of all, P2P gossip exchanged
via CORE−level neighbours is used to quickly spread
revocations to all connected peers. Second, whenever two
peers (that both support revocations) connect, the SET
service is used to compute the union of the respective
revocation sets.
In both cases, the exchanged messages are RevokeMessages which contain the public key that is being revoked, a matching ECDSA signature, and a proof−of−work. Whenever a peer learns about a new revocation this way, it first validates the signature and the proof−of−work, then stores it to disk (typically to a file $GNUNET_DATA_HOME/revocation.dat) and finally spreads the information to all directly connected neighbours.
For computing the union using the SET service, the peer with the smaller hashed peer identity will connect (as a "client" in the two−party set protocol) to the other peer after one second (to reduce traffic spikes on connect) and initiate the computation of the set union. All revocation services use a common hash to identify the SET operation over revocation sets.
The current implementation accepts revocation set union operations from all peers at any time; however, well−behaved peers should only initiate this operation once after establishing a connection to a peer with a larger hashed peer identity.
MESSENGER
â Room−based end−to−end
messaging
The MESSENGER subsystem is responsible for secure
end−to−end communication in groups of nodes in
the GNUnet overlay network. MESSENGER builds on the CADET
subsystem which provides a reliable and secure
end−to−end communication between the nodes
inside of these groups.
Additionally to the CADET security benefits, MESSENGER provides following properties designed for application level usage:
• |
MESSENGER provides integrity by signing the messages with the users provided ego | ||
• |
MESSENGER adds (optional) forward secrecy by replacing the key pair of the used ego and signing the propagation of the new one with old one (chaining egos) | ||
• |
MESSENGER provides verification of a original sender by checking against all used egos from a member which are currently in active use (active use depends on the state of a member session) | ||
• |
MESSENGER offsers (optional) decentralized message forwarding between all nodes in a group to improve availability and prevent MITM−attacks | ||
• |
MESSENGER handles new connections and disconnections from nodes in the group by reconnecting them preserving an efficient structure for message distribution (ensuring availability and accountablity) | ||
• |
MESSENGER provides replay protection (messages can be uniquely identified via SHA−512, include a timestamp and the hash of the last message) | ||
• |
MESSENGER allows detection for dropped messages by chaining them (messages refer to the last message by their hash) improving accountability | ||
• |
MESSENGER allows requesting messages from other peers explicitly to ensure availability | ||
• |
MESSENGER provides confidentiality by padding messages to few different sizes (512 bytes, 4096 bytes, 32768 bytes and maximal message size from CADET) | ||
• |
MESSENGER adds (optional) confidentiality with ECDHE to exchange and use symmetric encryption, encrypting with both AES−256 and Twofish but allowing only selected members to decrypt (using the receivers ego for ECDHE) |
Also MESSENGER provides multiple features with privacy in mind:
• |
MESSENGER allows deleting messages from all peers in the group by the original sender (uses the MESSENGER provided verification) | ||
• |
MESSENGER allows using the publicly known anonymous ego instead of any unique identifying ego | ||
• |
MESSENGER allows your node to decide between acting as host of the used messaging room (sharing your peerâs identity with all nodes in the group) or acting as guest (sharing your peerâs identity only with the nodes you explicitly open a connection to) | ||
• |
MESSENGER handles members independently of the peerâs identity making forwarded messages indistinguishable from directly received ones ( complicating the tracking of messages and identifying its origin) | ||
• |
MESSENGER allows names of members being not unique (also names are optional) | ||
• |
MESSENGER does not include information about the selected receiver of an explicitly encrypted message in its header, complicating it for other members to draw conclusions from communication partners |
libgnunetmessenger
The MESSENGER API (defined in
gnunet_messenger_service.h) allows P2P applications
built using GNUnet to communicate with specified kinds of
messages in a group. It provides applications the ability to
send and receive encrypted messages to any group of peers
participating in GNUnet in a decentralized way ( without
even knowing all peersâs identities).
MESSENGER delivers messages to other peers in "rooms". A room uses a variable amount of CADET "channels" which will all be used for message distribution. Each channel can represent an outgoing connection opened by entering a room with GNUNET_MESSENGER_enter_room or an incoming connection if the room was opened before via GNUNET_MESSENGER_open_room.
[image: messenger_room] [image]
To enter a room you have to specify the "door" (peerâs identity of a peer which has opened the room) and the key of the room (which is identical to a CADET "port"). To open a room you have to specify only the key to use. When opening a room you automatically distribute a PEER−message sharing your peerâs identity in the room.
Entering or opening a room can also be combined in any order. In any case you will automatically get a unique member ID and send a JOIN−message notifying others about your entry and your public key from your selected ego.
The ego can be selected by name with the initial GNUNET_MESSENGER_connect besides setting a (identity−)callback for each change/confirmation of the used ego and a (message−)callback which gets called every time a message gets sent or received in the room. Once the identity−callback got called you can check your used ego with GNUNET_MESSENGER_get_key providing only its public key. The function returns NULL if the anonymous ego is used. If the ego should be replaced with a newly generated one, you can use GNUNET_MESSENGER_update to ensure proper chaining of used egos.
Also once the identity−callback got called you can check your used name with GNUNET_MESSENGER_get_name and potentially change or set a name via GNUNET_MESSENGER_set_name. A name is for example required to create a new ego with GNUNET_MESSENGER_update. Also any change in ego or name will automatically be distributed in the room with a NAME− or KEY−message respectively.
To send a message a message inside of a room you can use GNUNET_MESSENGER_send_message. If you specify a selected contact as receiver, the message gets encrypted automatically and will be sent as PRIVATE− message instead.
To request a potentially missed message or to get a specific message after its original call of the message−callback, you can use GNUNET_MESSENGER_get_message. Additionally once a message was distributed to application level and the message−callback got called, you can get the contact respresenting a messageâs sender respectively with GNUNET_MESSENGER_get_sender. This allows getting name and the public key of any sender currently in use with GNUNET_MESSENGER_contact_get_name and GNUNET_MESSENGER_contact_get_key. It is also possible to iterate through all current members of a room with GNUNET_MESSENGER_iterate_members using a callback.
To leave a room you can use GNUNET_MESSENGER_close_room which will also close the rooms connections once all applications on the same peer have left the room. Leaving a room will also send a LEAVE−message closing a member session on all connected peers before any connection will be closed. Leaving a room is however not required for any application to keep your member session open between multiple sessions of the actual application.
Finally, when an application no longer wants to use CADET, it should call GNUNET_MESSENGER_disconnect. You donât have to explicitly close the used rooms or leave them.
Here is a little summary to the kinds of messages you can send manually:
MERGE−message
MERGE−messages will generally be sent automatically to
reduce the amount of parallel chained messages. This is
necessary to close a member session for example. You can
also send MERGE−messages manually if required to merge
two chains of messages.
INVITE−message
INVITE−messages can be used to invite other members in
a room to a different room, sharing one potential door and
the required key to enter the room. This kind of message is
typically sent as encrypted PRIVATE−message to
selected members because it doesnât make much sense to
invite all members from one room to another considering a
rooms key doesnât specify its usage.
TEXT−message
TEXT−messages can be used to send simple
text−based messages and should be considered as being
in readable form without complex decoding. The text has to
end with a NULL−terminator character and should be in
UTF−8 encoding for most compatibility.
FILE−message
FILE−messages can be used to share files inside of a
room. They do not contain the actual file being shared but
its original hash, filename, URI to download the file and a
symmetric key to decrypt the downloaded file.
It is recommended to use the FS subsystem and the FILE−messages in combination.
DELETE−message
DELETE−messages can be used to delete messages
selected with its hash. You can also select any custom delay
relative to the time of sending the DELETE−message.
Deletion will only be processed on each peer in a room if
the sender is authorized.
The only information of a deleted message which being kept will be the chained hashes connecting the message graph for potential traversion. For example the check for completion of a member session requires this information.
Member
sessions
A member session is a triple of the room key, the member ID
and the public key of the memberâs ego. Member
sessions allow that a member can change their ID or their
ego once at a time without losing the ability to delete old
messages or identifying the original sender of a message. On
every change of ID or EGO a session will be marked as
closed. So every session chain will only contain one open
session with the current ID and public key.
If a session is marked as closed the MESSENGER service will check from the first message opening a session to its last one closing the session for completion. If a the service can confirm that there is no message still missing which was sent from the closed member session, it will be marked as completed.
A completed member session is not able to verify any incoming message to ensure forward secrecy preventing others from using old stolen egos.
REST â
RESTful GNUnet Web APIs
Todo
Define REST
Using the REST subsystem, you can expose REST−based APIs or services. The REST service is designed as a pluggable architecture. To create a new REST endpoint, simply add a library in the form âplugin_rest_*â. The REST service will automatically load all REST plugins on startup.
Configuration
The REST service can be configured in various ways. The reference config file can be found in src/rest/rest.conf:
[rest] REST_PORT=7776 REST_ALLOW_HEADERS=Authorization,Accept,Content−Type REST_ALLOW_ORIGIN=* REST_ALLOW_CREDENTIALS=true
The port as well as CORS (cross−origin resource sharing) headers that are supposed to be advertised by the rest service are configurable.
Namespace
considerations
The gnunet−rest−service will load all
plugins that are installed. As such it is important that the
endpoint namespaces do not clash.
For example, plugin X might expose the endpoint â/xxxâ while plugin Y exposes endpoint â/xxx/yyyâ. This is a problem if plugin X is also supposed to handle a call to â/xxx/yyyâ. Currently the REST service will not complain or warn about such clashes, so please make sure that endpoints are unambiguous.
Endpoint
documentation
This is WIP. Endpoints should be documented appropriately.
Preferably using annotations.
C
Tutorial
This tutorials explains how to install GNUnet on a GNU/Linux
system and gives an introduction on how GNUnet can be used
to develop a Peer−to−Peer application. Detailed
installation instructions for various operating systems and
a detailed list of all dependencies can be found on our
website at https://docs.gnunet.org/#Installation and
in our Reference Documentation (GNUnet Handbook).
Please read this tutorial carefully since every single step is important, and do not hesitate to contact the GNUnet team if you have any questions or problems! Visit this link in your webbrowser to learn how to contact the GNUnet team: https://gnunet.org/en/contact.html
Introduction
to GNUnet Architecture
GNUnet is organized in layers and services. Each service is
composed of a main service implementation and a client
library for other programs to use the serviceâs
functionality, described by an API. Some services provide an
additional command line tool to enable the user to interact
with the service.
Very often it is other GNUnet services that will use these APIs to build the higher layers of GNUnet on top of the lower ones. Each layer expands or extends the functionality of the service below (for instance, to build a mesh on top of a DHT).
The main service implementation runs as a standalone process in the Operating System and the client code runs as part of the client program, so crashes of a client do not affect the service process or other clients. The service and the clients communicate via a message protocol to be defined and implemented by the programmer.
First Steps
with GNUnet
Configure your peer
First of all we need to configure your peer. Each peer is
started with a configuration containing settings for GNUnet
itself and its services. This configuration is based on the
default configuration shipped with GNUnet and can be
modified. The default configuration is located in the
$PREFIX/share/gnunet/config.d directory. When
starting a peer, you can specify a customized configuration
using the the −c command line switch when
starting the ARM service and all other services. When using
a modified configuration the default values are loaded and
only values specified in the configuration file will replace
the default values.
Since we want to start additional peers later, we need some modifications from the default configuration. We need to create a separate service home and a file containing our modifications for this peer:
$ mkdir ~/gnunet1/ $ touch peer1.conf
Now add the following lines to peer1.conf to use this directory. For simplified usage we want to prevent the peer to connect to the GNUnet network since this could lead to confusing output. This modifications will replace the default settings:
[PATHS] # Use this directory to store GNUnet data GNUNET_HOME = ~/gnunet1/ [hostlist] # prevent bootstrapping SERVERS =
Start a
peer
Each GNUnet instance (called peer) has an identity (peer ID)
based on a cryptographic public private key pair. The peer
ID is the printable hash of the public key.
GNUnet services are controlled by a master service, the so called Automatic Restart Manager (ARM). ARM starts, stops and even restarts services automatically or on demand when a client connects. You interact with the ARM service using the gnunet−arm tool. GNUnet can then be started with gnunet−arm −s and stopped with gnunet−arm −e. An additional service not automatically started can be started using gnunet−arm −i <service name> and stopped using gnunet−arm −k <servicename>.
Once you have started your peer, you can use many other GNUnet commands to interact with it. For example, you can run:
$ gnunet−peerinfo −s
to obtain the public key of your peer.
You should see an output containing the peer ID similar to:
I am peer `0PA02UVRKQTS2C .. JL5Q78F6H0B1ACPV1CJI59MEQUMQCC5G'.
Monitor a
peer
In this section, we will monitor the behaviour of our
peerâs DHT service with respect to a specific key.
First we will start GNUnet and then start the DHT service
and use the DHT monitor tool to monitor the PUT and GET
commands we issue ussing the
gnunet−dht−put and
gnunet−dht−get commands. Using the
âmonitorâ line given below, you can observe the
behavior of your own peerâs DHT with respect to the
specified KEY:
# start gnunet with all default services: $ gnunet−arm −c ~/peer1.conf −s # start DHT service: $ gnunet−arm −c ~/peer1.conf −i dht $ cd ~/gnunet/src/dht; $ ./gnunet−dht−monitor −c ~/peer1.conf −k KEY
Now open a separate terminal and change again to the gnunet/src/dht directory:
$ cd ~/gnunet/src/dht # put VALUE under KEY in the DHT: $ ./gnunet−dht−put −c ~/peer1.conf −k KEY −d VALUE # get key KEY from the DHT: $ ./gnunet/src/dht/gnunet−dht−get −c ~/peer1.conf −k KEY # print statistics about current GNUnet state: $ gnunet−statistics −c ~/peer1.conf # print statistics about DHT service: $ gnunet−statistics −c ~/peer1.conf −s dht
Starting Two
Peers by Hand
This section describes how to start two peers on the same
machine by hand. The process is rather painful, but the
description is somewhat instructive. In practice, you might
prefer the automated method (see Starting Peers Using the
Testbed Service).
Setup a
second peer
We will now start a second peer on your machine. For the
second peer, you will need to manually create a modified
configuration file to avoid conflicts with ports and
directories. A peers configuration file is by default
located in ~/.gnunet/gnunet.conf. This file is
typically very short or even empty as only the differences
to the defaults need to be specified. The defaults are
located in many files in the
$PREFIX/share/gnunet/config.d directory.
To configure the second peer, use the files $PREFIX/share/gnunet/config.d as a template for your main configuration file:
$ cat $PREFIX/share/gnunet/config.d/*.conf > peer2.conf
Now you have to edit peer2.conf and change:
• |
GNUNET\_TEST\_HOME under PATHS | ||
• |
Every (uncommented) value for âPORTâ (add 10000) in any section (the option may be commented out if PORT is prefixed by "#", in this case, UNIX domain sockets are used and the PORT option does not need to be touched) | ||
• |
Every value for âUNIXPATHâ in any section (e.g. by adding a "−p2" suffix) |
to a fresh, unique value. Make sure that the PORT numbers stay below 65536. From now on, whenever you interact with the second peer, you need to specify −c peer2.conf as an additional command line argument.
Now, generate the 2nd peerâs private key:
$ gnunet−peerinfo −s −c peer2.conf
This may take a while, generate entropy using your keyboard or mouse as needed. Also, make sure the output is different from the gnunet−peerinfo output for the first peer (otherwise you made an error in the configuration).
Start the
second peer and connect the peers
Then, you can start a second peer using:
$ gnunet−arm −c peer2.conf −s $ gnunet−arm −c peer2.conf −i dht $ ~/gnunet/src/dht/gnunet−dht−put −c peer2.conf −k KEY −d VALUE $ ~/gnunet/src/dht/gnunet−dht−get −c peer2.conf −k KEY
If you want the two peers to connect, you have multiple options:
• |
UDP neighbour discovery (automatic) |
|||
• |
Setup a bootstrap server |
|||
• |
Connect manually |
To setup peer 1 as bootstrapping server change the configuration of the first one to be a hostlist server by adding the following lines to peer1.conf to enable bootstrapping server:
[hostlist] OPTIONS = −p
Then change peer2.conf and replace the âSERVERSâ line in the â[hostlist]â section with âhttp://localhost:8080/â. Restart both peers using:
# stop first peer $ gnunet−arm −c peer1.conf −e # start first peer $ gnunet−arm −c peer1.conf −s # start second peer $ gnunet−arm −c peer2.conf −s
Note that if you start your peers without changing these settings, they will use the âglobalâ hostlist servers of the GNUnet P2P network and likely connect to those peers. At that point, debugging might become tricky as youâre going to be connected to many more peers and would likely observe traffic and behaviors that are not explicitly controlled by you.
How to
connect manually
If you want to use the peerinfo tool to connect your
peers, you should:
• |
Set IMMEDIATE_START = NO in section hostlist (to not connect to the global GNUnet) | ||
• |
Start both peers running gnunet−arm −c peer1.conf −s and gnunet−arm −c peer2.conf −s | ||
• |
Get HELLO message of the first peer running gnunet−peerinfo −c peer1.conf −g | ||
• |
Give the output to the second peer by running gnunet−peerinfo −c peer2.conf −p '<output>' |
Check that they are connected using gnunet−core −c peer1.conf, which should give you the other peerâs peer identity:
$ gnunet−core −c peer1.conf Peer `9TVUCS8P5A7ILLBGO6 [...shortened...] 1KNBJ4NGCHP3JPVULDG'
Starting
Peers Using the Testbed Service
GNUnetâs testbed service is used for testing scenarios
where a number of peers are to be started. The testbed can
manage peers on a single host or on multiple hosts in a
distributed fashion. On a single affordable computer, it
should be possible to run around tens of peers without
drastically increasing the load on the system.
The testbed service can be access through its API include/gnunet\_testbed\_service.h. The API provides many routines for managing a group of peers. It also provides a helper function GNUNET\_TESTBED\_test\_run() to quickly setup a minimalistic testing environment on a single host.
This function takes a configuration file which will be used as a template configuration for the peers. The testbed takes care of modifying relevant options in the peersâ configuration such as SERVICEHOME, PORT, UNIXPATH to unique values so that peers run without running into conflicts. It also checks and assigns the ports in configurations only if they are free.
Additionally, the testbed service also reads its options from the same configuration file. Various available options and details about them can be found in the testbed default configuration file src/testbed/testbed.conf.
With the testbed API, a sample test case can be structured as follows:
#include <unistd.h> #include <gnunet/platform.h> #include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_testbed_service.h> #include <gnunet/gnunet_dht_service.h> #define NUM_PEERS 20 static struct GNUNET_TESTBED_Operation *dht_op; static struct GNUNET_DHT_Handle *dht_handle; struct MyContext { int ht_len; } ctxt; static int result; static void shutdown_task (void *cls) { if (NULL != dht_op) { GNUNET_TESTBED_operation_done (dht_op); dht_op = NULL; dht_handle = NULL; } result = GNUNET_OK; } static void service_connect_comp (void *cls, struct GNUNET_TESTBED_Operation *op, void *ca_result, const char *emsg) { GNUNET_assert (op == dht_op); dht_handle = ca_result; // Do work here... GNUNET_SCHEDULER_shutdown (); } static void * dht_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) { struct MyContext *ctxt = cls; dht_handle = GNUNET_DHT_connect (cfg, ctxt−>ht_len); return dht_handle; } static void dht_da (void *cls, void *op_result) { struct MyContext *ctxt = cls; GNUNET_DHT_disconnect ((struct GNUNET_DHT_Handle *) op_result); dht_handle = NULL; } static void test_master (void *cls, struct GNUNET_TESTBED_RunHandle *h, unsigned int num_peers, struct GNUNET_TESTBED_Peer **peers, unsigned int links_succeeded, unsigned int links_failed) { ctxt.ht_len = 10; dht_op = GNUNET_TESTBED_service_connect (NULL, peers[0], "dht", &service_connect_comp, NULL, &dht_ca, &dht_da, &ctxt); GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); } int main (int argc, char **argv) { int ret; result = GNUNET_SYSERR; ret = GNUNET_TESTBED_test_run ("awesome−test", "template.conf", NUM_PEERS, 0LL, NULL, NULL, &test_master, NULL); if ( (GNUNET_OK != ret) || (GNUNET_OK != result) ) return 1; return 0; }
The source code for the above listing can be found at https://git.gnunet.org/gnunet.git/tree/doc/documentation/testbed_test.c or in the doc/ folder of your repository check−out. After installing GNUnet, the above source code can be compiled as:
$ export CPPFLAGS="−I/path/to/gnunet/headers" $ export LDFLAGS="−L/path/to/gnunet/libraries" $ gcc $CPPFLAGS $LDFLAGS −o testbed−test testbed_test.c \ −lgnunettestbed −lgnunetdht −lgnunetutil # Generate (empty) configuration $ touch template.conf # run it (press CTRL−C to stop) $ ./testbed−test
The CPPFLAGS and LDFLAGS are necessary if GNUnet is installed into a different directory other than /usr/local.
All of testbed APIâs peer management functions treat management actions as operations and return operation handles. It is expected that the operations begin immediately, but they may get delayed (to balance out load on the system). The program using the API then has to take care of marking the operation as âdoneâ so that its associated resources can be freed immediately and other waiting operations can be executed. Operations will be canceled if they are marked as âdoneâ before their completion.
An operation is treated as completed when it succeeds or fails. Completion of an operation is either conveyed as events through controller event callback or through respective operation completion callbacks. In functions which support completion notification through both controller event callback and operation completion callback, first the controller event callback will be called. If the operation is not marked as done in that callback or if the callback is given as NULL when creating the operation, the operation completion callback will be called. The API documentation shows which event are to be expected in the controller event notifications. It also documents any exceptional behaviour.
Once the peers are started, test cases often need to connect some of the peersâ services. Normally, opening a connect to a peerâs service requires the peerâs configuration. While using testbed, the testbed automatically generates per−peer configuration. Accessing those configurations directly through file system is discouraged as their locations are dynamically created and will be different among various runs of testbed. To make access to these configurations easy, testbed API provides the function GNUNET\_TESTBED\_service\_connect(). This function fetches the configuration of a given peer and calls the Connect Adapter. In the example code, it is the dht\_ca. A connect adapter is expected to open the connection to the needed service by using the provided configuration and return the created service connection handle. Successful connection to the needed service is signaled through service\_connect\_comp\_cb.
A dual to connect adapter is the Disconnect Adapter. This callback is called after the connect adapter has been called when the operation from GNUNET\_TESTBED\_service\_connect() is marked as âdoneâ. It has to disconnect from the service with the provided service handle (op\_result).
Exercise: Find out how many peers you can run on your system.
Exercise: Find out how to create a 2D torus topology by changing the options in the configuration file. See section âThe GNUnet Reference Documentationâ in The GNUnet Reference Documentation, then use the DHT API to store and retrieve values in the network.
Developing
Applications
gnunet−ext
To develop a new peer−to−peer application or to
extend GNUnet we provide a template build system for writing
GNUnet extensions in C. It can be obtained as follows:
$ git clone https://git.gnunet.org/gnunet−ext.git $ cd gnunet−ext/ $ ./bootstrap $ ./configure −−prefix=$PREFIX −−with−gnunet=$PREFIX $ make $ make install $ make check
The GNUnet ext template includes examples and a working buildsystem for a new GNUnet service. A common GNUnet service consists of the following parts which will be discussed in detail in the remainder of this document. The functionality of a GNUnet service is implemented in:
• |
the GNUnet service (gnunet−ext/src/ext/gnunet−service−ext.c) | ||
• |
the client API (gnunet−ext/src/ext/ext_api.c) | ||
• |
the client application using the service API (gnunet−ext/src/ext/gnunet−ext.c) |
The interfaces for these entities are defined in:
• |
client API interface (gnunet−ext/src/ext/ext.h) | ||
• |
the service interface (gnunet−ext/src/include/gnunet_service_SERVICE.h) | ||
• |
the P2P protocol (gnunet−ext/src/include/gnunet_protocols_ext.h) |
In addition the ext systems provides:
• |
a test testing the API (gnunet−ext/src/ext/test_ext_api.c) | ||
• |
a configuration template for the service (gnunet−ext/src/ext/ext.conf.in) |
Adapting the
Template
The first step for writing any extension with a new service
is to ensure that the ext.conf.in file contains
entries for the UNIXPATH, PORT and
BINARY for the service in a section named after the
service.
If you want to adapt the template rename the ext.conf.in to match your services name, you have to modify the AC\_OUTPUT section in configure.ac in the gnunet−ext root.
Writing a
Client Application
When writing any client application (for example, a
command−line tool), the basic structure is to start
with the GNUNET\_PROGRAM\_run function. This function
will parse command−line options, setup the scheduler
and then invoke the run function (with the remaining
non−option arguments) and a handle to the parsed
configuration (and the configuration file name that was
used, which is typically not needed):
#include <gnunet/platform.h> #include <gnunet/gnunet_util_lib.h> static int ret; static void run (void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { // main code here ret = 0; } int main (int argc, char *const *argv) { struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_OPTION_END }; return (GNUNET_OK == GNUNET_PROGRAM_run (argc, argv, "binary−name", gettext_noop ("binary description text"), options, &run, NULL)) ? ret : 1; }
Handling
command−line options
Options can then be added easily by adding global variables
and expanding the options array. For example, the
following would add a string−option and a binary flag
(defaulting to NULL and GNUNET\_NO
respectively):
static char *string_option; static int a_flag; // ... struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_option_string ('s', "name", "SOMESTRING", gettext_noop ("text describing the string_option NAME"), &string_option}, GNUNET_GETOPT_option_flag ('f', "flag", gettext_noop ("text describing the flag option"), &a_flag), GNUNET_GETOPT_OPTION_END }; string_option = NULL; a_flag = GNUNET_SYSERR; // ...
Issues such as displaying some helpful text describing options using the −−help argument and error handling are taken care of when using this approach. Other GNUNET\_GETOPT\_−functions can be used to obtain integer value options, increment counters, etc. You can even write custom option parsers for special circumstances not covered by the available handlers. To check if an argument was specified by the user you initialize the variable with a specific value (e.g. NULL for a string and GNUNET_SYSERR for a integer) and check after parsing happened if the values were modified.
Inside the run method, the program would perform the application−specific logic, which typically involves initializing and using some client library to interact with the service. The client library is supposed to implement the IPC whereas the service provides more persistent P2P functions.
Exercise: Add a few command−line options and print them inside of run. What happens if the user gives invalid arguments?
Writing a
Client Library
The first and most important step in writing a client
library is to decide on an API for the library. Typical API
calls include connecting to the service, performing
application−specific requests and cleaning up. Many
examples for such service APIs can be found in the
gnunet/src/include/gnunet\_*\_service.h files.
Then, a client−service protocol needs to be designed. This typically involves defining various message formats in a header that will be included by both the service and the client library (but is otherwise not shared and hence located within the serviceâs directory and not installed by make install). Each message must start with a struct GNUNET\_MessageHeader and must be shorter than 64k. By convention, all fields in IPC (and P2P) messages must be in big−endian format (and thus should be read using ntohl and similar functions and written using htonl and similar functions). Unique message types must be defined for each message struct in the gnunet\_protocols.h header (or an extension−specific include file).
Connecting
to the Service
Before a client library can implement the
application−specific protocol with the service, a
connection must be created:
struct GNUNET_MQ_MessageHandlers handlers[] = { // ... GNUNET_MQ_handler_end () }; struct GNUNET_MQ_Handle *mq; mq = GNUNET_CLIENT_connect (cfg, "service−name", handlers, &error_cb, NULL);
As a result a GNUNET\_MQ\_Handle is returned which can to used henceforth to transmit messages to the service. The complete MQ API can be found in gnunet\_mq\_lib.h. The handlers array in the example above is incomplete. Here is where you will define which messages you expect to receive from the service, and which functions handle them. The error\_cb is a function that is to be called whenever there are errors communicating with the service.
Sending
messages
In GNUnet, messages are always sent beginning with a
struct GNUNET\_MessageHeader in big endian format.
This header defines the size and the type of the message,
the payload follows after this header.
struct GNUNET_MessageHeader { uint16_t size GNUNET_PACKED; uint16_t type GNUNET_PACKED; };
Existing message types are defined in gnunet\_protocols.h. A common way to create a message is with an envelope:
struct GNUNET_MQ_Envelope *env; struct GNUNET_MessageHeader *msg; env = GNUNET_MQ_msg_extra (msg, payload_size, GNUNET_MY_MESSAGE_TYPE); GNUNET_memcpy (&msg[1], &payload, payload_size); // Send message via message queue 'mq' GNUNET_mq_send (mq, env);
Exercise: Define a message struct that includes a 32−bit unsigned integer in addition to the standard GNUnet MessageHeader. Add a C struct and define a fresh protocol number for your message. Protocol numbers in gnunet−ext are defined in gnunet−ext/src/include/gnunet_protocols_ext.h
Exercise: Find out how you can determine the number of messages in a message queue.
Exercise: Find out how you can determine when a message you have queued was actually transmitted.
Exercise: Define a helper function to transmit a 32−bit unsigned integer (as payload) to a service using some given client handle.
Receiving
Replies from the Service
Clients can receive messages from the service using the
handlers specified in the handlers array we specified
when connecting to the service. Entries in the the array are
usually created using one of two macros, depending on
whether the message is fixed size or variable size. Variable
size messages are managed using two callbacks, one to check
that the message is well−formed, the other to actually
process the message. Fixed size messages are fully checked
by the MQ−logic, and thus only need to provide the
handler to process the message. Note that the prefixes
check\_ and handle\_ are mandatory.
static void handle_fix (void *cls, const struct MyMessage *msg) { // process 'msg' } static int check_var (void *cls, const struct MyVarMessage *msg) { // check 'msg' is well−formed return GNUNET_OK; } static void handle_var (void *cls, const struct MyVarMessage *msg) { // process 'msg' } struct GNUNET_MQ_MessageHandler handlers[] = { GNUNET_MQ_hd_fixed_size (fix, GNUNET_MESSAGE_TYPE_MY_FIX, struct MyMessage, NULL), GNUNET_MQ_hd_fixed_size (var, GNUNET_MESSAGE_TYPE_MY_VAR, struct MyVarMessage, NULL), GNUNET_MQ_handler_end () };
Exercise: Expand your helper function to receive a response message (for example, containing just the struct GNUnet MessageHeader without any payload). Upon receiving the serviceâs response, you should call a callback provided to your helper functionâs API.
Exercise: Figure out where you can pass values to the closures (cls).
Writing a
user interface
Given a client library, all it takes to access a service now
is to combine calls to the client library with parsing
command−line options.
Exercise: Call your client API from your run() method in your client application to send a request to the service. For example, send a 32−bit integer value based on a number given at the command−line to the service.
Writing a
Service
Before you can test the client youâve written so far,
youâll need to also implement the corresponding
service.
Code
Placement
New services are placed in their own subdirectory under
gnunet/src. This subdirectory should contain the API
implementation file SERVICE\_api.c, the description
of the client−service protocol SERVICE.h and
P2P protocol SERVICE\_protocol.h, the implementation
of the service itself
gnunet−service−SERVICE.h and several
files for tests, including test code and configuration
files.
Starting a
Service
The key API definition for creating a service is the
GNUNET\_SERVICE\_MAIN macro:
GNUNET_SERVICE_MAIN ("service−name", GNUNET_SERVICE_OPTION_NONE, &run, &client_connect_cb, &client_disconnect_cb, NULL, GNUNET_MQ_hd_fixed_size (...), GNUNET_MQ_hd_var_size (...), GNUNET_MQ_handler_end ());
In addition to the service name and flags, the macro takes three functions, typically called run, client\_connect\_cb and client\_disconnect\_cb as well as an array of message handlers that will be called for incoming messages from clients.
A minimal version of the three central service functions would look like this:
static void run (void *cls, const struct GNUNET_CONFIGURATION_Handle *c, struct GNUNET_SERVICE_Handle *service) { } static void * client_connect_cb (void *cls, struct GNUNET_SERVICE_Client *c, struct GNUNET_MQ_Handle *mq) { return c; } static void client_disconnect_cb (void *cls, struct GNUNET_SERVICE_Client *c, void *internal_cls) { GNUNET_assert (c == internal_cls); }
Exercise: Write a stub service that processes no messages at all in your code. Create a default configuration for it, integrate it with the build system and start the service from gnunet−service−arm using gnunet−arm −i NAME.
Exercise: Figure out how to set the closure (cls) for handlers of a service.
Exercise: Figure out how to send messages from the service back to the client.
Each handler function in the service must eventually (possibly in some asynchronous continuation) call GNUNET\_SERVICE\_client\_continue(). Only after this call additional messages from the same client may be processed. This way, the service can throttle processing messages from the same client.
Exercise: Change the service to âhandleâ the message from your client (for now, by printing a message). What happens if you forget to call GNUNET\_SERVICE\_client\_continue()?
Interacting
directly with other Peers using the CORE Service
FIXME: This section still needs to be updated to the latest
API!
One of the most important services in GNUnet is the CORE service managing connections between peers and handling encryption between peers.
One of the first things any service that extends the P2P protocol typically does is connect to the CORE service using:
#include <gnunet/gnunet_core_service.h> struct GNUNET_CORE_Handle * GNUNET_CORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, void *cls, GNUNET_CORE_StartupCallback init, GNUNET_CORE_ConnectEventHandler connects, GNUNET_CORE_DisconnectEventHandler disconnects, const struct GNUNET_MQ_MessageHandler *handlers);
New P2P
connections
Before any traffic with a different peer can be exchanged,
the peer must be known to the service. This is notified by
the CORE connects callback, which communicates the
identity of the new peer to the service:
void * connects (void *cls, const struct GNUNET_PeerIdentity *peer, struct GNUNET_MQ_Handle *mq) { return mq; }
Note that whatever you return from connects is given as the cls argument to the message handlers for messages from the respective peer.
Exercise: Create a service that connects to the CORE. Then start (and connect) two peers and print a message once your connect callback is invoked.
Receiving
P2P Messages
To receive messages from CORE, you pass the desired
handlers to the GNUNET\_CORE\_connect()
function, just as we showed for services.
It is your responsibility to process messages fast enough or to implement flow control. If an application does not process CORE messages fast enough, CORE will randomly drop messages to not keep a very long queue in memory.
Exercise: Start one peer with a new service that has a message handler and start a second peer that only has your âoldâ service without message handlers. Which âconnectâ handlers are invoked when the two peers are connected? Why?
Sending P2P
Messages
You can transmit messages to other peers using the mq
you were given during the connect callback. Note that
the mq automatically is released upon
disconnect and that you must not use it
afterwards.
It is your responsibility to not over−fill the message queue, GNUnet will send the messages roughly in the order given as soon as possible.
Exercise: Write a service that upon connect sends messages as fast as possible to the other peer (the other peer should run a service that âprocessesâ those messages). How fast is the transmission? Count using the STATISTICS service on both ends. Are messages lost? How can you transmit messages faster? What happens if you stop the peer that is receiving your messages?
End of P2P
connections
If a message handler returns GNUNET\_SYSERR, the
remote peer shuts down or there is an unrecoverable network
disconnection, CORE notifies the service that the peer
disconnected. After this notification no more messages will
be received from the peer and the service is no longer
allowed to send messages to the peer. The disconnect
callback looks like the following:
void disconnects (void *cls, const struct GNUNET_PeerIdentity * peer) { /* Remove peer's identity from known peers */ /* Make sure no messages are sent to peer from now on */ }
Exercise: Fix your service to handle peer disconnects.
Storing
peer−specific data using the PEERSTORE service
GNUnetâs PEERSTORE service offers a persistorage for
arbitrary peer−specific data. Other GNUnet services
can use the PEERSTORE to store, retrieve and monitor data
records. Each data record stored with PEERSTORE contains the
following fields:
• |
subsystem: Name of the subsystem responsible for the record. | ||
• |
peerid: Identity of the peer this record is related to. | ||
• |
key: a key string identifying the record. | ||
• |
value: binary record value. | ||
• |
expiry: record expiry date. |
The first step is to start a connection to the PEERSTORE service:
#include "gnunet_peerstore_service.h" peerstore_handle = GNUNET_PEERSTORE_connect (cfg);
The service handle peerstore_handle will be needed for all subsequent PEERSTORE operations.
Storing
records
To store a new record, use the following function:
struct GNUNET_PEERSTORE_StoreContext * GNUNET_PEERSTORE_store (struct GNUNET_PEERSTORE_Handle *h, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, const void *value, size_t size, struct GNUNET_TIME_Absolute expiry, enum GNUNET_PEERSTORE_StoreOption options, GNUNET_PEERSTORE_Continuation cont, void *cont_cls);
The options parameter can either be GNUNET_PEERSTORE_STOREOPTION_MULTIPLE which means that multiple values can be stored under the same key combination (subsystem, peerid, key), or GNUNET_PEERSTORE_STOREOPTION_REPLACE which means that PEERSTORE will replace any existing values under the given key combination (subsystem, peerid, key) with the new given value.
The continuation function cont will be called after the store request is successfully sent to the PEERSTORE service. This does not guarantee that the record is successfully stored, only that it was received by the service.
The GNUNET_PEERSTORE_store function returns a handle to the store operation. This handle can be used to cancel the store operation only before the continuation function is called:
void GNUNET_PEERSTORE_store_cancel (struct GNUNET_PEERSTORE_StoreContext *sc);
Retrieving
records
To retrieve stored records, use the following function:
struct GNUNET_PEERSTORE_IterateContext * GNUNET_PEERSTORE_iterate (struct GNUNET_PEERSTORE_Handle *h, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, GNUNET_PEERSTORE_Processor callback, void *callback_cls);
The values of peer and key can be NULL. This allows the iteration over values stored under any of the following key combinations:
• |
(subsystem) |
|||
• |
(subsystem, peerid) |
|||
• |
(subsystem, key) |
|||
• |
(subsystem, peerid, key) |
The callback function will be called once with each retrieved record and once more with a NULL record to signal the end of results.
The GNUNET_PEERSTORE_iterate function returns a handle to the iterate operation. This handle can be used to cancel the iterate operation only before the callback function is called with a NULL record.
Monitoring
records
PEERSTORE offers the functionality of monitoring for new
records stored under a specific key combination (subsystem,
peerid, key). To start the monitoring, use the following
function:
struct GNUNET_PEERSTORE_WatchContext * GNUNET_PEERSTORE_watch (struct GNUNET_PEERSTORE_Handle *h, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, GNUNET_PEERSTORE_Processor callback, void *callback_cls);
Whenever a new record is stored under the given key combination, the callback function will be called with this new record. This will continue until the connection to the PEERSTORE service is broken or the watch operation is canceled:
void GNUNET_PEERSTORE_watch_cancel (struct GNUNET_PEERSTORE_WatchContext *wc);
Disconnecting
from PEERSTORE
When the connection to the PEERSTORE service is no longer
needed, disconnect using the following function:
void GNUNET_PEERSTORE_disconnect (struct GNUNET_PEERSTORE_Handle *h, int sync_first);
If the sync_first flag is set to GNUNET_YES, the API will delay the disconnection until all store requests are received by the PEERSTORE service. Otherwise, it will disconnect immediately.
Using the
DHT
The DHT allows to store data so other peers in the P2P
network can access it and retrieve data stored by any peers
in the network. This section will explain how to use the
DHT. Of course, the first thing to do is to connect to the
DHT service:
dht_handle = GNUNET_DHT_connect (cfg, parallel_requests);
The second parameter indicates how many requests in parallel to expect. It is not a hard limit, but a good approximation will make the DHT more efficient.
Storing data
in the DHT
Since the DHT is a dynamic environment (peers join and leave
frequently) the data that we put in the DHT does not stay
there indefinitely. It is important to ârefreshâ
the data periodically by simply storing it again, in order
to make sure other peers can access it.
The put API call offers a callback to signal that the PUT request has been sent. This does not guarantee that the data is accessible to others peers, or even that is has been stored, only that the service has requested to a neighboring peer the retransmission of the PUT request towards its final destination. Currently there is no feedback about whether or not the data has been successfully stored or where it has been stored. In order to improve the availablilty of the data and to compensate for possible errors, peers leaving and other unfavorable events, just make several PUT requests!
message_sent_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { // Request has left local node } struct GNUNET_DHT_PutHandle * GNUNET_DHT_put (struct GNUNET_DHT_Handle *handle, const struct GNUNET_HashCode *key, uint32_t desired_replication_level, enum GNUNET_DHT_RouteOption options, enum GNUNET_BLOCK_Type type, size_t size, const void *data, struct GNUNET_TIME_Absolute exp, struct GNUNET_TIME_Relative timeout, GNUNET_DHT_PutContinuation cont, void *cont_cls)
Exercise: Store a value in the DHT periodically to make sure it is available over time. You might consider using the function GNUNET\_SCHEDULER\_add\_delayed and call GNUNET\_DHT\_put from inside a helper function.
Obtaining
data from the DHT
As we saw in the previous example, the DHT works in an
asynchronous mode. Each request to the DHT is executed
âin the backgroundâ and the API calls return
immediately. In order to receive results from the DHT, the
API provides a callback. Once started, the request runs in
the service, the service will try to get as many results as
possible (filtering out duplicates) until the timeout
expires or we explicitly stop the request. It is possible to
give a âforeverâ timeout with
GNUNET\_TIME\_UNIT\_FOREVER\_REL.
If we give a route option GNUNET\_DHT\_RO\_RECORD\_ROUTE the callback will get a list of all the peers the data has travelled, both on the PUT path and on the GET path.
static void get_result_iterator (void *cls, struct GNUNET_TIME_Absolute expiration, const struct GNUNET_HashCode *key, const struct GNUNET_PeerIdentity *get_path, unsigned int get_path_length, const struct GNUNET_PeerIdentity *put_path, unsigned int put_path_length, enum GNUNET_BLOCK_Type type, size_t size, const void *data) { // Optionally: GNUNET_DHT_get_stop (get_handle); } get_handle = GNUNET_DHT_get_start (dht_handle, block_type, &key, replication, GNUNET_DHT_RO_NONE, NULL, 0, &get_result_iterator, cls)
Exercise: Store a value in the DHT and after a while retrieve it. Show the IDs of all the peers the requests have gone through. In order to convert a peer ID to a string, use the function GNUNET\_i2s. Pay attention to the route option parameters in both calls!
Implementing
a block plugin
In order to store data in the DHT, it is necessary to
provide a block plugin. The DHT uses the block plugin to
ensure that only well−formed requests and replies are
transmitted over the network.
The block plugin should be put in a file plugin\_block\_SERVICE.c in the serviceâs respective directory. The mandatory functions that need to be implemented for a block plugin are described in the following sections.
Validating
requests and replies
The evaluate function should validate a reply or a request.
It returns a GNUNET\_BLOCK\_EvaluationResult, which
is an enumeration. All possible answers are in
gnunet\_block\_lib.h. The function will be called
with a reply\_block argument of NULL for
requests. Note that depending on how evaluate is
called, only some of the possible return values are valid.
The specific meaning of the xquery argument is
application−specific. Applications that do not use an
extended query should check that the xquery\_size is
zero. The block group is typically used to filter duplicate
replies.
static enum GNUNET_BLOCK_EvaluationResult block_plugin_SERVICE_evaluate (void *cls, enum GNUNET_BLOCK_Type type, struct GNUNET_BlockGroup *bg, const GNUNET_HashCode *query, const void *xquery, size_t xquery_size, const void *reply_block, size_t reply_block_size) { // Verify type, block and bg }
Note that it is mandatory to detect duplicate replies in this function and return the respective status code. Duplicate detection is typically done using the Bloom filter block group provided by libgnunetblockgroup.so. Failure to do so may cause replies to circle in the network.
Deriving a
key from a reply
The DHT can operate more efficiently if it is possible to
derive a key from the value of the corresponding block. The
get\_key function is used to obtain the key of a
block â for example, by means of hashing. If deriving
the key is not possible, the function should simply return
GNUNET\_SYSERR (the DHT will still work just fine
with such blocks).
static int block_plugin_SERVICE_get_key (void *cls, enum GNUNET_BLOCK_Type type, const void *block, size_t block_size, struct GNUNET_HashCode *key) { // Store the key in the key argument, return GNUNET_OK on success. }
Initialization
of the plugin
The plugin is realized as a shared C library. The library
must export an initialization function which should
initialize the plugin. The initialization function specifies
what block types the plugin cares about and returns a struct
with the functions that are to be used for validation and
obtaining keys (the ones just defined above).
void * libgnunet_plugin_block_SERVICE_init (void *cls) { static enum GNUNET_BLOCK_Type types[] = { GNUNET_BLOCK_TYPE_SERVICE_BLOCKYPE, GNUNET_BLOCK_TYPE_ANY }; struct GNUNET_BLOCK_PluginFunctions *api; api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions); api−>evaluate = &block_plugin_SERICE_evaluate; api−>get_key = &block_plugin_SERVICE_get_key; api−>types = types; return api; }
Shutdown of
the plugin
Following GNUnetâs general plugin API concept, the
plugin must export a second function for cleaning up. It
usually does very little.
void * libgnunet_plugin_block_SERVICE_done (void *cls) { struct GNUNET_TRANSPORT_PluginFunctions *api = cls; GNUNET_free (api); return NULL; }
Integration
of the plugin with the build system
In order to compile the plugin, the Makefile.am file
for the service SERVICE should contain a rule similar to
this:
plugindir = $(libdir)/gnunet plugin_LTLIBRARIES = \ libgnunet_plugin_block_ext.la libgnunet_plugin_block_ext_la_SOURCES = \ plugin_block_ext.c libgnunet_plugin_block_ext_la_LIBADD = \ $(prefix)/lib/libgnunethello.la \ $(prefix)/lib/libgnunetblock.la \ $(prefix)/lib/libgnunetutil.la libgnunet_plugin_block_ext_la_LDFLAGS = \ $(GN_PLUGIN_LDFLAGS) libgnunet_plugin_block_ext_la_DEPENDENCIES = \ $(prefix)/lib/libgnunetblock.la
Exercise: Write a block plugin that accepts all queries and all replies but prints information about queries and replies when the respective validation hooks are called.
Monitoring
the DHT
It is possible to monitor the functioning of the local DHT
service. When monitoring the DHT, the service will alert the
monitoring program of any events, both started locally or
received for routing from another peer. The are three
different types of events possible: a GET request, a PUT
request or a response (a reply to a GET).
Since the different events have different associated data, the API gets 3 different callbacks (one for each message type) and optional type and key parameters, to allow for filtering of messages. When an event happens, the appropriate callback is called with all the information about the event.
static void get_callback (void *cls, enum GNUNET_DHT_RouteOption options, enum GNUNET_BLOCK_Type type, uint32_t hop_count, uint32_t desired_replication_level, unsigned int path_length, const struct GNUNET_PeerIdentity *path, const struct GNUNET_HashCode * key) { } static void get_resp_callback (void *cls, enum GNUNET_BLOCK_Type type, const struct GNUNET_PeerIdentity *get_path, unsigned int get_path_length, const struct GNUNET_PeerIdentity *put_path, unsigned int put_path_length, struct GNUNET_TIME_Absolute exp, const struct GNUNET_HashCode * key, const void *data, size_t size) { } static void put_callback (void *cls, enum GNUNET_DHT_RouteOption options, enum GNUNET_BLOCK_Type type, uint32_t hop_count, uint32_t desired_replication_level, unsigned int path_length, const struct GNUNET_PeerIdentity *path, struct GNUNET_TIME_Absolute exp, const struct GNUNET_HashCode * key, const void *data, size_t size) { } monitor_handle = GNUNET_DHT_monitor_start (dht_handle, block_type, key, &get_callback, &get_resp_callback, &put_callback, cls);
Debugging
with gnunet−arm
Even if services are managed by gnunet−arm, you
can start them with gdb or valgrind. For
example, you could add the following lines to your
configuration file to start the DHT service in a gdb
session in a fresh xterm:
[dht] PREFIX=xterm −e gdb −−args
Alternatively, you can stop a service that was started via ARM and run it manually:
$ gnunet−arm −k dht $ gdb −−args gnunet−service−dht −L DEBUG $ valgrind gnunet−service−dht −L DEBUG
Assuming other services are well−written, they will automatically re−integrate the restarted service with the peer.
GNUnet provides a powerful logging mechanism providing log levels ERROR, WARNING, INFO and DEBUG. The current log level is configured using the $GNUNET_FORCE_LOG environmental variable. The DEBUG level is only available if −−enable−logging=verbose was used when running configure. More details about logging can be found under https://docs.gnunet.org/#Logging.
You should also probably enable the creation of core files, by setting ulimit, and echoâing 1 into /proc/sys/kernel/core\_uses\_pid. Then you can investigate the core dumps with gdb, which is often the fastest method to find simple errors.
Exercise: Add a memory leak to your service and obtain a trace pointing to the leak using valgrind while running the service from gnunet−service−arm.
GNU Free
Documentation License
license, GNU Free Documentation License Version 1.3, 3
November 2008
Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. http://fsf.org/ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
1. |
PREAMBLE |
The purpose of this License is to make a manual, textbook, or other functional and useful document free in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
This License is a kind of âcopyleftâ, which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
2. |
APPLICABILITY AND DEFINITIONS |
This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world−wide, royalty−free license, unlimited in duration, to use that work under the conditions stated herein. The âDocumentâ, below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as âyouâ. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
A âModified Versionâ of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
A âSecondary Sectionâ is a named appendix or a front−matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Documentâs overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
The âInvariant Sectionsâ are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
The âCover Textsâ are certain short passages of text that are listed, as Front−Cover Texts or Back−Cover Texts, in the notice that says that the Document is released under this License. A Front−Cover Text may be at most 5 words, and a Back−Cover Text may be at most 25 words.
A âTransparentâ copy of the Document means a machine−readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not âTransparentâ is called âOpaqueâ.
Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard−conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine−generated HTML, PostScript or PDF produced by some word processors for output purposes only.
The âTitle Pageâ means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, âTitle Pageâ means the text near the most prominent appearance of the workâs title, preceding the beginning of the body of the text.
The âpublisherâ means any person or entity that distributes copies of the Document to the public.
A section âEntitled XYZâ means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as âAcknowledgementsâ, âDedicationsâ, âEndorsementsâ, or âHistoryâ.) To âPreserve the Titleâ of such a section when you modify the Document means that it remains a section âEntitled XYZâ according to this definition.
The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
3. |
VERBATIM COPYING |
You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and you may publicly display copies.
4. |
COPYING IN QUANTITY |
If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Documentâs license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front−Cover Texts on the front cover, and Back−Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine−readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer−network location from which the general network−using public has access to download using public−standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
5. |
MODIFICATIONS |
You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
A. |
Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. | ||
B. |
List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. | ||
C. |
State on the Title page the name of the publisher of the Modified Version, as the publisher. | ||
D. |
Preserve all the copyright notices of the Document. | ||
E. |
Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. | ||
F. |
Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. | ||
G. |
Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Documentâs license notice. | ||
H. |
Include an unaltered copy of this License. | ||
I. |
Preserve the section Entitled âHistoryâ, Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled âHistoryâ in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. | ||
J. |
Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the âHistoryâ section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. | ||
K. |
For any section Entitled âAcknowledgementsâ or âDedicationsâ, Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. | ||
L. |
Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. | ||
M. |
Delete any section Entitled âEndorsementsâ. Such a section may not be included in the Modified Version. | ||
N. |
Do not retitle any existing section to be Entitled âEndorsementsâ or to conflict in title with any Invariant Section. | ||
O. |
Preserve any Warranty Disclaimers. |
If the Modified Version includes new front−matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Versionâs license notice. These titles must be distinct from any other section titles.
You may add a section Entitled âEndorsementsâ, provided it contains nothing but endorsements of your Modified Version by various partiesâfor example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
You may add a passage of up to five words as a Front−Cover Text, and a passage of up to 25 words as a Back−Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front−Cover Text and one of Back−Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
6. |
COMBINING DOCUMENTS |
You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections Entitled âHistoryâ in the various original documents, forming one section Entitled âHistoryâ; likewise combine any sections Entitled âAcknowledgementsâ, and any sections Entitled âDedicationsâ. You must delete all sections Entitled âEndorsements.â
7. |
COLLECTIONS OF DOCUMENTS |
You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
8. |
AGGREGATION WITH INDEPENDENT WORKS |
A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an âaggregateâ if the copyright resulting from the compilation is not used to limit the legal rights of the compilationâs users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Documentâs Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
9. |
TRANSLATION |
Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
If a section in the Document is Entitled âAcknowledgementsâ, âDedicationsâ, or âHistoryâ, the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
10. |
TERMINATION |
You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.
However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.
11. |
FUTURE REVISIONS OF THIS LICENSE |
The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License âor any later versionâ applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxyâs public statement of acceptance of a version permanently authorizes you to choose that version for the Document.
12. |
RELICENSING |
âMassive Multiauthor Collaboration Siteâ (or âMMC Siteâ) means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A âMassive Multiauthor Collaborationâ (or âMMCâ) contained in the site means any set of copyrightable works thus published on the MMC site.
âCC−BY−SAâ means the Creative Commons Attribution−Share Alike 3.0 license published by Creative Commons Corporation, a not−for−profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.
âIncorporateâ means to publish or republish a Document, in whole or in part, as part of another Document.
An MMC is âeligible for relicensingâ if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.
The operator of an MMC Site may republish an MMC contained in the site under CC−BY−SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.
ADDENDUM: How to use this License for your documents
To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
Copyright (C) year your name. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front−Cover Texts, and no Back−Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''.
If you have Invariant Sections, Front−Cover Texts and Back−Cover Texts, replace the âwithâ¦Texts.â line with this:
with the Invariant Sections being list their titles, with the Front−Cover Texts being list, and with the Back−Cover Texts being list.
If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
Doxygen
The code documentation will soon be ported here. Until then
you can find it in the old place:
https://docs.gnunet.org/doxygen/
On this page you can find links to our RFC−style technical protocol specifications:
• |
LSD0000: Reserved | ||
• |
LSD0001: The GNU Name System | ||
• |
LSD0002: re:claimID | ||
• |
LSD0003: Byzantine Fault Tolerant Set Reconciliation (work−in−progress) | ||
• |
LSD0004: The R5N Distributed Hash Table (work−in−progress) |
The GNUnet Assigned Numbers Authority (GANA) contains various registries we maintain, for GNUnet other projects that need names and numbers for use in network protocols. If you need to open a new registry, please feel free to contact us at [email protected].
The registries can be found here: https://git.gnunet.org/gana.git
History
Barry Leiba wrote on April 4th 2020 that âNeither IANA
nor participants in the IETF will have any necessary
expertise to evaluate registration requests in the sort of
registry described, and no one will be well served by the
creation of such a registry at IANA. It would be far better
to have a registration process be described in this document
involving experts from the industry as reviewers and
maintenance of the registrations by an industry
organization, rather than by IANA.â
So here we are. As IETF/IANA âlack the necessary expertise to operate a registryâ for names and numbers used in network protocols, the GNUnet project is happy to step up.
License
The GANA database is licensed under the GPL. See
COPYING in the Git repository.
Design
Each registry must have a unique name and all associated
information lives in a directory under that unique name in
the Git repository.
Each registry must include at least the following files:
• |
README[.*]: document describing the purpose of the registry in English | ||
• |
POLICY[.*]: registration policy, explaining required fields and the procedure for adding, updating and deleting entries | ||
• |
registry.rec: GNU recutils data file with all of the current entries in the registry | ||
• |
Makefile: GNU make makefile with a make check target to run the validation logic. Ideally, the registry.rec should be written such that the check target is simply invoking recfix −−check registry.rec. Additional targets to convert data.rec to various formats may be defined. In particular, see format.sh in the root directory of the Git repository (try −−help). |
The
graphical configuration interface
If you also would like to use gnunet−gtk and
gnunet−setup (highly recommended for
beginners), do:
Configuring
your peer
This chapter will describe the various configuration options
in GNUnet.
The easiest way to configure your peer is to use the gnunet−setup tool. gnunet−setup is part of the gnunet−gtk package. You might have to install it separately.
Many of the specific sections from this chapter actually are linked from within gnunet−setup to help you while using the setup tool.
While you can also configure your peer by editing the configuration file by hand, this is not recommended for anyone except for developers as it requires a more in−depth understanding of the configuration files and internal dependencies of GNUnet.
Configuration
of the HOSTLIST proxy settings
The hostlist client can be configured to use a proxy to
connect to the hostlist server. This functionality can be
configured in the configuration file directly or using the
gnunet−setup tool.
The hostlist client supports the following proxy types at the moment:
• |
HTTP and HTTP 1.0 only proxy |
|||
• |
SOCKS 4/4a/5/5 with hostname |
In addition authentication at the proxy with username and password can be configured.
To configure proxy support for the hostlist client in the gnunet−setup tool, select the "hostlist" tab and select the appropriate proxy type. The hostname or IP address (including port if required) has to be entered in the "Proxy hostname" textbox. If required, enter username and password in the "Proxy username" and "Proxy password" boxes. Be aware that this information will be stored in the configuration in plain text (TODO: Add explanation and generalize the part in Chapter 3.6 about the encrypted home).
Configuration
of the HTTP and HTTPS transport plugins
The client parts of the http and https transport plugins can
be configured to use a proxy to connect to the hostlist
server. This functionality can be configured in the
configuration file directly or using the gnunet−setup
tool.
Both the HTTP and HTTPS clients support the following proxy types at the moment:
• |
HTTP 1.1 proxy |
|||
• |
SOCKS 4/4a/5/5 with hostname |
In addition authentication at the proxy with username and password can be configured.
To configure proxy support for the clients in the gnunet−setup tool, select the "transport" tab and activate the respective plugin. Now you can select the appropriate proxy type. The hostname or IP address (including port if required) has to be entered in the "Proxy hostname" textbox. If required, enter username and password in the "Proxy username" and "Proxy password" boxes. Be aware that these information will be stored in the configuration in plain text.
GTK
File−sharing User Interface
This chapter describes first steps for file−sharing
with GNUnet. To start, you should launch
gnunet−fs−gtk.
As we want to be sure that the network contains the data that we are looking for for testing, we need to begin by publishing a file.
Publishing
To publish a file, select "File Sharing" in the
menu bar just below the "Statistics" icon, and
then select "Publish" from the menu.
Afterwards, the following publishing dialog will appear:
|image3|
In this dialog, select the "Add File" button. This will open a file selection dialog:
|image4|
Now, you should select a file from your computer to be published on GNUnet. To see more of GNUnetâs features later, you should pick a PNG or JPEG file this time. You can leave all of the other options in the dialog unchanged. Confirm your selection by pressing the "OK" button in the bottom right corner. Now, you will briefly see a "Messages..." dialog pop up, but most likely it will be too short for you to really read anything. That dialog is showing you progress information as GNUnet takes a first look at the selected file(s). For a normal image, this is virtually instant, but if you later import a larger directory you might be interested in the progress dialog and potential errors that might be encountered during processing. After the progress dialog automatically disappears, your file should now appear in the publishing dialog:
|image5|
Now, select the file (by clicking on the file name) and then click the "Edit" button. This will open the editing dialog:
|image6|
In this dialog, you can see many details about your file. In the top left area, you can see meta data extracted about the file, such as the original filename, the mimetype and the size of the image. In the top right, you should see a preview for the image (if GNU libextractor was installed correctly with the respective plugins). Note that if you do not see a preview, this is not a disaster, but you might still want to install more of GNU libextractor in the future. In the bottom left, the dialog contains a list of keywords. These are the keywords under which the file will be made available. The initial list will be based on the extracted meta data. Additional publishing options are in the right bottom corner. We will now add an additional keyword to the list of keywords. This is done by entering the keyword above the keyword list between the label "Keyword" and the "Add keyword" button. Enter "test" and select "Add keyword". Note that the keyword will appear at the bottom of the existing keyword list, so you might have to scroll down to see it. Afterwards, push the "OK" button at the bottom right of the dialog.
You should now be back at the "Publish content on GNUnet" dialog. Select "Execute" in the bottom right to close the dialog and publish your file on GNUnet! Afterwards, you should see the main dialog with a new area showing the list of published files (or ongoing publishing operations with progress indicators).
Searching
Below the menu bar, there are four entry widges labeled
"Namespace", "Keywords",
"Anonymity" and "Mime−type" (from
left to right). These widgets are used to control searching
for files in GNUnet. Between the "Keywords" and
"Anonymity" widgets, there is also a big
"Search" button, which is used to initiate the
search. We will ignore the "Namespace",
"Anonymity" and "Mime−type"
options in this tutorial, please leave them empty. Instead,
simply enter "test" under "Keywords" and
press "Search". Afterwards, you should immediately
see a new tab labeled after your search term, followed by
the (current) number of search results â
"(15)" in our screenshot. Note that your results
may vary depending on what other users may have shared and
how your peer is connected.
You can now select one of the search results. Once you do this, additional information about the result should be displayed on the right. If available, a preview image should appear on the top right. Meta data describing the file will be listed at the bottom right.
Once a file is selected, at the bottom of the search result list a little area for downloading appears.
Downloading
In the downloading area, you can select the target directory
(default is "Downloads") and specify the desired
filename (by default the filename it taken from the meta
data of the published file). Additionally, you can specify
if the download should be anonymous and (for directories) if
the download should be recursive. In most cases, you can
simply start the download with the "Download!"
button.
Once you selected download, the progress of the download will be displayed with the search result. You may need to resize the result list or scroll to the right. The "Status" column shows the current status of the download, and "Progress" how much has been completed. When you close the search tab (by clicking on the "X" button next to the "test" label), ongoing and completed downloads are not aborted but moved to a special "*" tab.
You can remove completed downloads from the "*" tab by clicking the cleanup button next to the "*". You can also abort downloads by right clicking on the respective download and selecting "Abort download" from the menu.
Thatâs it, you now know the basics for file−sharing with GNUnet!
This will be available here soon. Until then, look here: https://www.gnunet.org/en/faq.html
GNUnet Project
2022, GNUnet Project