aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--.gitmodules3
-rw-r--r--archetypes/default.md10
-rw-r--r--archetypes/page.md8
-rw-r--r--archetypes/section.md6
-rw-r--r--config.toml23
-rw-r--r--content/_index.md19
-rw-r--r--content/about.md35
-rw-r--r--content/compopt.html434
-rw-r--r--content/contribute.md83
-rw-r--r--content/dev-tut-1.md73
-rw-r--r--content/documentation.md43
-rw-r--r--content/download.md25
-rw-r--r--content/extra.md169
-rw-r--r--content/faq.md121
-rw-r--r--content/install.md52
-rw-r--r--content/manuals.md17
-rw-r--r--content/performance.md73
-rw-r--r--content/requirements.md71
-rw-r--r--content/tutorial-1.md153
-rw-r--r--content/tutorial-2.md297
-rw-r--r--content/tutorial-3.md210
-rw-r--r--content/tutorial-4.md123
-rw-r--r--static/images/avgrttlocal.pngbin0 -> 45270 bytes
-rw-r--r--static/images/goodputlocalmbits.pngbin0 -> 47697 bytes
-rw-r--r--static/images/icon.pngbin0 -> 922 bytes
-rw-r--r--static/images/ouroboros_tut1_overview.pngbin0 -> 35029 bytes
-rw-r--r--static/images/ouroboros_tut2_enrolled.pngbin0 -> 10816 bytes
-rw-r--r--static/images/ouroboros_tut2_overview.pngbin0 -> 9310 bytes
-rw-r--r--static/images/throughputlocalpps.pngbin0 -> 47844 bytes
-rw-r--r--static/patches/ouroboros-ioq3.patch415
m---------themes/terrassa0
32 files changed, 2467 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c5ae86
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+_site/
+.sass-cache/
+public/
+*~ \ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..30b532e
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "themes/terrassa"]
+ path = themes/terrassa
+ url = https://github.com/danielkvist/hugo-terrassa-theme.git
diff --git a/archetypes/default.md b/archetypes/default.md
new file mode 100644
index 0000000..33b7e3d
--- /dev/null
+++ b/archetypes/default.md
@@ -0,0 +1,10 @@
+---
+title: "{{ replace .Name "-" " " | title }}"
+description: ""
+date: {{ .Date }}
+publishDate: {{ .Date }}
+author: "John Doe"
+images: []
+draft: true
+tags: []
+--- \ No newline at end of file
diff --git a/archetypes/page.md b/archetypes/page.md
new file mode 100644
index 0000000..2789892
--- /dev/null
+++ b/archetypes/page.md
@@ -0,0 +1,8 @@
+---
+title: "{{ replace .Name "-" " " | title }}"
+description: ""
+images: []
+draft: true
+menu: main
+weight: 0
+--- \ No newline at end of file
diff --git a/archetypes/section.md b/archetypes/section.md
new file mode 100644
index 0000000..efe83a1
--- /dev/null
+++ b/archetypes/section.md
@@ -0,0 +1,6 @@
+---
+title : "{{ replace .Name "-" " " | title }}"
+description: ""
+draft: true
+weight: 0
+--- \ No newline at end of file
diff --git a/config.toml b/config.toml
new file mode 100644
index 0000000..dfc0b79
--- /dev/null
+++ b/config.toml
@@ -0,0 +1,23 @@
+baseURL = "https://ouroboros.rocks/"
+languageCode = "en-us"
+title = "Ouroboros"
+theme = "terrassa"
+enableRobotsTXT = true
+DefaultContentLanguage = "en"
+
+[params]
+description = "Ouroboros is a decentralized packet network"
+favicon = "/images/icon.png"
+logo = "/images/icon.png"
+
+[params.contact]
+email = "ouroboros@freelists.org"
+
+[params.copy]
+copy = ""
+
+[menu]
+[[menu.main]]
+name = "Repository"
+weight = 40
+url = "/cgit"
diff --git a/content/_index.md b/content/_index.md
new file mode 100644
index 0000000..6a089f1
--- /dev/null
+++ b/content/_index.md
@@ -0,0 +1,19 @@
+---
+title: "A decentralized packet network"
+---
+
+Ouroboros is a decentralized packet network for POSIX operating
+systems.
+
+Ouroboros provides a simple and minimal API for synchronous and
+asynchronous IPC. Ouroboros can use IP, Ethernet LLC, Ethernet DIX or
+Ethernet PHY (using the experimental raptor NetFPGA implementation) at
+the lowest layer.
+
+The best place to start exploring Ouroboros is this introduction
+presented at [FOSDEM
+2018](https://www.fosdem.org/2018/schedule/event/ipc/).
+
+This website is currently under construction and undergoing frequent
+updates. The documentation is still sparse, please don't hesitate to
+[contact us](/contribute) with any questions you might have.
diff --git a/content/about.md b/content/about.md
new file mode 100644
index 0000000..0ac9966
--- /dev/null
+++ b/content/about.md
@@ -0,0 +1,35 @@
+---
+title: "About"
+menu: "main"
+draft: false
+menu:
+ main:
+ weight: 30
+---
+
+The Ouroboros project is a decentralized packet network implementation
+started by Sander Vrijders and Dimitri Staessens.
+
+### Licenses
+
+Ouroboros is [free
+software](https://www.gnu.org/philosophy/free-sw.html). The libraries
+are available under the [GNU Lesser Public License (LGPL) version
+2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html). The
+daemons are available under the [GNU Public License (GPL) version
+2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html). The tools
+are availabe under the [3-Clause BSD
+License](https://opensource.org/licenses/BSD-3-Clause).
+
+Ouroboros logos © Dimitri Staessens. All Rights Reserved.
+
+### Disclaimer
+
+Ouroboros is distributed in the hope that it will be useful, but without
+any warranty; without even the implied warranty of merchantability or
+fitness for a particular purpose.
+
+At present, Ouroboros is a research prototype. Although it is developed
+with utmost care, it may expose the host system to unknown security
+risks and attack vectors. As such, we recommend to test it in a
+contained environment.
diff --git a/content/compopt.html b/content/compopt.html
new file mode 100644
index 0000000..93ad1c4
--- /dev/null
+++ b/content/compopt.html
@@ -0,0 +1,434 @@
+---
+title: "Compilation options"
+draft: false
+---
+
+<p>
+ Below is a list of the compile-time configuration options for
+ Ouroboros. These can be set using
+</p>
+<pre><code>$ cmake -D&lt;option&gt;=&lt;value&gt; ..</code></pre>
+<p>or using</p>
+<pre><code>ccmake .</code></pre>
+<p>
+ Options will only show up in ccmake if they are relevant for
+ your system configuration. The default value for each option
+ is <u>underlined</u>. Boolean values will print as ON/OFF in
+ ccmake instead of True/False.
+</p>
+<table>
+ <tr>
+ <th>Option</th>
+ <th>Description</th>
+ <th>Values</th>
+ </tr>
+ <tr>
+ <th colspan="3">Compilation options</th>
+ </tr>
+ <tr>
+ <td>CMAKE_BUILD_TYPE</td>
+ <td>
+ Set the build type for Ouroboros. Debug builds will add some
+ extra logging. The debug build can further enable the
+ address sanitizer (ASan) thread sanitizer (TSan) and leak
+ sanitizer (LSan) options.
+ </td>
+ <td>
+ <u>Release</u>, Debug, DebugASan, DebugTSan, DebugLSan
+ </td>
+ </tr>
+ <tr>
+ <td>CMAKE_INSTALL_PREFIX</td>
+ <td>
+ Set a path prefix in order to install Ouroboros in a
+ sandboxed environment. Default is a system-wide install.
+ </td>
+ <td>
+ &lt;path&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>DISABLE_SWIG</td>
+ <td>
+ Disable SWIG support.
+ </td>
+ <td>
+ True, <u>False</u>
+ </td>
+ </tr>
+ <tr>
+ <th colspan="3">Library options</th>
+ <tr>
+ <tr>
+ <td>DISABLE_FUSE</td>
+ <td>
+ Disable FUSE support, removing the virtual filesystem under
+ &lt;FUSE_PREFIX&gt;.
+ </td>
+ <td>
+ True, <u>False</u>
+ </td>
+ </tr>
+ <tr>
+ <td>FUSE_PREFIX</td>
+ <td>
+ Set the path where the fuse system should be
+ mounted. Default is /tmp/ouroboros.
+ </td>
+ <td>
+ &lt;path&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>DISABLE_LIBGCRYPT</td>
+ <td>
+ Disable support for using the libgcrypt library for
+ cryptographically secure random number generation and
+ hashing.
+ </td>
+ <td>
+ True, <u>False</u>
+ </td>
+ </tr>
+ <tr>
+ <td>DISABLE_OPENSSL</td>
+ <td>
+ Disable support for the libssl library for cryptographic
+ random number generation and hashing.
+ </td>
+ <td>
+ True, <u>False</u>
+ </td>
+ </tr>
+ <tr>
+ <td>DISABLE_ROBUST_MUTEXES</td>
+ <td>
+ Disable
+ <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutexattr_getrobust.html">
+ robust mutex
+ </a>
+ support. Without robust mutex support, Ouroboros may lock up
+ if processes are killed using SIGKILL.
+ </td>
+ <td>
+ True, <u>False</u>
+ </td>
+ </tr>
+ <tr>
+ <td>PTHREAD_COND_CLOCK</td>
+ <td>
+ Set the
+ <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/time.h.html">
+ clock type
+ </a>
+ to use for timeouts for pthread condition variables. Default
+ on Linux/FreeBSD: CLOCK_MONOTONIC. Default on OS X:
+ CLOCK_REALTIME.
+ </td>
+ <td>
+ &lt;clock_id_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <th colspan="3">Shared memory system options</th>
+ <tr>
+ <tr>
+ <td>SHM_PREFIX</td>
+ <td>
+ Set a prefix for the shared memory filenames. The mandatory
+ leading
+ <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html">
+ slash
+ </a>
+ is added by the build system. Default is "ouroboros".
+ </td>
+ <td>
+ &lt;size_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>SHM_BUFFER_SIZE</td>
+ <td>
+ Set the maximum total number of packet blocks Ouroboros
+ can buffer at any point in time. Must be a power of 2.
+ </td>
+ <td>
+ &lt;size_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>SHM_RDRB_BLOCK_SIZE</td>
+ <td>
+ Set the size of a packet block. Default: page size of the
+ system.
+ </td>
+ <td>
+ &lt;size_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>SHM_RDRB_MULTI-BLOCK</td>
+ <td>
+ Allow packets that are larger than a single packet block.
+ </td>
+ <td>
+ <u>True</u>, False
+ </td>
+ </tr>
+ <tr>
+ <td>DU_BUFF_HEADSPACE</td>
+ <td>
+ Set the amount of space to allow for the addition of
+ protocol headers when a new packet buffer is passed to the
+ system. Default: 128 bytes.
+ </td>
+ <td>
+ &lt;size_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>DU_BUFF_TAILSPACE</td>
+ <td>
+ Set the amount of space to allow for the addition of
+ protocol tail information (CRCs) when a new packet buffer
+ is passed to the system. Default: 32 bytes.
+ </td>
+ <td>
+ &lt;size_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <th colspan="3">IRMd options</th>
+ </tr>
+ <tr>
+ <td>SYS_MAX_FLOWS</td>
+ <td>
+ The maximum number of flows this Ouroboros system can
+ allocate. Default: 10240.
+ </td>
+ <td>
+ &lt;size_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>SOCKET_TIMEOUT</td>
+ <td>
+ The IRMd sends commands to IPCPs over UNIX sockets. This
+ sets the timeout for such commands in milliseconds. Some
+ commands can be set independently. Default: 1000.
+ </td>
+ <td>
+ &lt;time_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>BOOTSTRAP_TIMEOUT</td>
+ <td>
+ Timeout for the IRMd to wait for a response to a bootstrap
+ command from an IPCP in milliseconds. Default: 5000.
+ </td>
+ <td>
+ &lt;time_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>ENROLL_TIMEOUT</td>
+ <td>
+ Timeout for the IRMd to wait for a response to an enroll
+ command from an IPCP in milliseconds. Default: 60000.
+ </td>
+ <td>
+ &lt;time_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>CONNECT_TIMEOUT</td>
+ <td>
+ Timeout for the IRMd to wait for a response to a connect
+ command from an IPCP in milliseconds. Default: 5000.
+ </td>
+ <td>
+ &lt;time_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>REG_TIMEOUT</td>
+ <td>
+ Timeout for the IRMd to wait for a response to a register
+ command from an IPCP in milliseconds. Default: 3000.
+ </td>
+ <td>
+ &lt;time_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>QUERY_TIMEOUT</td>
+ <td>
+ Timeout for the IRMd to wait for a response to a query
+ command from an IPCP in milliseconds. Default: 3000.
+ </td>
+ <td>
+ &lt;time_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>IRMD_MIN_THREADS</td>
+ <td>
+ The minimum number of threads in the threadpool the IRMd
+ keeps waiting for commands. Default: 8.
+ </td>
+ <td>
+ &lt;size_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>IRMD_ADD_THREADS</td>
+ <td>
+ The number of threads the IRMd will create if the current
+ available threadpool is lower than
+ IRMD_MIN_THREADS. Default: 8.
+ </td>
+ <td>
+ &lt;size_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <th colspan="3">IPCP options</th>
+ </tr>
+ <tr>
+ <td>DISABLE_RAPTOR</td>
+ <td>
+ Disable support for the raptor NetFPGA implementation.
+ </td>
+ <td>
+ True, <u>False</u>
+ </td>
+ </tr>
+ <tr>
+ <td>DISABLE_BPF</td>
+ <td>
+ Disable support for the Berkeley Packet Filter device
+ interface for the Ethernet LLC layer. If no suitable
+ interface is found, the LLC layer will not be built.
+ </td>
+ <td>
+ True, <u>False</u>
+ </td>
+ </tr>
+ <tr>
+ <td>DISABLE_NETMAP</td>
+ <td>
+ Disable <a href="http://info.iet.unipi.it/~luigi/netmap/">netmap</a>
+ support for the Ethernet LLC layer. If no suitable interface
+ is found, the LLC layer will not be built.
+ </td>
+ <td>
+ True, <u>False</u>
+ </td>
+ </tr>
+ <tr>
+ <td>DISABLE_RAW_SOCKETS</td>
+ <td>
+ Disable raw sockets support for the Ethernet LLC layer. If
+ no suitable interface is found,the LLC layer will not be
+ built.
+ </td>
+ <td>
+ True, <u>False</u>
+ </td>
+ </tr>
+ <tr>
+ <td>DISABLE_DDNS</td>
+ <td>
+ Disable Dynamic Domain Name System support for the UDP
+ layer.
+ </td>
+ <td>
+ True, <u>False</u>
+ </td>
+ </tr>
+ <tr>
+ <td>IPCP_SCHED_THR_MUL</td>
+ <td>
+ The number of scheduler threads an IPCP runs per QoS
+ cube. Default is 2.
+ </td>
+ <td>
+ &lt;size_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>IPCP_QOS_CUBE_BE_PRIORITY</td>
+ <td>
+ Priority for the best effort qos cube scheduler
+ thread. This is mapped to a system value. Scheduler
+ threads have at least half the system max priority value.
+ </td>
+ <td>
+ <u>0</u>..99
+ </td>
+ </tr>
+ <tr>
+ <td>IPCP_QOS_CUBE_VIDEO_PRIORITY</td>
+ <td>
+ Priority for the video qos cube scheduler thread. This is
+ mapped to a system value. Scheduler threads have at least
+ half the system max priority value.
+ </td>
+ <td>
+ 0..<u>90</u>..99
+ </td>
+ </tr>
+ <tr>
+ <td>IPCP_QOS_CUBE_VOICE_PRIORITY</td>
+ <td>
+ Priority for the voice qos cube scheduler thread. This is
+ mapped to a system value. Scheduler threads have at least
+ half the system max priority value.
+ </td>
+ <td>
+ 0..<u>99</u>
+ </td>
+ </tr>
+ <tr>
+ <td>IPCP_FLOW_STATS</td>
+ <td>
+ Enable statistics for the data transfer component.
+ </td>
+ <td>
+ True, <u>False</u>
+ </td>
+ </tr>
+
+ <tr>
+ <td>PFT_SIZE</td>
+ <td>
+ The forwarding table in the normal IPCP uses a
+ hashtable. This sets the size of this hash table. Default: 4096.
+ </td>
+ <td>
+ &lt;size_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>IPCP_MIN_THREADS</td>
+ <td>
+ The minimum number of threads in the threadpool the IPCP
+ keeps waiting for commands. Default: 4.
+ </td>
+ <td>
+ &lt;size_t&gt;
+ </td>
+ </tr>
+ <tr>
+ <td>IPCP_ADD_THREADS</td>
+ <td>
+ The number of threads the IPCP will create if the current
+ available threadpool is lower than
+ IPCP_MIN_THREADS. Default:4.
+ </td>
+ <td>
+ &lt;size_t&gt;
+ </td>
+ </tr>
+</table>
diff --git a/content/contribute.md b/content/contribute.md
new file mode 100644
index 0000000..8cd6b56
--- /dev/null
+++ b/content/contribute.md
@@ -0,0 +1,83 @@
+---
+title: "Contribute"
+menu: "main"
+draft: false
+menu:
+ main:
+ weight: 20
+---
+
+General discussion and support
+------------------------------
+
+For general discussion of Ouroboros,
+[subscribe](https://www.freelists.org/list/ouroboros) to our mailing
+list: [ouroboros@freelists.org](mailto:ouroboros@freelists.org).
+
+For day-to-day discussions, join our IRC chat:
+[\#ouroboros](irc://irc.freenode.net/ouroboros).
+
+Contact us on twitter: @ODecentralize
+
+Contributing code
+-----------------
+
+Contributions should be sent as patches to the mailing list, using your
+real name and real e-mail address.
+
+The git repository contains three branches:
+
+- master: Contains the most stable version of Ouroboros.
+- testing: Contains tested code but may still contain bugs.
+- be: Contains untested but compiling code.
+
+New development is done against the 'be' branch of the git repository.
+The testing and master branches take only bugfixes.
+
+Bug reporting
+-------------
+
+Please report all bugs [here](/bugzilla). When reporting a bug, please
+do the following:
+
+1. Provide a description of the bug. How did you get it and which
+ version of Ouroboros were you using? Which Operating System are you
+ on?
+2. Provide as much technical information as possible (system logs,
+ debug traces, \...).
+3. If possible, provide a minimal code example to reproduce the bug.
+4. If you can provide a bugfix, provide it against the HEAD of the most
+ stable branch where the bug is present and send the patch to the
+ mailing list.
+
+Todo list
+---------
+
+We are currently looking for
+
+- Testing and bugfixing.
+- Integration testing for the build system beyond the "make check"
+ unit tests.
+- People that are interested in setting up some nodes to establish a
+ global testing layer.
+- Non-blocking flow allocation: Allow specifying a {0, 0} timespec to
+ return immediately and use fevent() to know when the flow is ready
+ (or allocation failed).
+- Asynchronous IPC over the UNIX sockets. For each command to the
+ IRMd, we create a UNIX socket, send the request and wait for the
+ response. This could be changed so that there is only a single UNIX
+ socket that is used for all messaging. This would simplify parallel
+ querying of the IPCPs and speed up flow allocation. The far-future
+ option is to ditch UNIX sockets and bootstrap Ouroboros local IPC
+ over itself.
+- ECDH-AES encryption using libssl and/or libgcrypt. The goal is to
+ support both libraries so that we have a fallback should major bugs
+ be discovered in one of them.
+- Customized packet serialization to remove the dependency on Google
+ Protocol Buffers. We like GPB, but it\'s not perfect. Importing
+ .proto files may give rise to multiple definitions and we found no
+ way to solve that.
+- Caching for the DHT.
+- Cross-compilation to OpenWRT (musl).
+- Ported applications! If you want to add native Ouroboros support for
+ your applications, just let us know and we will help you out!
diff --git a/content/dev-tut-1.md b/content/dev-tut-1.md
new file mode 100644
index 0000000..ceac8b6
--- /dev/null
+++ b/content/dev-tut-1.md
@@ -0,0 +1,73 @@
+---
+title: "Developer tutorial 1: Writing your first Ouroboros C program"
+draft: false
+---
+
+This tutorial will guide you to write your first ouroboros program. It
+will use the basic Ouroboros IPC Application Programming Interface. It
+will has a client and a server that send a small message from the client
+to the server.
+
+We will explain how to connect two applications. The server application
+uses the flow_accept() call to accept incoming connections and the
+client uses the flow_alloc() call to connect to the server. The
+flow_accept and flow_alloc call have the following definitions:
+
+```
+int flow_accept(qosspec_t * qs, const struct timespec * timeo);
+int flow_alloc(const char * dst, qosspec_t * qs, const struct
+timespec * timeo);
+```
+
+On the server side, the flow_accept() call is a blocking call that will
+wait for an incoming flow from a client. On the client side, the
+flow_alloc() call is a blocking call that allocates a flow to *dst*.
+Both calls return an non-negative integer number describing a "flow
+descriptor", which is very similar to a file descriptor. On error, they
+will return a negative error code. (See the [man
+page](/man/man3/flow_alloc.html) for all details). If the *timeo*
+parameter supplied is NULL, the calls will block indefinitely, otherwise
+flow_alloc() will return -ETIMEDOUT when the time interval provided by
+*timeo* expires. We are working on implementing non-blocking versions if
+the provided *timeo* is 0.
+
+After the flow is allocated, the flow_read() and flow_write() calls
+are used to read from the flow descriptor. They operate just like the
+read() and write() POSIX calls. The default behaviour is that these
+calls will block. To release the resource, the flow can be deallocated
+using flow_dealloc.
+
+```
+ssize_t flow_write(int fd, const void * buf, size_t count);
+ssize_t flow_read(int fd, void * buf, size_t count); int
+flow_dealloc(int fd);
+```
+
+So a very simple application would just need a couple of lines of code
+for both the server and the client:
+
+```
+/* server side */
+char msg[BUF_LEN];
+int fd = flow_accept(NULL, NULL);
+flow_read(fd, msg, BUF_LEN);
+flow_dealloc(fd);
+
+/* client side */
+char * msg = "message";
+int fd = flow_alloc("server", NULL, NULL);
+flow_write(fd, msg, strlen(msg));
+flow_dealloc(fd);
+```
+
+The full code for an example is the
+[oecho](/cgit/ouroboros/tree/src/tools/oecho/oecho.c)
+application in the tools directory.
+
+To compile your C program from the command line, you have to link
+-lourobos-dev. For instance, in the Ouroboros repository, you can do
+
+```
+cd src/tools/oecho
+gcc -louroboros-dev oecho.c -o oecho
+``` \ No newline at end of file
diff --git a/content/documentation.md b/content/documentation.md
new file mode 100644
index 0000000..ed1c3d3
--- /dev/null
+++ b/content/documentation.md
@@ -0,0 +1,43 @@
+---
+title: "Documentation"
+draft: false
+menu: "main"
+menu:
+ main:
+ weight: 10
+---
+
+# Getting started
+
+* [Requirements](/requirements/)
+* [Download Ouroboros](/download/)
+* [Installing Ouroboros](/install/)
+* [Compilation options](/compopt/)
+
+# User tutorials
+
+These tutorials will be kept up-to-date for the latest version of
+Ouroboros. Check the version that is installed on your system using:
+
+```
+$ irmd --version
+```
+
+The output shown in the tutorials uses a [*debug*](/compopt) build
+of Ouroboros, with FUSE installed and IPCP\_FLOW\_STATS enabled to show
+some additional details of what is happening.
+
+* [Tutorial 1: Local test](/tutorial-1/)
+* [Tutorial 2: Adding a layer](/tutorial-2/)
+* [Tutorial 3: IPCP statistics](/tutorial-3/)
+* [Tutorial 4: Connecting two machines over Ethernet](/tutorial-4/)
+
+# Developer tutorials
+
+* [Developer tutorial 1: Writing your first Ouroboros C program](/dev-tut-1/)
+
+# Extra info
+
+* [Manual pages](/manuals/)
+* [Frequently Asked Questions (FAQ)](/faq/)
+* [Performance tests](/performance/)
diff --git a/content/download.md b/content/download.md
new file mode 100644
index 0000000..5afb6c3
--- /dev/null
+++ b/content/download.md
@@ -0,0 +1,25 @@
+---
+title: "Download Ouroboros"
+draft: false
+---
+
+### Get Ouroboros
+
+**Packages:**
+
+For ArchLinux users, the easiest way to try Ouroboros is via the [Arch
+User Repository](https://aur.archlinux.org/packages/ouroboros-git/),
+which will also install all dependencies.
+
+**Source:**
+
+You can clone the [repository](/cgit/ouroboros) over http or https or
+git:
+
+```
+$ git clone http://ouroboros.rocks/git/ouroboros
+$ git clone https://ouroboros.rocks/git/ouroboros
+$ git clone git://ouroboros.rocks/ouroboros
+```
+
+Or download a [snapshot](cgit/ouroboros/).
diff --git a/content/extra.md b/content/extra.md
new file mode 100644
index 0000000..4cdddab
--- /dev/null
+++ b/content/extra.md
@@ -0,0 +1,169 @@
+---
+title: "Extra"
+draft: false
+menu: "main"
+menu:
+ main:
+ weight: 40
+---
+
+Raptor
+------
+
+Raptor is a proof-of-concept FPGA demonstrator for running Ouroboros
+directly over Ethernet PHY (OSI L1). For this, it uses the [NetFPGA
+10G](http://netfpga.org/site/#/systems/3netfpga-10g/details/) platform,
+which has Broadcom AEL2005 PHY 10G Ethernet devices. Raptor is
+point-to-point and does not use addresses. It is available for Linux
+only and support is minimal. If you are interested in trying it out,
+please contact us via the ouroboros mailing list or the IRC channel.
+
+You can clone the [raptor repository](/cgit/raptor/):
+
+```
+$ git clone http://ouroboros.rocks/git/raptor
+$ git clone https://ouroboros.rocks/git/raptor
+$ git clone git://ouroboros.rocks/raptor
+```
+
+There are two directories. The *linux* directory contains the kernel
+module, the *netfpga10g* directory contains the files necessary to build
+the FPGA design.
+
+To build and install the kernel module:
+
+```
+$ make
+$ sudo make install
+```
+
+You can now load/unload the raptor kernel module:
+
+```
+$ sudo modprobe raptor
+$ sudo rmmod raptor
+```
+
+To uninstall the module:
+
+```
+$ sudo make uninstall
+```
+
+You will need to get some cores (such as PCIe and XAUI) from Xilinx in
+order to build this project using the provided tcl script. Detailed
+instructions on how to build the NetFPGA project under construction.
+
+Raptor is integrated in Ouroboros and the raptor IPCP will be built if
+the kernel module is installed in the default location.
+
+Raptor was developed as part of the master thesis (in Dutch)
+"Implementatie van de Recursive Internet Architecture op een FPGA
+platform" by Alexander D'hoore. The kernel module is licensed under
+the [GNU Public License (GPL) version
+2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html). The NetFPGA
+design is available under the [GNU Lesser Public License (LGPL) version
+2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html).
+
+ioq3
+----
+
+As a demo, we have added Ouroboros support to the
+[ioq3](https://github.com/ioquake/ioq3) game engine. ioq3 is a fork of
+ID software's [Quake III Arena GPL Source
+Release](https://github.com/id-Software/Quake-III-Arena). The port is
+available as a [patch](/patches/ouroboros-ioq3.patch). The servers
+currently only work in dedicated mode (there is no way yet to start a
+server from the client).
+
+To get the demo, first get the latest ioq3 sources:
+
+```
+$ git clone https://github.com/ioquake/ioq3.git
+$ cd ioq3
+```
+
+Copy the patch via the link above, or get it via wget:
+
+```
+$ wget https://ouroboros.rocks/patches/ouroboros-ioq3.patch
+```
+
+Apply the patch to the ioq3 code:
+
+```
+$ git apply ouroboros-ioq3.patch
+```
+
+With Ouroboros installed, build the ioq3 project in standalone mode:
+
+```
+$ STANDALONE=1 make
+```
+
+You may need to install some dependencies like SDL2, see the [ioq3
+documentation](http://wiki.ioquake3.org/Building_ioquake3).
+
+The ioq3 project only supplies the game engine. To play Quake III Arena,
+you need the original game files and a valid key. Various open source
+games make use of the engine. We wil detail the procedure for running
+OpenArena in your ioq3 build directory.
+
+Go to your build directory:
+
+```
+$ cd build/<release_dir>/
+```
+
+To run OpenArena, you only need to the game files. First download the
+zip archive (openarena-0.8.8.zip) from the [OpenArena
+website](http://www.openarena.ws) (or via wget) and then extract the
+baseoa folder:
+
+```
+$ wget http://www.openarena.ws/request.php?4 -O openarena-0.8.8.zip
+$ unzip -j openarena-0.8.8.zip 'openarena-0.8.8/baseoa/*' -d
+./baseoa
+```
+
+Make sure you have a local Ouroboros layer running in your system (see
+[this](/tutorial-1/).
+
+To test the game, start a server (replace <arch> with the correct
+architecture extension for your machine, eg x86_64):
+
+```
+$ ./ioq3ded.<arch> +set com_basegame baseoa +map aggressor
+```
+
+Bind the pid of the server to a name and register it in the local layer:
+
+```
+$ irm bind proc <pid> name my.ioq3.server
+$ irm reg name my.ioq3.server layer <your_local_layer>
+```
+
+To connect, start a client (in a different terminal):
+
+```
+$ ./ioquake3.<arch> +set com_basegame baseoa
+```
+
+When the client starts, go to the console by typing ~ (tilde) and enter
+the following command
+
+```
+connect -O my.ioq3.server
+```
+
+The client should now connect to the ioq3 dedicated server over
+Ouroboros. Register the name in non-local layers to connect from other
+machines. Happy Fragging!
+
+Rumba
+-----
+
+Rumba is an experimentation framework for deploying recursive network
+experiments in various network testbeds. It is developed as part of the
+[ARCFIRE](http://ict-arcfire.eu) project, and available on
+[gitlab](https://gitlab.com/arcfire/rumba) .
diff --git a/content/faq.md b/content/faq.md
new file mode 100644
index 0000000..954e1a2
--- /dev/null
+++ b/content/faq.md
@@ -0,0 +1,121 @@
+---
+title: "Frequently Asked Questions (FAQ)"
+draft: false
+---
+
+Got a question that is not listed here? Just pop it on our IRC channel
+or mailing list and we will be happy to answer it!
+
+[What is Ouroboros?](#what)\
+[Is Ouroboros the same as the Recursive InterNetwork Architecture
+(RINA)?](#rina)\
+[How can I use Ouroboros right now?](#deploy)\
+[What are the benefits of Ouroboros?](#benefits)\
+[How do you manage the namespaces?](#namespaces)\
+
+### <a name="what">What is Ouroboros?</a>
+
+Ouroboros is a packet-based IPC mechanism. It allows programs to
+communicate by sending messages, and provides a very simple API to do
+so. At its core, it's an implementation of a recursive network
+architecture. It can run next to, or over, common network technologies
+such as Ethernet and IP.
+
+[[back to top](#top)]
+
+### <a name="rina">Is Ouroboros the same as the Recursive InterNetwork Architecture (RINA)?</a>
+
+No. Ouroboros is a recursive network, and is born as part of our
+research into RINA networks. Without the pioneering work of John Day and
+others on RINA, Ouroboros would not exist. We consider the RINA model an
+elegant way to think about distributed applications and networks.
+
+However, there are major architectural differences between Ouroboros and
+RINA. The most important difference is the location of the "transport
+functions" which are related to connection management, such as
+fragmentation, packet ordering and automated repeat request (ARQ). RINA
+places these functions in special applications called IPCPs that form
+layers known as Distributed IPC Facilities (DIFs) as part of a protocol
+called EFCP. This allows a RINA DIF to provide an *IPC service* to the
+layer on top.
+
+Ouroboros has those functions in *every* application. The benefit of
+this approach is that it is possible to multi-home applications in
+different networks, and still have a reliable connection. It is also
+more resilient since every connection is - at least in theory -
+recoverable unless the application itself crashes. So, Ouroboros IPCPs
+form a layer that only provides *IPC resources*. The application does
+its connection management, which is implemented in the Ouroboros
+library. This architectural difference impact the components and
+protocols that underly the network, which are all different from RINA.
+
+This change has a major impact on other components and protocols. We are
+preparing a research paper on Ouroboros that will contain all these
+details and more.
+
+[[back to top](#top)]
+
+### <a name="deploy">How can I use Ouroboros right now?</a>
+
+At this point, Ouroboros is a useable prototype. You can use it to build
+small deployments for personal use. There is no global Ouroboros network
+yet, but if you're interested in helping us set that up, contact us on
+our channel or mailing list.
+
+[[back to top](#top)]
+
+### <a name="benefits">What are the benefits of Ouroboros?</a>
+
+We get this question a lot, and there is no single simple answer to
+it. Its benefits are those of a RINA network and more. In general, if
+two systems provide the same service, simpler systems tend to be the
+more robust and reliable ones. This is why we designed Ouroboros the
+way we did. It has a bunch of small improvements over current networks
+which may not look like anything game-changing by themselves, but do
+add up. The reaction we usually get when demonstrating Ouroboros, is
+that it makes everything really really easy.
+
+Some benefits are improved anonymity as we do not send source addresses
+in our data transfer packets. This also prevents all kinds of swerve and
+amplification attacks. The packet structures are not fixed (as the
+number of layers is not fixed), so there is no fast way to decode a
+packet when captured "raw" on the wire. It also makes Deep Packet
+Inspection harder to do. By attaching names to data transfer components
+(so there can be multiple of these to form an "address"), we can
+significantly reduce routing table sizes.
+
+The API is very simple and universal, so we can run applications as
+close to the hardware as possible to reduce latency. Currently it
+requires quite some work from the application programmer to create
+programs that run directly over Ethernet or over UDP or over TCP. With
+the Ouroboros API, the application doesn't need to be changed. Even if
+somebody comes up with a different transmission technology, the
+application will never need to be modified to run over it.
+
+Ouroboros also makes it easy to run different instances of the same
+application on the same server and load-balance them. In IP networks
+this requires at least some NAT trickery (since each application is tied
+to an interface:port). For instance, it takes no effort at all to run
+three different webserver implementations and load-balance flows between
+them for resiliency and seamless attack mitigation.
+
+The architecture still needs to be evaluated at scale. Ultimately, the
+only way to get the numbers, are to get a large (pre-)production
+deployment with real users.
+
+[[back to top](#top)]
+
+### <a name="namespaces">How do you manage the namespaces?</a>
+
+Ouroboros uses names that are attached to programs and processes. The
+layer API always uses hashes and the network maps hashes to addresses
+for location. This function is similar to a DNS lookup. The current
+implementation uses a DHT for that function in the ipcp-normal (the
+ipcp-udp uses a DynDNS server, the eth-llc and eth-dix use a local
+database with broadcast queries).
+
+But this leaves the question how we assign names. Currently this is
+ad-hoc, but eventually we will need an organized way for a global
+namespace so that application names are unique. If we want to avoid a
+central authority like ICANN, a distributed ledger would be a viable
+technology to implement this, similar to, for instance, namecoin.
diff --git a/content/install.md b/content/install.md
new file mode 100644
index 0000000..ef8220c
--- /dev/null
+++ b/content/install.md
@@ -0,0 +1,52 @@
+---
+title: "Install Ouroboros"
+draft: false
+---
+
+We recommend creating a build directory:
+
+```
+$ mkdir build && cd build
+```
+
+Run cmake providing the path to where you cloned the Ouroboros
+repository. Assuming you created the build directory inside the
+repository directory, do:
+
+```
+$ cmake ..
+```
+
+Build and install Ouroboros:
+
+```
+$ sudo make install
+```
+
+### Advanced options
+
+Ouroboros can be configured by providing parameters to the cmake
+command:
+
+```
+$ cmake -D<option>=<value> ..
+```
+
+Alternatively, after running cmake and before installation, run
+[ccmake](https://cmake.org/cmake/help/v3.0/manual/ccmake.1.html) to
+configure Ouroboros:
+
+```
+$ ccmake .
+```
+
+A list of all options can be found [here](/compopt).
+
+### Remove Ouroboros
+
+To uninstall Ouroboros, simply execute the following command from your
+build directory:
+
+```
+$ sudo make uninstall
+``` \ No newline at end of file
diff --git a/content/manuals.md b/content/manuals.md
new file mode 100644
index 0000000..46593a9
--- /dev/null
+++ b/content/manuals.md
@@ -0,0 +1,17 @@
+---
+title: "Manuals"
+draft: false
+---
+
+These are the man pages for ouroboros. If ouroboros is installed on your
+system, you can also access them using "man".
+
+For general use of Ouroboros, refer to the [Ouroboros User
+Manual](/man/man8/ouroboros.8.html).
+
+For use of the API, refer to the [Ouroboros Programmer's
+Manual](/man/man3/flow_alloc.3.html).
+
+The man section also contains a
+[tutorial](man/man7/ouroboros-tutorial.7.html) and a
+[glossary](man/man7/ouroboros-glossary.7.html).
diff --git a/content/performance.md b/content/performance.md
new file mode 100644
index 0000000..c5b2cf3
--- /dev/null
+++ b/content/performance.md
@@ -0,0 +1,73 @@
+---
+title: "Performance tests"
+draft: false
+---
+
+Below you will find some measurements on the performance of Ouroboros.
+
+### Local IPC performance test
+
+This test uses the *oping* tool to measure round trip time. This tools
+generates traffic from a single thread. The server has a single thread
+that handles ping requests and sends responses.
+
+```
+$ oping -n oping -i 0 -s <sdu size>
+```
+
+The figure below shows the round-trip-time (rtt) in milliseconds (ms)
+for IPC over a local layer for different packet sizes, measured on an
+Intel Core i7 4500U (2 cores @ 2.4GHz). For small payloads (up to 1500
+bytes), the rtt is quite stable at around 30 µs. This will mostly depend
+on CPU frequency and to a lesser extent the OS scheduler.
+
+![Ouroboros local rtt](/images/avgrttlocal.png)
+
+This test uses the *ocbr* tool to measure goodput between a sender and
+receiver. The sender generates traffic from a single thread. The
+receiver handles traffic from a single thread. The performance will
+heavily depend on your system's memory layout (cache sizes etc). This
+test was run on a Dell XPS13 9333 (2013 model).
+
+```
+$ ocbr -n ocbr -f -s <sdu size>
+```
+
+![Ouroboros local pps](/images/throughputlocalpps.png)
+
+The goodput (Mb/s) is shown below:
+
+![ouroboros local mbits](/images/goodputlocalmbits.png)
+
+### Ethernet + Normal test
+
+This connects 2 machines over a Gb LAN using the eth-dix and a normal
+layer. The oping server is registered in the dix as oping.dix and in the
+normal as oping.normal. The machines (dual-socket Intel Xeon E5520) are
+connected over a non-blocking switch.
+
+Latency test:
+
+ICMP ping:
+
+```
+--- 192.168.1.2 ping statistics ---
+1000 packets transmitted, 1000 received, 0% packet loss, time 65ms
+rtt min/avg/max/mdev = 0.046/0.049/0.083/0.002 ms, ipg/ewma 0.065/0.049 ms
+```
+
+oping over eth-dix:
+
+```
+--- oping.dix ping statistics ---
+1000 SDUs transmitted, 1000 received, 0% packet loss, time: 66.142 ms
+rtt min/avg/max/mdev = 0.098/0.112/0.290/0.010 ms
+```
+
+oping over eth-normal:
+
+```
+--- oping.normal ping statistics ---
+1000 SDUs transmitted, 1000 received, 0% packet loss, time: 71.532 ms
+rtt min/avg/max/mdev = 0.143/0.180/0.373/0.020 ms
+``` \ No newline at end of file
diff --git a/content/requirements.md b/content/requirements.md
new file mode 100644
index 0000000..a8444d7
--- /dev/null
+++ b/content/requirements.md
@@ -0,0 +1,71 @@
+---
+title: "Requirements"
+draft: false
+---
+
+### System requirements
+
+Ouroboros builds on most POSIX compliant systems. Below you will find
+instructions for GNU/Linux, FreeBSD and OS X. On Windows 10, you can
+build Ouroboros using the [Linux Subsystem for
+Windows](https://docs.microsoft.com/en-us/windows/wsl/install-win10) .
+
+You need [*git*](https://git-scm.com/) to clone the
+repository. To build Ouroboros, you need [*cmake*](https://cmake.org/),
+[*google protocol buffers*](https://github.com/protobuf-c/protobuf-c)
+installed in addition to a C compiler ([*gcc*](https://gcc.gnu.org/) or
+[*clang*](https://clang.llvm.org/)) and
+[*make*](https://www.gnu.org/software/make/).
+
+Optionally, you can also install
+[*libgcrypt*](https://gnupg.org/software/libgcrypt/index.html),
+[*libssl*](https://www.openssl.org/),
+[*fuse*](https://github.com/libfuse), *dnsutils* and
+[*swig*](http://swig.org/).
+
+On GNU/Linux you will need either libgcrypt (≥ 1.7.0) or libssl if your
+[*glibc*](https://www.gnu.org/software/libc/) is older than version
+2.25.
+
+On OS X, you will need [homebrew](https://brew.sh/). [Disable System
+Integrity
+Protection](https://developer.apple.com/library/content/documentation/Security/Conceptual/System_Integrity_Protection_Guide/ConfiguringSystemIntegrityProtection/ConfiguringSystemIntegrityProtection.html)
+during the [installation](#install) and [removal](#remove) of
+Ouroboros.
+
+### Install the dependencies
+
+**Debian/Ubuntu Linux:**
+
+```
+$ apt-get install git protobuf-c-compiler cmake
+$ apt-get install libgcrypt20-dev libssl-dev libfuse-dev dnsutils swig cmake-curses-gui
+```
+
+On some distributions you need to install the protobuf C library
+explicitly:
+
+```
+$ apt-get install libprotobuf-c-dev
+```
+
+**Arch Linux:**
+
+```
+$ pacman -S git protobuf-c cmake
+$ pacman -S libgcrypt openssl fuse dnsutils swig
+```
+
+**FreeBSD 11:**
+
+```
+$ pkg install git protobuf-c cmake
+$ pkg install libgcrypt openssl fusefs-libs bind-tools swig
+```
+
+**Mac OS X Sierra / High Sierra:**
+
+```
+$ brew install git protobuf-c cmake
+$ brew install libgcrypt openssl swig
+``` \ No newline at end of file
diff --git a/content/tutorial-1.md b/content/tutorial-1.md
new file mode 100644
index 0000000..d1ac3c6
--- /dev/null
+++ b/content/tutorial-1.md
@@ -0,0 +1,153 @@
+---
+title: "Tutorial 1: local test"
+draft: false
+---
+
+This tutorial runs through the basics of Ouroboros. Here, we will see
+the general use of two core components of Ouroboros, the IPC Resource
+Manager daemon (IRMd) and an IPC Process (IPCP).
+
+![Tutorial 1 setup](/images/ouroboros_tut1_overview.png)
+
+We will start the IRMd, create a local IPCP, start a ping server and
+connect a client. This will involve **binding (1)** that server to a
+name and **registering (2)** that name into the local layer. After that
+the client will be able to **allocate a flow (3)** to that name for
+which the server will respond.
+
+We recommend to open 3 terminal windows for this tutorial. In the first
+window, start the IRMd (as a superuser) in stdout mode. The output shows
+the process id (pid) of the IRMd, which will be different on your
+machine.
+
+```
+$ sudo irmd --stdout
+==02301== irmd(II): Ouroboros IPC Resource Manager daemon started\...
+```
+
+The type of IPCP we will create is a "local" IPCP. The local IPCP is a
+kind of loopback interface that is native to Ouroboros. It implements
+all the functions that the Ouroboros API provides, but only for a local
+scope. The IPCP create function will instantiate a new local IPC
+process, which in our case has pid 2324. The "ipcp create" command
+merely creates the IPCP. At this point it is not a part of a layer. We
+will also need to bootstrap this IPCP in a layer, we will name it
+"local_layer". As a shortcut, the bootstrap command will
+automatically create an IPCP if no IPCP by than name exists, so in this
+case, the IPCP create command is optional. In the second terminal, enter
+the commands:
+
+```
+$ irm ipcp create type local name local_ipcp
+$ irm ipcp bootstrap type local name local_ipcp layer local_layer
+```
+
+The IRMd and ipcpd output in the first terminal reads:
+
+```
+==02301== irmd(II): Created IPCP 2324.
+==02324== ipcpd-local(II): Bootstrapped local IPCP with pid 2324.
+==02301== irmd(II): Bootstrapped IPCP 2324 in layer local_layer.
+```
+
+From the third terminal window, let's start our oping application in
+server mode ("oping --help" shows oping command line parameters):
+
+```
+$ oping --listen
+Ouroboros ping server started.
+```
+
+The IRMd will notice that an oping server with pid 10539 has started:
+
+```
+==02301== irmd(DB): New instance (10539) of oping added.
+==02301== irmd(DB): This process accepts flows for:
+```
+
+The server application is not yet reachable by clients. Next we will
+bind the server to a name and register that name in the
+"local_layer". The name for the server can be chosen at will, let's
+take "oping_server". In the second terminal window, execute:
+
+```
+$ irm bind proc 2337 name oping_server
+$ irm register name oping_server layer local_layer
+```
+
+The IRMd and IPCPd in terminal one will now acknowledge that the name is
+bound and registered:
+
+```
+==02301== irmd(II): Bound process 2337 to name oping_server.
+==02324== ipcpd-local(II): Registered 4721372d.
+==02301== irmd(II): Registered oping_server in local_layer as
+4721372d.
+```
+
+Ouroboros registers name not in plaintext but using a (configurable)
+hashing algorithm. The default hash is a 256 bit SHA3 hash. The output
+in the logs is truncated to the first 4 bytes in a HEX notation.
+
+Now that we have bound and registered our server, we can connect from
+the client. In the second terminal window, start an oping client with
+destination oping_server and it will begin pinging:
+
+```
+$ oping -n oping_server -c 5
+Pinging oping_server with 64 bytes of data:
+
+64 bytes from oping_server: seq=0 time=0.694 ms
+64 bytes from oping_server: seq=1 time=0.364 ms
+64 bytes from oping_server: seq=2 time=0.190 ms
+64 bytes from oping_server: seq=3 time=0.269 ms
+64 bytes from oping_server: seq=4 time=0.351 ms
+
+--- oping_server ping statistics ---
+5 SDUs transmitted, 5 received, 0% packet loss, time: 5001.744 ms
+rtt min/avg/max/mdev = 0.190/0.374/0.694/0.192 ms
+```
+
+The server will acknowledge that it has a new flow connected on flow
+descriptor 64, which will time out a few seconds after the oping client
+stops sending:
+
+```
+New flow 64.
+Flow 64 timed out.
+```
+
+The IRMd and IPCP logs provide some additional output detailing the flow
+allocation process:
+
+```
+==02324== ipcpd-local(DB): Allocating flow to 4721372d on fd 64.
+==02301== irmd(DB): Flow req arrived from IPCP 2324 for 4721372d.
+==02301== irmd(II): Flow request arrived for oping_server.
+==02324== ipcpd-local(II): Pending local allocation request on fd 64.
+==02301== irmd(II): Flow on port_id 0 allocated.
+==02324== ipcpd-local(II): Flow allocation completed, fds (64, 65).
+==02301== irmd(II): Flow on port_id 1 allocated.
+==02301== irmd(DB): New instance (2337) of oping added.
+==02301== irmd(DB): This process accepts flows for:
+==02301== irmd(DB): oping_server
+```
+
+First, the IPCPd shows that it will allocate a flow towards a
+destination hash "4721372d" (truncated). The IRMd logs that IPCPd 2324
+(our local IPCPd) requests a flow towards any process that is listening
+for "4721372d", and resolves it to "oping_server", as that is a
+process that is bound to that name. At this point, the local IPCPd has a
+pending flow on the client side. Since this is the first port_id in the
+system, it has port_id 0. The server will accept the flow and the other
+end of the flow gets port_id 1. The local IPCPd sees that the flow
+allocation is completed. Internally it sees the endpoints as flow
+descriptors 64 and 65 which map to port_id 0 and port_id 1. The IPCP
+cannot directly access port_ids, they are assigned and managed by the
+IRMd. After it has accepted the flow, the oping server enters
+flow_accept() again. The IRMd notices the instance and reports that it
+accepts flows for "oping_server".
+
+This concludes this first short tutorial. All running processes can be
+terminated by issuing a Ctrl-C command in their respective terminals or
+you can continue with the next tutorial.
diff --git a/content/tutorial-2.md b/content/tutorial-2.md
new file mode 100644
index 0000000..392a659
--- /dev/null
+++ b/content/tutorial-2.md
@@ -0,0 +1,297 @@
+---
+title: "Tutorial 2: Adding a layer"
+draft: false
+---
+
+In this tutorial we will add a *normal layer* on top of the local layer.
+Make sure you have a local layer running. The network will look like
+this:
+
+![Tutorial 2 setup](/images/ouroboros_tut2_overview.png)
+
+Let's start adding the normal layer. We will first bootstrap a normal
+IPCP, with name "normal_1" into the "normal_layer" (using default
+options). In terminal 2, type:
+
+```
+$ irm ipcp bootstrap type normal name normal_1 layer normal_layer
+```
+
+The IRMd and IPCP will report the bootstrap:
+
+```
+==02301== irmd(II): Created IPCP 4363.
+==04363== normal-ipcp(DB): IPCP got address 465922905.
+==04363== directory(DB): Bootstrapping directory.
+==04363== directory(II): Directory bootstrapped.
+==04363== normal-ipcp(DB): Bootstrapped in layer normal_layer.
+==02301== irmd(II): Bootstrapped IPCP 4363 in layer normal_layer.
+==02301== irmd(DB): New instance (4363) of ipcpd-normal added.
+==02301== irmd(DB): This process accepts flows for:
+```
+
+The new IPCP has pid 4363. It also generated an *address* for itself,
+465922905. Then it bootstrapped a directory. The directory will map
+registered names to an address or a set of addresses. In the normal DHT
+the current default (and only option) for the directory is a Distributed
+Hash Table (DHT) based on the Kademlia protocol, similar to the DHT used
+in the mainline BitTorrent as specified by the
+[BEP5](http://www.bittorrent.org/beps/bep_0005.html). This DHT will use
+the hash algorithm specified for the layer (default is 256-bit SHA3)
+instead of the SHA1 algorithm used by Kademlia. Just like any
+Ouroboros-capable process, the IRMd will notice a new instance of the
+normal IPCP. We will now bind this IPCP to some names and register them
+in the local_layer:
+
+```
+$ irm bind ipcp normal_1 name normal_1
+$ irm bind ipcp normal_1 name normal_layer
+$ irm register name normal_1 layer local_layer
+$ irm register name normal_layer layer local_layer
+```
+
+The "irm bind ipcp" call is a shorthand for the "irm bind proc" call
+that uses the ipcp name instead of the pid for convenience. Note that
+we have bound the same process to two different names. This is to
+allow enrollment using a layer name (anycast) instead of a specific
+ipcp_name. The IRMd and local IPCP should log the following, just as
+in tutorial 1:
+
+```
+==02301== irmd(II): Bound process 4363 to name normal_1.
+==02301== irmd(II): Bound process 4363 to name normal_layer.
+==02324== ipcpd-local(II): Registered e9504761.
+==02301== irmd(II): Registered normal_1 in local_layer as e9504761.
+==02324== ipcpd-local(II): Registered f40ee0f0.
+==02301== irmd(II): Registered normal_layer in local_layer as
+f40ee0f0.
+```
+
+We will now create a second IPCP and enroll it in the normal_layer.
+Like the "irm ipcp bootstrap command", the "irm ipcp enroll" command
+will create the IPCP if an IPCP with that name does not yet exist in the
+system. An "autobind" option is a shorthand for binding the IPCP to
+the IPCP name and the layer name.
+
+```
+$ irm ipcp enroll name normal_2 layer normal_layer autobind
+```
+
+The activity is shown by the output of the IRMd and the IPCPs. Let's
+break it down. First, the new normal IPCP is created and bound to its
+process name:
+
+```
+==02301== irmd(II): Created IPCP 13569.
+==02301== irmd(II): Bound process 13569 to name normal_2.
+```
+
+Next, that IPCP will *enroll* with an existing member of the layer
+"normal_layer". To do that it first allocates a flow over the local
+layer:
+
+```
+==02324== ipcpd-local(DB): Allocating flow to f40ee0f0 on fd 64.
+==02301== irmd(DB): Flow req arrived from IPCP 2324 for f40ee0f0.
+==02301== irmd(II): Flow request arrived for normal_layer.
+==02324== ipcpd-local(II): Pending local allocation request on fd 64.
+==02301== irmd(II): Flow on port_id 0 allocated.
+==02324== ipcpd-local(II): Flow allocation completed, fds (64, 65).
+==02301== irmd(II): Flow on port_id 1 allocated.
+```
+
+Over this flow, it connects to the enrollment component of the normal_1
+IPCP. It sends some information that it will speak the Ouroboros
+Enrollment Protocol (OEP). Then it will receive boot information from
+normal_1 (the configuration of the layer that was provided when we
+bootstrapped the normal_1 process), such as the hash it will use for
+the directory. It signals normal_1 that it got the information so that
+normal_1 knows this was successful. It will also get an address. After
+enrollment is complete, both normal_1 and normal_2 will be ready to
+accept incoming flows:
+
+```
+==13569== connection-manager(DB): Sending cacep info for protocol OEP to
+fd 64.
+==13569== enrollment(DB): Getting boot information.
+==02301== irmd(DB): New instance (4363) of ipcpd-normal added.
+==02301== irmd(DB): This process accepts flows for:
+==02301== irmd(DB): normal_layer
+==02301== irmd(DB): normal_1
+==04363== enrollment(DB): Enrolling a new neighbor.
+==04363== enrollment(DB): Sending enrollment info (49 bytes).
+==13569== enrollment(DB): Received enrollment info (49 bytes).
+==13569== normal-ipcp(DB): IPCP got address 416743497.
+==04363== enrollment(DB): Neighbor enrollment successful.
+==02301== irmd(DB): New instance (13569) of ipcpd-normal added.
+==02301== irmd(DB): This process accepts flows for:
+==02301== irmd(DB): normal_2
+```
+
+Now that the member is enrolled, normal_1 and normal_2 will deallocate
+the flow over which it enrolled and signal the IRMd that the enrollment
+was successful:
+
+```
+==02301== irmd(DB): Partial deallocation of port_id 0 by process
+13569.
+==02301== irmd(DB): Partial deallocation of port_id 1 by process 4363.
+==02301== irmd(II): Completed deallocation of port_id 0 by process
+2324.
+==02301== irmd(II): Completed deallocation of port_id 1 by process
+2324.
+==02324== ipcpd-local(II): Flow with fd 64 deallocated.
+==02324== ipcpd-local(II): Flow with fd 65 deallocated.
+==13569== normal-ipcp(II): Enrolled with normal_layer.
+==02301== irmd(II): Enrolled IPCP 13569 in layer normal_layer.
+```
+
+Now that normal_2 is a full member of the layer, the irm tool will
+complete the autobind option and bind normal_2 to the name
+"normal_layer" so it can also enroll new members.
+
+```
+==02301== irmd(II): Bound process 13569 to name normal_layer.
+```
+
+![Tutorial 2 after enrolment](/images/ouroboros_tut2_enrolled.png)
+
+At this point, have two enrolled members of the normal_layer. What we
+need to do next is connect them. We will need a *management flow*, for
+the management network, which is used to distribute point-to-point
+information (such as routing information) and a *data transfer flow*
+over which the layer will forward traffic coming either from higher
+layers or internal components (such as the DHT and flow allocator). They
+can be established in any order, but it is recommended to create the
+management network first to achieve the minimal setup times for the
+network layer:
+
+```
+$ irm ipcp connect name normal_2 dst normal_1 comp mgmt
+$ irm ipcp connect name normal_2 dst normal_1 comp dt
+```
+
+The IPCP and IRMd log the flow and connection establishment:
+
+```
+==02301== irmd(DB): Connecting Management to normal_1.
+==02324== ipcpd-local(DB): Allocating flow to e9504761 on fd 64.
+==02301== irmd(DB): Flow req arrived from IPCP 2324 for e9504761.
+==02301== irmd(II): Flow request arrived for normal_1.
+==02324== ipcpd-local(II): Pending local allocation request on fd 64.
+==02301== irmd(II): Flow on port_id 0 allocated.
+==02324== ipcpd-local(II): Flow allocation completed, fds (64, 65).
+==02301== irmd(II): Flow on port_id 1 allocated.
+==13569== connection-manager(DB): Sending cacep info for protocol LSP to
+fd 64.
+==04363== link-state-routing(DB): Type mgmt neighbor 416743497 added.
+==02301== irmd(DB): New instance (4363) of ipcpd-normal added.
+==02301== irmd(DB): This process accepts flows for:
+==02301== irmd(DB): normal_layer
+==02301== irmd(DB): normal_1
+==13569== link-state-routing(DB): Type mgmt neighbor 465922905 added.
+==02301== irmd(II): Established Management connection between IPCP 13569
+and normal_1.
+```
+
+The IPCPs established a management flow between the link-state routing
+components (currently that is the only component that needs a management
+flow). The output is similar for the data transfer flow, however,
+creating a data transfer flow triggers some additional activity:
+
+```
+==02301== irmd(DB): Connecting Data Transfer to normal_1.
+==02324== ipcpd-local(DB): Allocating flow to e9504761 on fd 66.
+==02301== irmd(DB): Flow req arrived from IPCP 2324 for e9504761.
+==02301== irmd(II): Flow request arrived for normal_1.
+==02324== ipcpd-local(II): Pending local allocation request on fd 66.
+==02301== irmd(II): Flow on port_id 2 allocated.
+==02324== ipcpd-local(II): Flow allocation completed, fds (66, 67).
+==02301== irmd(II): Flow on port_id 3 allocated.
+==13569== connection-manager(DB): Sending cacep info for protocol dtp to
+fd 65.
+==04363== dt(DB): Added fd 65 to SDU scheduler.
+==04363== link-state-routing(DB): Type dt neighbor 416743497 added.
+==02301== irmd(DB): New instance (4363) of ipcpd-normal added.
+==02301== irmd(DB): This process accepts flows for:
+==02301== irmd(DB): normal_layer
+==02301== irmd(DB): normal_1
+==13569== dt(DB): Added fd 65 to SDU scheduler.
+==13569== link-state-routing(DB): Type dt neighbor 465922905 added.
+==13569== dt(DB): Could not get nhop for addr 465922905.
+==02301== irmd(II): Established Data Transfer connection between IPCP
+13569 and normal_1.
+==13569== dt(DB): Could not get nhop for addr 465922905.
+==13569== dht(DB): Enrollment of DHT completed.
+```
+
+First, the data transfer flow is added to the SDU scheduler. Next, the
+neighbor's address is added to the link-state database and a Link-State
+Update message is broadcast over the management network. Finally, if the
+DHT is not yet enrolled, it will try to do so when it detects a new data
+transfer flow. Since this is the first data transfer flow in the
+network, the DHT will try to enroll. It may take some time for the
+routing entry to get inserted to the forwarding table, so the DHT
+re-tries a couple of times (this is the "could not get nhop" message
+in the debug log).
+
+Our oping server is not registered yet in the normal layer. Let's
+register it in the normal layer as well, and connect the client:
+
+```
+$ irm r n oping_server layer normal_layer
+$ oping -n oping_server -c 5
+```
+
+The IRMd and IPCP will log:
+
+```
+==02301== irmd(II): Registered oping_server in normal_layer as
+465bac77.
+==02301== irmd(II): Registered oping_server in normal_layer as
+465bac77.
+==02324== ipcpd-local(DB): Allocating flow to 4721372d on fd 68.
+==02301== irmd(DB): Flow req arrived from IPCP 2324 for 4721372d.
+==02301== irmd(II): Flow request arrived for oping_server.
+==02324== ipcpd-local(II): Pending local allocation request on fd 68.
+==02301== irmd(II): Flow on port_id 4 allocated.
+==02324== ipcpd-local(II): Flow allocation completed, fds (68, 69).
+==02301== irmd(II): Flow on port_id 5 allocated.
+==02301== irmd(DB): New instance (2337) of oping added.
+==02301== irmd(DB): This process accepts flows for:
+==02301== irmd(DB): oping_server
+==02301== irmd(DB): Partial deallocation of port_id 4 by process 749.
+==02301== irmd(II): Completed deallocation of port_id 4 by process
+2324.
+==02324== ipcpd-local(II): Flow with fd 68 deallocated.
+==02301== irmd(DB): Dead process removed: 749.
+==02301== irmd(DB): Partial deallocation of port_id 5 by process 2337.
+==02301== irmd(II): Completed deallocation of port_id 5 by process
+2324.
+==02324== ipcpd-local(II): Flow with fd 69 deallocated.
+```
+
+The client connected over the local layer instead of the normal layer.
+This is because the IRMd prefers the local layer. If we unregister the
+name from the local layer, the client will connect over the normal
+layer:
+
+```
+$ irm unregister name oping_server layer local_layer
+$ oping -n oping_server -c 5
+```
+
+As shown by the logs (the normal IPCP doesn't log the flow allocation):
+
+```
+==02301== irmd(DB): Flow req arrived from IPCP 13569 for 465bac77.
+==02301== irmd(II): Flow request arrived for oping_server.
+==02301== irmd(II): Flow on port_id 5 allocated.
+==02301== irmd(II): Flow on port_id 4 allocated.
+==02301== irmd(DB): New instance (2337) of oping added.
+==02301== irmd(DB): This process accepts flows for:
+==02301== irmd(DB): oping_server
+```
+
+This concludes tutorial 2. You can shut down everything or continue with
+tutorial 3.
diff --git a/content/tutorial-3.md b/content/tutorial-3.md
new file mode 100644
index 0000000..2dd0645
--- /dev/null
+++ b/content/tutorial-3.md
@@ -0,0 +1,210 @@
+---
+title: "Tutorial 3: IPCP statistics"
+draft: false
+---
+
+For this tutorial, you should have a local layer, a normal layer and a
+ping server registered in the normal layer. You will need to have the
+FUSE libraries installed and Ouroboros compiled with FUSE support. We
+will show you how to get some statistics from the network layer which is
+exported by the IPCPs at /tmp/ouroboros (this mountpoint can be set at
+compile time):
+
+```
+$ tree /tmp/ouroboros
+/tmp/ouroboros/
+|-- ipcpd-normal.13569
+| |-- dt
+| | |-- 0
+| | |-- 1
+| | `-- 65
+| `-- lsdb
+| |-- 416743497.465922905
+| |-- 465922905.416743497
+| |-- dt.465922905
+| `-- mgmt.465922905
+`-- ipcpd-normal.4363
+ |-- dt
+ | |-- 0
+ | |-- 1
+ | `-- 65
+ `-- lsdb
+ |-- 416743497.465922905
+ |-- 465922905.416743497
+ |-- dt.416743497
+ `-- mgmt.416743497
+
+6 directories, 14 files
+```
+
+There are two filesystems, one for each normal IPCP. Currently, it shows
+information for two components: data transfer and the link-state
+database. The data transfer component lists flows on known flow
+descriptors. The flow allocator component will usually be on fd 0 and
+the directory (DHT). There is a single (N-1) data transfer flow on fd 65
+that the IPCPs can use to send data (these fd's will usually not be the
+same). The routing component sees that data transfer flow as two
+unidirectional links. It has a management flow and data transfer flow to
+its neighbor. Let's have a look at the data transfer flow in the
+network:
+
+```
+$ cat /tmp/ouroboros/ipcpd-normal.13569/dt/65
+Flow established at: 2018-03-07 18:47:43
+Endpoint address: 465922905
+Queued packets (rx): 0
+Queued packets (tx): 0
+
+Qos cube 0:
+ sent (packets): 4
+ sent (bytes): 268
+ rcvd (packets): 3
+ rcvd (bytes): 298
+ local sent (packets): 4
+ local sent (bytes): 268
+ local rcvd (packets): 3
+ local rcvd (bytes): 298
+ dropped ttl (packets): 0
+ dropped ttl (bytes): 0
+ failed writes (packets): 0
+ failed writes (bytes): 0
+ failed nhop (packets): 0
+ failed nhop (bytes): 0
+
+<no traffic on other qos cubes>
+```
+
+The above output shows the statistics for the data transfer component of
+the IPCP that enrolled into the layer. It shows the time the flow was
+established, the endpoint address and the number of packets that are in
+the incoming and outgoing queues. Then it lists packet statistics per
+QoS cube. It sent 4 packets, and received 3 packets. All the packets
+came from local sources (internal components of the IPCP) and were
+delivered to local destinations. Let's have a look where they went.
+
+```
+$ cat /tmp/ouroboros/ipcpd-normal.13569/dt/1
+Flow established at: 2018-03-07 18:47:43
+Endpoint address: flow-allocator
+Queued packets (rx): 0
+Queued packets (tx): 0
+
+<no packets on this flow>
+```
+
+There is no traffic on fd 0, which is the flow allocator component. This
+will only be used when higher layer applications will use this normal
+layer. Let's have a look at fd 1.
+
+```
+$ cat /tmp/ouroboros/ipcpd-normal.13569/dt/1
+Flow established at: 2018-03-07 18:47:43
+Endpoint address: dht
+Queued packets (rx): 0
+Queued packets (tx): 0
+
+Qos cube 0:
+ sent (packets): 3
+ sent (bytes): 298
+ rcvd (packets): 0
+ rcvd (bytes): 0
+ local sent (packets): 0
+ local sent (bytes): 0
+ local rcvd (packets): 6
+ local rcvd (bytes): 312
+ dropped ttl (packets): 0
+ dropped ttl (bytes): 0
+ failed writes (packets): 0
+ failed writes (bytes): 0
+ failed nhop (packets): 2
+ failed nhop (bytes): 44
+
+<no traffic on other qos cubes>
+```
+
+The traffic for the directory (DHT) is on fd1. Take note that this is
+from the perspective of the data transfer component. The data transfer
+component sent 3 packets to the DHT, these are the 3 packets it received
+from the data transfer flow. The data transfer component received 6
+packets from the DHT. It only sent 4 on fd 65. 2 packets failed because
+of nhop. This is because the forwarding table was being updated from the
+routing table. Let's send some traffic to the oping server.
+
+```
+$ oping -n oping_server -i 0
+Pinging oping_server with 64 bytes of data:
+
+64 bytes from oping_server: seq=0 time=0.547 ms
+...
+64 bytes from oping_server: seq=999 time=0.184 ms
+
+--- oping_server ping statistics ---
+1000 SDUs transmitted, 1000 received, 0% packet loss, time: 106.538 ms
+rtt min/avg/max/mdev = 0.151/0.299/2.269/0.230 ms
+```
+
+This sent 1000 packets to the server. Let's have a look at the flow
+allocator:
+
+```
+$ cat /tmp/ouroboros/ipcpd-normal.13569/dt/0
+Flow established at: 2018-03-07 18:47:43
+Endpoint address: flow-allocator
+Queued packets (rx): 0
+Queued packets (tx): 0
+
+Qos cube 0:
+ sent (packets): 1
+ sent (bytes): 59
+ rcvd (packets): 0
+ rcvd (bytes): 0
+ local sent (packets): 0
+ local sent (bytes): 0
+ local rcvd (packets): 1
+ local rcvd (bytes): 51
+ dropped ttl (packets): 0
+ dropped ttl (bytes): 0
+ failed writes (packets): 0
+ failed writes (bytes): 0
+ failed nhop (packets): 0
+ failed nhop (bytes): 0
+
+<no traffic on other qos cubes>
+```
+
+The flow allocator has sent and received a message: a request and a
+response for the flow allocation between the oping client and server.
+The data transfer flow will also have additional traffic:
+
+```
+$ cat /tmp/ouroboros/ipcpd-normal.13569/dt/65
+Flow established at: 2018-03-07 18:47:43
+Endpoint address: 465922905
+Queued packets (rx): 0
+Queued packets (tx): 0
+
+Qos cube 0:
+ sent (packets): 1013
+ sent (bytes): 85171
+ rcvd (packets): 1014
+ rcvd (bytes): 85373
+ local sent (packets): 13
+ local sent (bytes): 1171
+ local rcvd (packets): 14
+ local rcvd (bytes): 1373
+ dropped ttl (packets): 0
+ dropped ttl (bytes): 0
+ failed writes (packets): 0
+ failed writes (bytes): 0
+ failed nhop (packets): 0
+ failed nhop (bytes): 0
+```
+
+This shows the traffic from the oping application. The additional
+traffic (the oping sent 1000, the flow allocator 1 and the DHT
+previously sent 3) is additional DHT traffic (the DHT periodically
+updates). Also note that the traffic reported on the link includes the
+FRCT and data transfer headers which in the default configuration is 20
+bytes per packet.
+
+This concludes tutorial 3.
diff --git a/content/tutorial-4.md b/content/tutorial-4.md
new file mode 100644
index 0000000..fd7db3a
--- /dev/null
+++ b/content/tutorial-4.md
@@ -0,0 +1,123 @@
+---
+title: "Tutorial 4: Connecting two machines over Ethernet"
+draft: false
+---
+
+In this tutorial we will connect two machines over an Ethernet network
+using the eth-llc or eth-dix IPCPs. The eth-llc use of the IEEE 802.2
+Link Layer Control (LLC) service type 1 frame header. The eth-dix IPCP
+uses DIX (DEC, Intel, Xerox) Ethernet, also known as Ethernet II. Both
+provide a connectionless packet service with unacknowledged delivery.
+
+Make sure that you have an Ouroboros IRM daemon running on both
+machines:
+
+```
+$ sudo irmd --stdout
+```
+
+The eth-llc and eth-dix IPCPs attach to an ethernet interface, which is
+specified by its device name. The device name can be found in a number
+of ways, we'll use the "ip" command here:
+
+```
+$ ip a
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
+group default qlen 1
+link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+...
+2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast
+state UP group default qlen 1000
+link/ether fa:16:3e:42:00:38 brd ff:ff:ff:ff:ff:ff
+...
+3: ens6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast
+state UP group default qlen 1000
+link/ether fa:16:3e:00:76:c2 brd ff:ff:ff:ff:ff:ff
+...
+```
+
+The output of this command differs between operating systems and
+distributions. The interface we need to use in our setup is "ens3" on
+both machines, but for you it may be something like "eth0" or
+"enp0s7f1" if you are on a wired LAN, or something like "wlan0" or
+"wlp2s0" if you are on a Wi-Fi network. For Wi-Fi networks, we
+recommend using the eth-dix.
+
+Usually the interface you will use is the one that has an IP address for
+your LAN set. Note that you do not need to have an IP address for this
+tutorial, but do make sure the interface is UP.
+
+Now that we know the interfaces to connect to the network with, let's
+start the eth-llc/eth-dix IPCPs. The eth-llc/eth-dix layers don't have
+an enrollment phase, all eth-llc IPCPs that are connected to the same
+Ethernet, will be part of the layer. For eth-dix IPCPs the layers can be
+separated by ethertype. The eth-llc and eth-dix IPCPs can only be
+bootstrapped, so care must be taken by to provide the same hash
+algorithm to all eth-llc and eth-dix IPCPs that should be in the same
+network. We use the default (256-bit SHA3) for the hash and 0xa000 for
+the Ethertype for the DIX IPCP. For our setup, it's the exact same
+command on both machines. You will likely need to set a different
+interface name on each machine. The irm tool allows abbreviated commands
+(it is modelled after the "ip" command), which we show here (both
+commands do the same):
+
+```
+node0: $ irm ipcp bootstrap type eth-llc name llc layer eth dev ens3
+node1: $ irm i b t eth-llc n llc l eth if ens3
+```
+
+Both IRM daemons should acknowledge the creation of the IPCP:
+
+```
+==26504== irmd(II): Ouroboros IPC Resource Manager daemon started...
+==26504== irmd(II): Created IPCP 27317.
+==27317== ipcpd/eth-llc(II): Using raw socket device.
+==27317== ipcpd/eth-llc(DB): Bootstrapped IPCP over Ethernet with LLC
+with pid 27317.
+==26504== irmd(II): Bootstrapped IPCP 27317 in layer eth.
+```
+
+If it failed, you may have mistyped the interface name, or your system
+may not have a valid raw packet API. We are using GNU/Linux machines, so
+the IPCP announces that it is using a [raw
+socket](http://man7.org/linux/man-pages/man2/socket.2.html) device. On
+OS X, the default is a [Berkeley Packet Filter
+(BPF)](http://www.manpages.info/macosx/bpf.4.html) device, and on
+FreeBSD, the default is a
+[netmap](http://info.iet.unipi.it/~luigi/netmap/) device. See the
+[compilation options](/compopt) for more information on choosing the
+raw packet API.
+
+The Ethernet layer is ready to use. We will now create a normal layer
+on top of it, just like we did over the local layer in the second
+tutorial. We are showing some different ways of entering these
+commands on the two machines:
+
+```
+node0:
+$ irm ipcp bootstrap type normal name normal_0 layer normal_layer
+$ irm bind ipcp normal_0 name normal_0
+$ irm b i normal_0 n normal_layer
+$ irm register name normal_layer layer eth
+$ irm r n normal_0 l eth
+node1:
+$ irm ipcp enroll name normal_1 layer normal_layer autobind
+$ irm r n normal_layer l eth
+$ irm r n normal_1 l eth
+```
+
+The IPCPs should acknowledge the enrollment in their logs:
+
+```
+node0:
+==27452== enrollment(DB): Enrolling a new neighbor.
+==27452== enrollment(DB): Sending enrollment info (47 bytes).
+==27452== enrollment(DB): Neighbor enrollment successful.
+node1:
+==27720== enrollment(DB): Getting boot information.
+==27720== enrollment(DB): Received enrollment info (47 bytes).
+```
+
+You can now continue to set up a management flow and data transfer
+flow for the normal layer, like in tutorial 2. This concludes the
+fourth tutorial.
diff --git a/static/images/avgrttlocal.png b/static/images/avgrttlocal.png
new file mode 100644
index 0000000..b19f916
--- /dev/null
+++ b/static/images/avgrttlocal.png
Binary files differ
diff --git a/static/images/goodputlocalmbits.png b/static/images/goodputlocalmbits.png
new file mode 100644
index 0000000..85adda7
--- /dev/null
+++ b/static/images/goodputlocalmbits.png
Binary files differ
diff --git a/static/images/icon.png b/static/images/icon.png
new file mode 100644
index 0000000..15ce526
--- /dev/null
+++ b/static/images/icon.png
Binary files differ
diff --git a/static/images/ouroboros_tut1_overview.png b/static/images/ouroboros_tut1_overview.png
new file mode 100644
index 0000000..a16a289
--- /dev/null
+++ b/static/images/ouroboros_tut1_overview.png
Binary files differ
diff --git a/static/images/ouroboros_tut2_enrolled.png b/static/images/ouroboros_tut2_enrolled.png
new file mode 100644
index 0000000..0788856
--- /dev/null
+++ b/static/images/ouroboros_tut2_enrolled.png
Binary files differ
diff --git a/static/images/ouroboros_tut2_overview.png b/static/images/ouroboros_tut2_overview.png
new file mode 100644
index 0000000..4efef99
--- /dev/null
+++ b/static/images/ouroboros_tut2_overview.png
Binary files differ
diff --git a/static/images/throughputlocalpps.png b/static/images/throughputlocalpps.png
new file mode 100644
index 0000000..28e4431
--- /dev/null
+++ b/static/images/throughputlocalpps.png
Binary files differ
diff --git a/static/patches/ouroboros-ioq3.patch b/static/patches/ouroboros-ioq3.patch
new file mode 100644
index 0000000..2c78843
--- /dev/null
+++ b/static/patches/ouroboros-ioq3.patch
@@ -0,0 +1,415 @@
+From c1cf00d86a4a3f5de170f968077f5b1e80b6b482 Mon Sep 17 00:00:00 2001
+From: Dimitri Staessens <dimitri.staessens@ugent.be>
+Date: Mon, 22 Jan 2018 10:25:54 +0100
+Subject: [PATCH] qcommon: Add Ouroboros support (experimental)
+
+This adds support for the Ouroboros IPC subsystem. This patch was
+written by
+
+Dimitri Staessens <dimitri.staessens@ugent.be>
+Sander Vrijders <sander.vrijders@ugent.be>
+Addy Bombeke <addy.bombeke@ugent.be>
+---
+ Makefile | 6 ++
+ code/client/cl_main.c | 6 +-
+ code/qcommon/net_chan.c | 6 ++
+ code/qcommon/net_ip.c | 27 +++++++-
+ code/qcommon/net_ouroboros.c | 144 +++++++++++++++++++++++++++++++++++++++++++
+ code/qcommon/net_ouroboros.h | 46 ++++++++++++++
+ code/qcommon/qcommon.h | 4 +-
+ 7 files changed, 234 insertions(+), 5 deletions(-)
+ create mode 100644 code/qcommon/net_ouroboros.c
+ create mode 100644 code/qcommon/net_ouroboros.h
+
+diff --git a/Makefile b/Makefile
+index 4aa9f519..12da9fde 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1645,6 +1645,7 @@ Q3OBJ = \
+ $(B)/client/msg.o \
+ $(B)/client/net_chan.o \
+ $(B)/client/net_ip.o \
++ $(B)/client/net_ouroboros.o \
+ $(B)/client/huffman.o \
+ \
+ $(B)/client/snd_adpcm.o \
+@@ -2209,6 +2210,7 @@ Q3DOBJ = \
+ $(B)/ded/msg.o \
+ $(B)/ded/net_chan.o \
+ $(B)/ded/net_ip.o \
++ $(B)/ded/net_ouroboros.o \
+ $(B)/ded/huffman.o \
+ \
+ $(B)/ded/q_math.o \
+@@ -2943,3 +2945,7 @@ endif
+ ifneq ($(findstring clean, $(MAKECMDGOALS)),)
+ .NOTPARALLEL:
+ endif
++
++#Ouroboros support
++LIBS+="-louroboros-dev"
++LIBS+="-lpthread"
+diff --git a/code/client/cl_main.c b/code/client/cl_main.c
+index 82d0edfb..33a85f83 100644
+--- a/code/client/cl_main.c
++++ b/code/client/cl_main.c
+@@ -1697,7 +1697,7 @@ void CL_Connect_f( void ) {
+ netadrtype_t family = NA_UNSPEC;
+
+ if ( argc != 2 && argc != 3 ) {
+- Com_Printf( "usage: connect [-4|-6] server\n");
++ Com_Printf( "usage: connect [-4|-6|-O] server\n");
+ return;
+ }
+
+@@ -1709,8 +1709,10 @@ void CL_Connect_f( void ) {
+ family = NA_IP;
+ else if(!strcmp(Cmd_Argv(1), "-6"))
+ family = NA_IP6;
++ else if(!strcmp(Cmd_Argv(1), "-O"))
++ family = NA_OUROBOROS;
+ else
+- Com_Printf( "warning: only -4 or -6 as address type understood.\n");
++ Com_Printf( "warning: only -4, -6 or -O as address type understood.\n");
+
+ Q_strncpyz( server, Cmd_Argv(2), sizeof( server ) );
+ }
+diff --git a/code/qcommon/net_chan.c b/code/qcommon/net_chan.c
+index 9e7d9b87..b0103cc3 100644
+--- a/code/qcommon/net_chan.c
++++ b/code/qcommon/net_chan.c
+@@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ #include "q_shared.h"
+ #include "qcommon.h"
++#include "net_ouroboros.h"
+
+ /*
+
+@@ -638,6 +639,11 @@ int NET_StringToAdr( const char *s, netadr_t *a, netadrtype_t family )
+ return 1;
+ }
+
++ if(family == NA_OUROBOROS) {
++ OUROBOROS_Resolve(s, a);
++ return 1;
++ }
++
+ Q_strncpyz( base, s, sizeof( base ) );
+
+ if(*base == '[' || Q_CountChar(base, ':') > 1)
+diff --git a/code/qcommon/net_ip.c b/code/qcommon/net_ip.c
+index bcccda20..1bcce311 100644
+--- a/code/qcommon/net_ip.c
++++ b/code/qcommon/net_ip.c
+@@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ #include "../qcommon/q_shared.h"
+ #include "../qcommon/qcommon.h"
++#include "net_ouroboros.h"
+
+ #ifdef _WIN32
+ # include <winsock2.h>
+@@ -392,6 +393,9 @@ qboolean NET_CompareBaseAdrMask(netadr_t a, netadr_t b, int netmask)
+ byte cmpmask, *addra, *addrb;
+ int curbyte;
+
++ if (a.type == NA_OUROBOROS && b.type == NA_OUROBOROS)
++ return a.fd == b.fd ? qtrue : qfalse;
++
+ if (a.type != b.type)
+ return qfalse;
+
+@@ -495,6 +499,9 @@ qboolean NET_CompareAdr (netadr_t a, netadr_t b)
+ if(!NET_CompareBaseAdr(a, b))
+ return qfalse;
+
++ if (a.type == NA_OUROBOROS && b.type == NA_OUROBOROS)
++ return a.fd == b.fd ? qtrue :qfalse;
++
+ if (a.type == NA_IP || a.type == NA_IP6)
+ {
+ if (a.port == b.port)
+@@ -627,6 +634,9 @@ qboolean NET_GetPacket(netadr_t *net_from, msg_t *net_message, fd_set *fdr)
+ }
+ }
+
++ ret = OUROBOROS_Recvfrom(net_message, net_from);
++ if (ret > 0)
++ return qtrue;
+
+ return qfalse;
+ }
+@@ -644,6 +654,11 @@ void Sys_SendPacket( int length, const void *data, netadr_t to ) {
+ int ret = SOCKET_ERROR;
+ struct sockaddr_storage addr;
+
++ if (to.type == NA_OUROBOROS) {
++ OUROBOROS_Sendto(length, data, &to);
++ return;
++ }
++
+ if( to.type != NA_BROADCAST && to.type != NA_IP && to.type != NA_IP6 && to.type != NA_MULTICAST6)
+ {
+ Com_Error( ERR_FATAL, "Sys_SendPacket: bad address type" );
+@@ -1585,8 +1600,14 @@ void NET_Init( void ) {
+ #endif
+
+ NET_Config( qtrue );
+-
++
+ Cmd_AddCommand ("net_restart", NET_Restart_f);
++
++#ifdef DEDICATED
++ OUROBOROS_Init(1);
++#else
++ OUROBOROS_Init(0);
++#endif
+ }
+
+
+@@ -1602,6 +1623,8 @@ void NET_Shutdown( void ) {
+
+ NET_Config( qfalse );
+
++ OUROBOROS_Fini();
++
+ #ifdef _WIN32
+ WSACleanup();
+ winsockInitialized = qfalse;
+@@ -1694,7 +1717,7 @@ void NET_Sleep(int msec)
+
+ if(retval == SOCKET_ERROR)
+ Com_Printf("Warning: select() syscall failed: %s\n", NET_ErrorString());
+- else if(retval > 0)
++ else
+ NET_Event(&fdr);
+ }
+
+diff --git a/code/qcommon/net_ouroboros.c b/code/qcommon/net_ouroboros.c
+new file mode 100644
+index 00000000..ee98a2e0
+--- /dev/null
++++ b/code/qcommon/net_ouroboros.c
+@@ -0,0 +1,144 @@
++/*
++ * Ouroboros calls for ioq3
++ *
++ * Addy Bombeke <addy.bombeke@ugent.be>
++ * Sander Vrijders <sander.vrijders@intec.ugent.be>
++ * Dimitri Staessens <dimitri.staessens@intec.ugent.be>
++ *
++ * Copyright (C) 2015 - 2018
++ *
++ * Modified from software originally written as part of the MSc
++ * thesis in electrical engineering,
++ *
++ * "Comparing RINA to TCP/IP for latency-constrained applications",
++ *
++ * at Ghent University, Academic Year 2014-2015
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include "net_ouroboros.h"
++
++#include <stdio.h>
++#include <pthread.h>
++#include <unistd.h>
++#include <stdlib.h>
++
++#include <ouroboros/dev.h>
++#include <ouroboros/fqueue.h>
++
++fset_t * flows;
++fqueue_t * fq;
++
++void OUROBOROS_Resolve(const char * s, netadr_t * a)
++{
++ int fd;
++
++ a->type = NA_OUROBOROS;
++
++ fd = flow_alloc(s, NULL, NULL);
++ if (fd < 0) {
++ printf("Failed to allocate flow.\n");
++ return;
++ }
++
++ fset_add(flows, fd);
++
++ a->fd = fd;
++}
++
++void OUROBOROS_Sendto(int length, const void * data, netadr_t * to)
++{
++ flow_write(to->fd, (void *) data, length);
++}
++
++int OUROBOROS_Recvfrom(msg_t * msg, netadr_t * from)
++{
++ int fd;
++ ssize_t count = 0;
++ struct timespec timeout = {0, 0};
++
++ fevent(flows, fq, &timeout);
++
++ if ((fd = fqueue_next(fq)) >= 0) {
++ count = flow_read(fd, msg->data, msg->maxsize);
++ if (count < 0)
++ return 0;
++
++ if (count > msg->maxsize) {
++ printf("Oversized packet received.\n");
++ return 0;
++ }
++
++ from->type = NA_OUROBOROS;
++ from->fd = fd;
++ msg->cursize = count;
++ msg->readcount = 0;
++
++ return count;
++ }
++
++ return 0;
++}
++
++static void * OUROBOROS_Server_Accept(void * o)
++{
++ int fd;
++
++ (void) o;
++
++ for (;;) {
++ fd = flow_accept(NULL, NULL);
++ if (fd < 0) {
++ printf("Failed to accept flow.\n");
++ continue;
++ }
++
++ fset_add(flows, fd);
++ }
++
++ return (void *) 0;
++}
++
++void OUROBOROS_Init(int server)
++{
++ pthread_t accept_thread;
++
++ flows = fset_create();
++ if (flows == NULL) {
++ printf("Failed to create flow set.\n");
++ return;
++ }
++
++ fq = fqueue_create();
++ if (fq == NULL) {
++ printf("Failed to create flow queue.\n");
++ return;
++ }
++
++ if (server) {
++ pthread_create(&accept_thread,
++ NULL,
++ OUROBOROS_Server_Accept,
++ NULL);
++ pthread_detach(accept_thread);
++ }
++}
++
++void OUROBOROS_Fini(void)
++{
++ fqueue_destroy(fq);
++ fset_destroy(flows);
++}
+diff --git a/code/qcommon/net_ouroboros.h b/code/qcommon/net_ouroboros.h
+new file mode 100644
+index 00000000..5f85987e
+--- /dev/null
++++ b/code/qcommon/net_ouroboros.h
+@@ -0,0 +1,46 @@
++/*
++ * Ouroboros calls for ioq3
++ *
++ * Addy Bombeke <addy.bombeke@ugent.be>
++ * Sander Vrijders <sander.vrijders@intec.ugent.be>
++ * Dimitri Staessens <dimitri.staessens@intec.ugent.be>
++ *
++ * Copyright (C) 2015 - 2018
++ *
++ * Modified from software originally written as part of the MSc
++ * thesis in electrical engineering,
++ *
++ * "Comparing RINA to TCP/IP for latency-constrained applications",
++ *
++ * at Ghent University, Academic Year 2014-2015
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#ifndef NET_OUROBOROS_H
++#define NET_OUROBOROS_H
++
++#include "../qcommon/q_shared.h"
++#include "../qcommon/qcommon.h"
++
++void OUROBOROS_Init(int server);
++void OUROBOROS_Fini(void);
++
++void OUROBOROS_Resolve(const char * s, netadr_t * a);
++
++int OUROBOROS_Recvfrom(msg_t * msg, netadr_t * from);
++void OUROBOROS_Sendto(int length, const void * data, netadr_t * to);
++
++#endif /*NET_OUROBOROS_H */
+diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h
+index 4471198c..eff5472f 100644
+--- a/code/qcommon/qcommon.h
++++ b/code/qcommon/qcommon.h
+@@ -147,7 +147,8 @@ typedef enum {
+ NA_IP,
+ NA_IP6,
+ NA_MULTICAST6,
+- NA_UNSPEC
++ NA_UNSPEC,
++ NA_OUROBOROS
+ } netadrtype_t;
+
+ typedef enum {
+@@ -164,6 +165,7 @@ typedef struct {
+
+ unsigned short port;
+ unsigned long scope_id; // Needed for IPv6 link-local addresses
++ int fd; // Needed for Ouroboros support
+ } netadr_t;
+
+ void NET_Init( void );
+--
+2.16.0
+
diff --git a/themes/terrassa b/themes/terrassa
new file mode 160000
+Subproject ba84a7715f8fac4fb116eb560e9c21ea8e1d029