<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Marek's homepage</title>
    <subtitle>Dies ist mein persönlicher Blog. Hier findest du ganz unterschiedliche Dinge. Manche auf Deutsch, manche auf Englisch, manche dumm, manche witzung und mache informativ.</subtitle>
    <id>urn:uuid:8fbc46b1-d222-4f90-bad6-8a09221b902d</id>
    
    <link rel="self" href="https://mk16.de/content.xml" />
    <link rel="self" href="https://homepage.bandura.dn42/content.xml" />
    <link rel="self" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/content.xml" />
    <link rel="self" href="http://homepage.bandura.i2p/content.xml" />
    <link rel="self" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/content.xml" />
    <link rel="self" href="http://bandura.ygg/content.xml" />
    
    <entry>
        <title>Was ist AmneziaWG?</title>
        
        <link rel="alternate" href="https://mk16.de/blog/amneziawg-de/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/amneziawg-de/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/amneziawg-de/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/amneziawg-de/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/amneziawg-de/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/amneziawg-de/" hreflang="de" />

        <id>/blog/amneziawg-de/</id>
        <published>2024-09-22T21:00:00+02:00</published>
        <updated>2024-09-22T21:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Was ist AmneziaWG? Und was bedeuten die Paramater eigentlich?</summary>
        <content type="html">
            <![CDATA[<p><a href="https://docs.amnezia.org/documentation/amnezia-wg/">AmneziaWG</a> ist eine WireGuard Fork, welcher Anti-Erkennungs-Techniken gegen DPI-Systeme implementiert. Diese Anti-Erkennungs-Techniken werden als “Advanced Security” bezeichnet. AmneziaWG kann rückwärtskompatibel zu vanilla WireGuard eingestellt werden.</p>

<p><img src="/assets/img/amnezia_logo.svg" alt="Auf dem Bild sieht man das Amnezia Logo. Dabei handelt es sich um das klassische Anarchie &quot;A&quot; im Kreis. Jedoch ist das Logo sehr farbenfroh - Flächen sind Orange, Lula, Hellblau, Pink und Grau gefüllt." /></p>

<p>(aus <a href="https://github.com/amnezia-vpn/amnezia-client">https://github.com/amnezia-vpn/amnezia-client</a>, GPL-3.0, ausgeschnitten von mir)</p>

<p>Es führt neun zusätzliche Paramater (<code class="language-plaintext highlighter-rouge">Jc</code>, <code class="language-plaintext highlighter-rouge">Jmin</code>, <code class="language-plaintext highlighter-rouge">Jmax</code>, <code class="language-plaintext highlighter-rouge">S1</code>, <code class="language-plaintext highlighter-rouge">S2</code>, <code class="language-plaintext highlighter-rouge">H1</code>, <code class="language-plaintext highlighter-rouge">H2</code>, <code class="language-plaintext highlighter-rouge">H3</code>, <code class="language-plaintext highlighter-rouge">H4</code>) ein.</p>

<h2 id="vanilla-wireguard">Vanilla WireGuard</h2>

<p>Das WireGuard-Protokoll besteht aus verschiedenen Nachrichten. Einige der wichtigsten Nachrichten sind die “Handshake Initation”, die “Handshake Response” und die eigentliche Datenpakete.
Diese WireGuard Nachrichten haben alle einen Header, in welchem ihr Type definiert wird. Dieser wird mithilfe einer ganzen Zahl angegeben.
Um eine WireGuard-Verbindung aufzubauen, wird im Allgemeinen vom Peer, welcher die Verbindung aufbauen will, eine “Handshake Initation” gesendet. Der andere Peer antwortet dann mit einer “Handshake Response”.
Diese Pakete lassen sich mithilfe von DPI-Systemen erkennen und dann entsprechend blockieren. Dadurch wird der ganze WireGuard Tunnel verhindert und WireGuard ist dann blockiert.</p>

<h2 id="junk-pakete">Junk-Pakete</h2>

<p>AmneziaWG kann vor dem Handshake Junk Pakete senden. Dabei handelt es sich um Pakete, welche zufällige Daten beinhalten und keinen Inhalt vermitteln sollen. Um die Art der Junk-Pakete zu definieren, gibt es drei Parameter: <code class="language-plaintext highlighter-rouge">Jc</code>, <code class="language-plaintext highlighter-rouge">Jmin</code> und <code class="language-plaintext highlighter-rouge">Jmax</code>.</p>

<p><code class="language-plaintext highlighter-rouge">Jc</code> legt die Anzahl der Junk-Pakete fest. Ein <code class="language-plaintext highlighter-rouge">Jc</code> von drei bedeutet beispielsweise, dass vor dem Handshake drei Pakete mit Zufallsdaten gesendet werden sollen. <code class="language-plaintext highlighter-rouge">Jc</code> darf maximal 128 (inklusive) groß sein.</p>

<p><code class="language-plaintext highlighter-rouge">Jmin</code> und <code class="language-plaintext highlighter-rouge">Jmax</code> legen die Größe der Junk-Pakete in Bytes fest - genauer die Größe des UDP-Payloads (die Größe des UDP-Header und des IP-Headers zählen also bei der Größenangabe nicht). <code class="language-plaintext highlighter-rouge">Jmin</code> legt dabei die minimale Größe des Payloads und <code class="language-plaintext highlighter-rouge">Jmax</code> die maximale Größe des Payloads fest. Die Größe des eigentlichen Payloads wird dann zufällig so gewählt, dass sie zwischen <code class="language-plaintext highlighter-rouge">Jmin</code> (inklusiv) und <code class="language-plaintext highlighter-rouge">Jmax</code> (inklusiv) liegt. <code class="language-plaintext highlighter-rouge">Jmin</code> muss kleiner oder gleich <code class="language-plaintext highlighter-rouge">Jmax</code> sein. Beide Werte dürfen maximal 1280 (inklusiv) Byte groß sein.</p>

<p>Wenn <code class="language-plaintext highlighter-rouge">Jmin</code> und <code class="language-plaintext highlighter-rouge">Jmax</code> gleich groß sind, liegt die Größe des Payloads zufällig zwischen <code class="language-plaintext highlighter-rouge">Jmin</code> und <code class="language-plaintext highlighter-rouge">Jmax + 1</code>.</p>

<h3 id="empfohlene-werte">Empfohlene Werte</h3>

<p>Je nach Region und Umständen können verschiedene Werte empfohlen werden. Im Allgemeinen sollte <code class="language-plaintext highlighter-rouge">Jc</code> zwischen 3 (inklusive) und 10 (inklusive) sein. Es wird häufig 3 oder 4 verwendet.
Für <code class="language-plaintext highlighter-rouge">Jmin</code> und <code class="language-plaintext highlighter-rouge">Jmax</code> wird 50 und 1000 bzw. 10 und 50 empfohlen.</p>

<h2 id="junk-im-handshake">Junk im Handshake</h2>

<p>Zusätzlich zu den separaten Junk-Paketen werden auch Zufallsdaten vor den eigentlichen WireGuard Daten in der “Handshake Initation” und der “Handshake Response” hinzugefügt. Die Parameter <code class="language-plaintext highlighter-rouge">S1</code> und <code class="language-plaintext highlighter-rouge">S2</code> legen fest, wie viel Bytes an Junkdaten vor den eigentlichen WireGuard Daten platziert werden sollen. In der “Handshake Initation” werden <code class="language-plaintext highlighter-rouge">S1</code> Bytes vor den eigentlichen Daten und in der “Handshake Response” <code class="language-plaintext highlighter-rouge">S2</code> Bytes Zufallsdaten hinzugefügt. Diese Einstellung muss bei allen Peers gleich sein, damit jeder weiß, wo die eigentlichen sinnvollen Daten anfangen.</p>

<h3 id="empfohlene-werte-1">Empfohlene Werte</h3>

<p>Für <code class="language-plaintext highlighter-rouge">S1</code> und <code class="language-plaintext highlighter-rouge">S2</code> wird ein zufälliger Wert zwischen 15 (inklusive) und 150 (inklusive) empfohlen. Dabei darf <code class="language-plaintext highlighter-rouge">S1 + 56</code> nicht gleich <code class="language-plaintext highlighter-rouge">S2</code> entsprechen.</p>

<h2 id="benutzerdefinierte-typen">Benutzerdefinierte Typen</h2>

<p>Um zu identifizieren, um was für eine WireGuard Nachricht es sich handelt, wird im WireGuard Header ein Typ in Form einer ganzen Zahl angegeben. So hat die “Handshake Initation” den Typ 1 und die “Handshake Response” den Typ 2. Insgesamt gibt es (aktuell) vier festgelegte Typen. Da diese Typen fest definiert sind, eignen sie sich als Kriterium, um WireGuard Pakete zu erkennen. Daher ist es bei AmneziaWG möglich, die vordefinierten Typen zu überschreiben. Der Typ 1 wird zu <code class="language-plaintext highlighter-rouge">H1</code>, der Typ 2 wird zu <code class="language-plaintext highlighter-rouge">H2</code>, der Typ 3 wird zu <code class="language-plaintext highlighter-rouge">H3</code> und der Typ 4 wird zu <code class="language-plaintext highlighter-rouge">H4</code>. Die <code class="language-plaintext highlighter-rouge">H</code>-Werte müssen sich voreinander unterscheiden.</p>

<h3 id="empfohlene-werte-2">Empfohlene Werte</h3>

<p>Es wird ein zufälliger Wert zwischen 5 und 2^31-1=2147483647 bzw. 2^32-1=4294967295 (siehe Diskussion <a href="https://github.com/amnezia-vpn/amneziawg-go/issues/42#issuecomment-2366785806">hier</a>) empfohlen.</p>

<h2 id="rückwärtskompatibilität">Rückwärtskompatibilität</h2>

<p>AmneziaWG kann rückwärtskompatibel zu Vanille WireGuard verwendet werden. Dabei wird lediglich das Feature von Junkpaketen vor dem Handshake aktiviert und Features wie Junkdaten im Handshake und benutzerdefinierte Typen deaktiviert.</p>

<p>Dafür können <a href="https://zaneschepke.com/wgtunnel-docs/features.html#how-to-convert-an-existing-wireguard-config-to-amnezia">folgende Parameter</a> verwendet werden: <code class="language-plaintext highlighter-rouge">S1</code> von 0, <code class="language-plaintext highlighter-rouge">S2</code> von 0, <code class="language-plaintext highlighter-rouge">H1</code> von 1, <code class="language-plaintext highlighter-rouge">H2</code> von 2, <code class="language-plaintext highlighter-rouge">H3</code> von 3 und <code class="language-plaintext highlighter-rouge">H4</code> von 4.</p>

<h2 id="konfiguration">Konfiguration</h2>

<p>Die zusätzlichen Parameter können wie Adress- oder Schlüsselparameter in eine WireGuard Konfigurationsdatei geschrieben werden:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Interface]
PrivateKey = uMnqnbbo+bbnnPVPjjyLqB72JHPvSOew/MqWxPz6PEg=
Address = 192.168.2.4/32

Jc = 3
Jmin = 50
Jmax = 1000
S1 = 20
S2 = 78
H1 = 39131278
H2 = 832138185
H3 = 1436957857
H4 = 1635877746

[Peer]
PublicKey = 8Yr++qyZzEL4zHS7CEgi+9zMm0d09qUA0xeqohNs8F8=
PresharedKey = fbDzRFA/PGH05Uwl9EFcI42F8KABRpSKoqS+Xv2qOl8=
Endpoint = 192.168.1.1:1234
AllowedIPs = fd00::/8
</code></pre></div></div>

<h2 id="paketierung">Paketierung</h2>

<h3 id="linux">Linux</h3>

<p>Unter Linux werden die <a href="https://github.com/amnezia-vpn/amneziawg-tools"><code class="language-plaintext highlighter-rouge">awg-tools</code></a> sowie die <a href="https://github.com/amnezia-vpn/amneziawg-linux-kernel-module">Kernel</a> oder die <a href="https://github.com/amnezia-vpn/amneziawg-go">Go</a>-Implementierung benötigt. Für Debian gibt es ein von den AmneziaWG-Entwicklern bereitgestelltes <a href="https://launchpad.net/~amnezia">Repository</a> für die Tools und die Kernel-Implementierung. Für Arch Linux sind die <a href="https://aur.archlinux.org/packages/amneziawg-tools">Tools</a> und die <a href="https://aur.archlinux.org/packages/amneziawg-go">Go-Implementierung</a> in AUR paketiert.</p>

<h3 id="android">Android</h3>

<p>Aktuell unterstützt in F-Droid nur <a href="https://github.com/zaneschepke/wgtunnel">WG Tunnel</a> AmneziaWG.</p>

<p>Die AmneziaWG Entwickler stellen jedoch auch ein <a href="https://github.com/amnezia-vpn/amnezia-client">eigene VPN-App</a> sowie einen <a href="https://github.com/amnezia-vpn/amneziawg-android">AmneziaWG Client</a> für Android bereit.
Es wurde eine <a href="https://github.com/amnezia-vpn/amneziawg-android/issues/6">Anfrage</a> an die AmneziaWG-Entwickler gestellt, den Client in F-Droid oder IzzyOnDroid aufzunehmen - aktuell ohne Rückmeldung von diesen. Des Weiteren scheint der AmneziaWG Client <a href="https://github.com/amnezia-vpn/amneziawg-android/issues/6#issuecomment-2313103667">nicht von den Entwicklern, sondern Google signiert</a> worden zu sein - falls bei Sicherheitsanwendung problematisch ist.
Auch bei der Amnezia Client-App wurde eine <a href="https://github.com/amnezia-vpn/amnezia-client/issues/263">Anfrage an die Entwickler</a> sowie an <a href="https://gitlab.com/fdroid/rfp/-/issues/2374">F-Droid</a> gesendet. Die Amnezia Client-App ist für IzzyOnDroid <a href="https://github.com/amnezia-vpn/amnezia-client/issues/263#issuecomment-2309026176">leider zu groß</a>.</p>

<h2 id="offizielle-dokumentation">Offizielle Dokumentation</h2>

<p>Eine genaue detaillierte offizielle Dokumentation wird es in vorhersehbarer Zukunft aus Sorge, dies würde Zensoren das Erkennen von AmenziaWG erleichtern <a href="https://github.com/amnezia-vpn/amneziawg-go/issues/42#issuecomment-2351742762">vermutlich nicht geben</a>.</p>

<h2 id="aussicht">Aussicht</h2>

<p>Es gibt aktuell mehrere Anfragen, AmneziaWG in <a href="https://github.com/openwrt/packages/pull/23632">OpenWrt</a> (<a href="https://github.com/openwrt/packages/pull/23633">Tools</a>), <a href="https://forum.mikrotik.com/viewtopic.php?t=206646">MikroTik’s RouterOS</a>, <a href="https://forum.gl-inet.com/t/feature-request-replace-wireguard-with-amneziawg/44783">GL.iNet’s OpenWrt</a>, <a href="https://forum.dd-wrt.com/phpBB2/viewtopic.php?t=337390">DD-Wrt</a> und andere Distributationen aufzunehmen.</p>

<p>Des Weiteren könnte <a href="https://9.demhack.org/en/tasks">geplant</a> sein ein <code class="language-plaintext highlighter-rouge">S3</code> bzw <code class="language-plaintext highlighter-rouge">S4</code> für den WireGuard Anti-DoS-Mechanismus einzuführen.</p>]]>
        </content>
    </entry>
    
    <entry>
        <title>What is AmneziaWG?</title>
        
        <link rel="alternate" href="https://mk16.de/blog/amneziawg-en/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/amneziawg-en/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/amneziawg-en/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/amneziawg-en/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/amneziawg-en/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/amneziawg-en/" hreflang="en" />

        <id>/blog/amneziawg-en/</id>
        <published>2024-09-22T21:00:00+02:00</published>
        <updated>2024-09-22T21:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>What is AmneziaWG? And what do the parameters actually mean?</summary>
        <content type="html">
            <![CDATA[<p><a href="https://docs.amnezia.org/documentation/amnezia-wg/">AmneziaWG</a> is a WireGuard fork that implements anti-detection techniques against DPI systems. These anti-detection techniques are called “Advanced Security”. AmneziaWG can be configured to be backwards compatible with vanilla WireGuard.</p>

<p><img src="/assets/img/amnezia_logo.svg" alt="The picture shows the Amnezia logo. It is the classic anarchy &quot;A&quot; in a circle. However, the logo is very colorful - areas are filled with orange, lula, light blue, pink and gray." /></p>

<p>(from <a href="https://github.com/amnezia-vpn/amnezia-client">https://github.com/amnezia-vpn/amnezia-client</a>, GPL-3.0, cut out by me)</p>

<p>It introduces nine additional parameters (<code class="language-plaintext highlighter-rouge">Jc</code>, <code class="language-plaintext highlighter-rouge">Jmin</code>, <code class="language-plaintext highlighter-rouge">Jmax</code>, <code class="language-plaintext highlighter-rouge">S1</code>, <code class="language-plaintext highlighter-rouge">S2</code>, <code class="language-plaintext highlighter-rouge">H1</code>, <code class="language-plaintext highlighter-rouge">H2</code>, <code class="language-plaintext highlighter-rouge">H3</code>, <code class="language-plaintext highlighter-rouge">H4</code>).</p>

<h2 id="vanilla-wireguard">Vanilla WireGuard</h2>

<p>The WireGuard protocol consists of a variety of messages. Some of the most important messages are the “Handshake Initiation”, the “Handshake Response” and the actual data packets.
These WireGuard messages all have a header in which their type is defined. This is specified using an integer.
To establish a WireGuard connection, a “handshake initiation” is generally sent by the peer that wants to establish the connection. The other peer then responds with a “handshake response”.
These packets can be recognized with the help of DPI systems and then blocked accordingly. This prevents the entire WireGuard tunnel and means that WireGuard is blocked.</p>

<h2 id="junk-packets">Junk packets</h2>

<p>AmneziaWG can send junk packets before the handshake. These are packets that contain random data and are not intended to communicate any information. There are three parameters to define the type of junk packets: <code class="language-plaintext highlighter-rouge">Jc</code>, <code class="language-plaintext highlighter-rouge">Jmin</code> and <code class="language-plaintext highlighter-rouge">Jmax</code>.</p>

<p><code class="language-plaintext highlighter-rouge">Jc</code> defines the number of junk packets. For example, a <code class="language-plaintext highlighter-rouge">Jc</code> of three means that three packets with random data should be sent before the handshake. The maximum size of <code class="language-plaintext highlighter-rouge">Jc</code> is 128 (inclusive).</p>

<p><code class="language-plaintext highlighter-rouge">Jmin</code> and <code class="language-plaintext highlighter-rouge">Jmax</code> define the size of the junk packets in bytes - more precisely the size of the UDP payload (the size of the UDP header and the IP header do not count when specifying the size). ‘Jmin’ specifies the minimum size of the payload and ‘Jmax’ the maximum size of the payload. The size of the actual payload is then randomly selected so that it lies between <code class="language-plaintext highlighter-rouge">Jmin</code> (inclusive) and <code class="language-plaintext highlighter-rouge">Jmax</code> (inclusive). <code class="language-plaintext highlighter-rouge">Jmin</code> must be less than or equal to <code class="language-plaintext highlighter-rouge">Jmax</code>. Both values may be a maximum of 1280 (inclusive) bytes in size.</p>

<p>If <code class="language-plaintext highlighter-rouge">Jmin</code> and <code class="language-plaintext highlighter-rouge">Jmax</code> are the same size, the size of the payload lies randomly between <code class="language-plaintext highlighter-rouge">Jmin</code> and <code class="language-plaintext highlighter-rouge">Jmax + 1</code>.</p>

<h3 id="recommended-values">Recommended values</h3>

<p>Depending on the region and circumstances, different values may be recommended. In general, ‘Jc’ should be between 3 (inclusive) and 10 (inclusive). Often 3 or 4 is used.
For <code class="language-plaintext highlighter-rouge">Jmin</code> and <code class="language-plaintext highlighter-rouge">Jmax</code>, 50 and 1000 or 10 and 50 are recommended respectively.</p>

<h2 id="junk-in-the-handshake">Junk in the handshake</h2>

<p>In addition to the separate junk packets, random data is also added before the actual WireGuard data in the “Handshake initiation” and the “Handshake response”. The parameters ‘S1’ and ‘S2’ define how many bytes of junk data are to be placed before the actual WireGuard data. In the “Handshake Initiation”, <code class="language-plaintext highlighter-rouge">S1</code> bytes are added before the actual data and in the “Handshake Response” <code class="language-plaintext highlighter-rouge">S2</code> bytes of random data. This setting must be the same for all peers so that everyone knows where the actual meaningful data begins.</p>

<h3 id="recommended-values-1">Recommended values</h3>

<p>A random value between 15 (inclusive) and 150 (inclusive) is recommended for <code class="language-plaintext highlighter-rouge">S1</code> and <code class="language-plaintext highlighter-rouge">S2</code>. However, <code class="language-plaintext highlighter-rouge">S1 + 56</code> must not equal <code class="language-plaintext highlighter-rouge">S2</code>.</p>

<h2 id="user-defined-types">User-defined types</h2>

<p>To identify what type of WireGuard message it is, a type is specified in the WireGuard header in the form of an integer. For example, the “Handshake Initiation” is type 1 and the “Handshake Response” is type 2. There are (currently) a total of four defined types. As these types are fixed, they are suitable as a criterion for recognizing WireGuard packets. It is therefore possible to overwrite the predefined types in AmneziaWG. Type 1 becomes <code class="language-plaintext highlighter-rouge">H1</code>, type 2 becomes <code class="language-plaintext highlighter-rouge">H2</code>, type 3 becomes <code class="language-plaintext highlighter-rouge">H3</code> and type 4 becomes <code class="language-plaintext highlighter-rouge">H4</code>. The <code class="language-plaintext highlighter-rouge">H</code> values must be different from each other.</p>

<h3 id="recommended-values-2">Recommended values</h3>

<p>A random value between 5 and 2^31-1=2147483647 or 2^32-1=4294967295 (see discussion <a href="https://github.com/amnezia-vpn/amneziawg-go/issues/42#issuecomment-2366785806">here</a>) is recommended.</p>

<h2 id="backward-compatibility">Backward compatibility</h2>

<p>AmneziaWG can be used backwards-compatible with Vanille WireGuard. Only the feature of junk packets before the handshake is activated and features such as junk data in the handshake and user-defined types are deactivated.</p>

<p>The <a href="https://zaneschepke.com/wgtunnel-docs/features.html#how-to-convert-an-existing-wireguard-config-to-amnezia">following parameters</a> can be used for this: ‘S1’ of 0, ‘S2’ of 0, ‘H1’ of 1, ‘H2’ of 2, ‘H3’ of 3 and ‘H4’ of 4.</p>

<h2 id="configuration">Configuration</h2>

<p>The additional parameters can be written to a WireGuard configuration file in the same way as address or key parameters:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Interface]
PrivateKey = uMnqnbbo+bbnnPVPjjyLqB72JHPvSOew/MqWxPz6PEg=
Address = 192.168.2.4/32

Jc = 3
Jmin = 50
Jmax = 1000
S1 = 20
S2 = 78
H1 = 39131278
H2 = 832138185
H3 = 1436957857
H4 = 1635877746

[Peer]
PublicKey = 8Yr++qyZzEL4zHS7CEgi+9zMm0d09qUA0xeqohNs8F8=
PresharedKey = fbDzRFA/PGH05Uwl9EFcI42F8KABRpSKoqS+Xv2qOl8=
Endpoint = 192.168.1.1:1234
AllowedIPs = fd00::/8
</code></pre></div></div>

<h2 id="packaging">Packaging</h2>

<h3 id="linux">Linux</h3>

<p>Under Linux, the <a href="https://github.com/amnezia-vpn/amneziawg-tools"><code class="language-plaintext highlighter-rouge">awg-tools</code></a> and the <a href="https://github.com/amnezia-vpn/amneziawg-linux-kernel-module">Kernel</a> or the <a href="https://github.com/amnezia-vpn/amneziawg-go">Go</a> implementation are required. For Debian there is a <a href="https://launchpad.net/~amnezia">repository</a> provided by the AmneziaWG developers for the tools and the kernel implementation. For Arch Linux, the <a href="https://aur.archlinux.org/packages/amneziawg-tools">Tools</a> and the <a href="https://aur.archlinux.org/packages/amneziawg-go">Go implementation</a> are packaged in AUR.</p>

<h3 id="android">Android</h3>

<p>Currently only <a href="https://github.com/zaneschepke/wgtunnel">WG Tunnel</a> supports AmneziaWG in F-Droid.</p>

<p>However, the AmneziaWG developers also provide an <a href="https://github.com/amnezia-vpn/amnezia-client">own VPN app</a> and an <a href="https://github.com/amnezia-vpn/amneziawg-android">AmneziaWG client</a> for Android.
A <a href="https://github.com/amnezia-vpn/amneziawg-android/issues/6">request</a> has been made to the AmneziaWG developers to include the client in F-Droid or IzzyOnDroid - currently without feedback from the developers. Furthermore, the AmneziaWG client seems to have been <a href="https://github.com/amnezia-vpn/amneziawg-android/issues/6#issuecomment-2313103667">not signed by the developers, but by Google</a> - which is problematic in the case of a security application.
A <a href="https://github.com/amnezia-vpn/amnezia-client/issues/263">request was also sent to the developers</a> and to <a href="https://gitlab.com/fdroid/rfp/-/issues/2374">F-Droid</a> for the Amnezia client app. The Amnezia client app is <a href="https://github.com/amnezia-vpn/amnezia-client/issues/263#issuecomment-2309026176">unfortunately too large</a> for IzzyOnDroid.</p>

<h2 id="official-documentation">Official documentation</h2>

<p>There will probably be <a href="https://github.com/amnezia-vpn/amneziawg-go/issues/42#issuecomment-2351742762">no detailed official documentation</a> in the foreseeable future out of concern that this would make it easier for censors to recognize AmenziaWG.</p>

<h2 id="outlook">Outlook</h2>

<p>There are currently several requests to include AmneziaWG in <a href="https://github.com/openwrt/packages/pull/23632">OpenWrt</a> (<a href="https://github.com/openwrt/packages/pull/23633">Tools</a>), <a href="https://forum.mikrotik.com/viewtopic.php?t=206646">MikroTik’s RouterOS</a>, <a href="https://forum.gl-inet.com/t/feature-request-replace-wireguard-with-amneziawg/44783">GL.iNet’s OpenWrt</a>, <a href="https://forum.dd-wrt.com/phpBB2/viewtopic.php?t=337390">DD-Wrt</a> and other distributions.</p>

<p>Furthermore, it could be <a href="https://9.demhack.org/en/tasks">planned</a> to introduce an <code class="language-plaintext highlighter-rouge">S3</code> or <code class="language-plaintext highlighter-rouge">S4</code> for the WireGuard Anti-DoS mechanism.</p>]]>
        </content>
    </entry>
    
    <entry>
        <title>Babel&apos;s RTT Optionen</title>
        
        <link rel="alternate" href="https://mk16.de/blog/babel-rtt-options-de/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/babel-rtt-options-de/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/babel-rtt-options-de/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/babel-rtt-options-de/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/babel-rtt-options-de/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/babel-rtt-options-de/" hreflang="de" />

        <id>/blog/babel-rtt-options-de/</id>
        <published>2024-07-18T00:00:00+02:00</published>
        <updated>2024-07-18T00:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Wie kann man babel&apos;s RTT-Funktion einstellen? Was bedeutet `rtt-min`, `rtt-max` und `rtt-cost` / `max-rtt-penalty`?</summary>
        <content type="html">
            <![CDATA[<p>Babel ist ein “Plug-und-Play” Routing Protokoll und hat ein Round Trip Time (RTT) Feature. Dieses Feature ermöglicht es, die Kosten zu den benachbarten Knoten anhand der RTT zu bestimmen. Dabei ist die RTT die Zeit, welche ein Paket braucht, zum anderen Knoten hin und zurückzukommen.</p>

<h2 id="begegnung-mit-rtt">Begegnung mit RTT</h2>

<p>Als ich das erste Mal davon gehört habe, fand ich die Funktion natürlich sehr toll und dachte nicht, dass man beim RTT Feature mehr als “aus” oder “ein” einstellen kann. Tatsächlich gibt es jedoch vier einstellbare Optionen mit Dokumentation. Diese Dokumentation kann für Anfänger in diesem Bereich, wie mir, jedoch schwer zu verstehen sein. Es gibt jedoch eine Mailingliste, auf der man sehr nette Hilfe erhält.</p>

<h2 id="birds-babel-implementierung-bekommt-das-rtt-feature">bird’s babel Implementierung bekommt das RTT Feature</h2>

<p>Das RTT Feature war anfangs nur in babeld - der Referenzimplementierung - verfügbar. Es wurde jedoch auch mit <a href="https://bird.network.cz/pipermail/bird-users/2023-October/017164.html">Version 2.14 in bird implementiert</a>. Ich selber hatte Interesse an babel RTT, da ich im dn42 bird als Routing Dämon und babel als IGP verwende. Des Weiteren bin ich Teilnehmer im CRXN, welches als primäres Routing Protokoll ganz auf babel setzt.</p>

<h2 id="rx-kosten--rtt">RX Kosten = RTT</h2>

<p>Es gibt insgesamt vier Konfigurationsoptionen: <code class="language-plaintext highlighter-rouge">rtt-min</code>, <code class="language-plaintext highlighter-rouge">rtt-max</code>, <code class="language-plaintext highlighter-rouge">rtt-cost</code> in bird bzw. <code class="language-plaintext highlighter-rouge">max-rtt-penalty</code> in babeld und <code class="language-plaintext highlighter-rouge">rtt-decay</code>.</p>

<p>Als ich für das CRXN das RTT das erste Mal konfigurierte, war ich sehr froh, folgenden Schnipsel gefunden zu haben (ich kann mich leider nicht mehr erinnern, wo): <code class="language-plaintext highlighter-rouge">rxcost 1 rtt-min 1 rtt-max 1001 max-rtt-penalty 1000</code>. Diese Zeile bewirkt kurz gesagt, dass die RX-Kosten gleich der Round Trip Time sind und ab einer RTT von 1s nicht mehr höher gehen.</p>

<p>Juliusz, der Entwickler von babel, <a href="https://bird.network.cz/pipermail/bird-users/2023-October/017216.html">schrieb dazu</a>:</p>
<blockquote>
  <p>It is of course possible that these values are not appropriate in your network; however, I fear that the extremely aggressive values that you describe above may cause unnecessary oscillations, especially since BIRD does not yet implement hysteresis on metrics.</p>
</blockquote>

<p>Seit diesem Moment an habe ich überlegt, welche Werte sinnvoller bzw. besser wären. Da ich jedoch recht faul bin und nicht den ganzen Tag mit Netzwerken spielen kann, hat es einige Zeit gedauert, bis ich weiter nachgefragt habe.</p>

<h2 id="konfigurationsoptionen">Konfigurationsoptionen</h2>

<p>Um gute Parameter zu wählen, ist es jedoch notwendig, zu verstehen, was die einzelnen Konfigurationsoptionen bedeuten:</p>

<h3 id="rtt-min"><code class="language-plaintext highlighter-rouge">rtt-min</code></h3>

<p><code class="language-plaintext highlighter-rouge">rtt-min</code> gibt an, ab welchem RTT, das RTT die RX-Kosten beeinflussen sollen. Wenn der aktuelle RTT also unter <code class="language-plaintext highlighter-rouge">rtt-min</code> ist, werden die RX-Kosten nicht verändert.</p>

<p>Juliusz <a href="https://bird.network.cz/pipermail/bird-users/2024-July/017764.html">sagte dazu</a>:</p>
<blockquote>
  <p>rtt-min should be chosen so that all links below rtt-min are local – the idea is that below rtt-min, it’s not worth optimising the path. The default value (10ms) should be fine in most networks.</p>
</blockquote>

<p>Beispiel: Ein <code class="language-plaintext highlighter-rouge">rtt-min</code> von 10ms bedeutet, dass das RTT die RX-Kosten erst beeinflusst, wenn der RTT größer 10ms. Wenn der RTT aktuell also aktuell 6ms ist, bleiben die RX-Kosten vom RTT unbeeinflusst. Wenn der aktuelle RTT allerdings beispielsweise 30ms ist, werden die Kosten erhöht.</p>

<p>Wahl: Das <code class="language-plaintext highlighter-rouge">rtt-min</code> sollte so gewählt werden, dass es nicht kleiner ist als die minimale lokale RTT ist. Wenn also zwei Computer im gleichen Datenzentrum eine RTT von 4ms aufweisen, sollte die <code class="language-plaintext highlighter-rouge">rtt-min</code> nicht kleiner als 4ms sein. Ich persönlich würde des Weiteren die <code class="language-plaintext highlighter-rouge">rtt-min</code> nicht kleiner als den Standardwert von 10ms machen.</p>

<h3 id="rtt-max"><code class="language-plaintext highlighter-rouge">rtt-max</code></h3>

<p><code class="language-plaintext highlighter-rouge">rtt-max</code> gibt an, ab welchem RTT die RX-Kosten nicht mehr beeinflusst werden sollen - also ab welchem RTT die maximalen RTT-spezifischen Kosten addiert werden sollen.</p>

<p>Juliusz <a href="https://bird.network.cz/pipermail/bird-users/2024-July/017764.html">sagte dazu</a>:</p>
<blockquote>
  <p>rtt-max should be larger than all good links in your network – the idea is that above rtt-max, it’s no longer worth optimising the path. In your case, you might want to increase the value to 350ms or so.</p>
</blockquote>

<p>Beispiel: Wenn <code class="language-plaintext highlighter-rouge">rtt-max</code> 120ms und der aktuelle RTT 150ms ist, dass, die RX-Kosten bereits maximal sind und nicht mehr steigen. Ab dem <code class="language-plaintext highlighter-rouge">rtt-max</code> sind alle Links “gleich schlecht” und sollten vermieden werden.</p>

<p>Wahl: Das <code class="language-plaintext highlighter-rouge">rtt-max</code> sollte so gewählt werden, dass es die maximale RTT (am besten mit etwas Luft nach oben) im Netzwerk darstellt. Wenn die maximale RTT im Netzwerk also zwischen 100ms und 130ms variiert, so könnte man 150ms als <code class="language-plaintext highlighter-rouge">rtt-max</code> wählen.</p>

<h3 id="rtt-cost--max-rtt-penalty"><code class="language-plaintext highlighter-rouge">rtt-cost</code> / <code class="language-plaintext highlighter-rouge">max-rtt-penalty</code></h3>

<p>Bei <code class="language-plaintext highlighter-rouge">rtt-max</code> habe ich geschrieben, dass RTL-spezifische Kosten zu den RX-Kosten addiert werden, jedoch nie erwähnt, wie groß diese eigentlich sind. Dies kann man mit <code class="language-plaintext highlighter-rouge">rtt-cost</code> in bird bzw. <code class="language-plaintext highlighter-rouge">max-rtt-penalty</code> in babeld festlegen. Wenn der RTT größer als <code class="language-plaintext highlighter-rouge">rtt-max</code> ist, wird zu den RX-Kosten also der Wert von <code class="language-plaintext highlighter-rouge">rtt-cost</code> addiert. Ansonsten wird der Wert beginnend beim RTT von <code class="language-plaintext highlighter-rouge">rtt-min</code> linear skaliert addiert.</p>

<p>Juliusz <a href="https://bird.network.cz/pipermail/bird-users/2024-July/017766.html">sagte dazu</a>:</p>
<blockquote>
  <p>Max-rtt-penalty controls how much Babel will prefer routes with many links but low RTT to routes with few links but hight RTT.  The default is fairly conservative, meaning that RTT has only a very small influence on routing. You should set it to 96*n where n is the maximum number of extra links that you’re willing to take in order to avoid one high-RTT link.</p>
</blockquote>

<p>Beispiel:
Die RX-Kosten sind mit <code class="language-plaintext highlighter-rouge">96</code> konfiguriert, die <code class="language-plaintext highlighter-rouge">rtt-cost</code> mit <code class="language-plaintext highlighter-rouge">100</code> und <code class="language-plaintext highlighter-rouge">rtt-max</code> mit <code class="language-plaintext highlighter-rouge">500</code>. Die RTT ist nun 643ms. Dies bedeutet, dass die RTT größer als <code class="language-plaintext highlighter-rouge">rtt-max</code> ist und somit die volle <code class="language-plaintext highlighter-rouge">rtt-cost</code> auf die RX-Kosten addiert werden. Die effektiven Kosten belaufen sich also auf 96+100=196.</p>

<p>Wahl: Es gilt, dass <code class="language-plaintext highlighter-rouge">rtt-cost</code>/(<code class="language-plaintext highlighter-rouge">rtt-max</code>-<code class="language-plaintext highlighter-rouge">rtt-min</code>) angibt, in welchem Intervall abhängig vom RTT sich die RX-Kosten erhöhen. Bei <code class="language-plaintext highlighter-rouge">rtt-min</code> von 10, <code class="language-plaintext highlighter-rouge">rtt-max</code> von 1000 und <code class="language-plaintext highlighter-rouge">rtt-cost</code> von 192, erhöhen sich beispielsweise die RX-Kosten jede 5ms im RTT um Eins. Des Weiteren sollte man bedenken, dass ein zu hoher Wert und die Verwendung von einer nicht vollständigen RTT Implementierung zu Schwingungen und im schlimmsten Fall dann auch zu Route Flapping führen könnte. Ich persönlich empfehle daher, die <code class="language-plaintext highlighter-rouge">rtt-cost</code> niedriger als die <code class="language-plaintext highlighter-rouge">rtt-max</code> zu wählen.</p>

<h3 id="rtt-decay"><code class="language-plaintext highlighter-rouge">rtt-decay</code></h3>

<p><code class="language-plaintext highlighter-rouge">rtt-decay</code> gibt an, inwiefern ältere RTT Proben bei der Berechnung des RTT berücksichtigt werden sollen. Ein höheres Decay bedeutet, dass ältere RTT Proben verworfen bzw. nicht mehr berücksichtigt werden sollen. Ein niedriges Decay bedeutet dagegen, dass mehr ältere RTT Proben berücksichtigt werden sollen.</p>

<p>Juliusz <a href="https://bird.network.cz/pipermail/bird-users/2024-July/017766.html">sagte dazu</a>:</p>
<blockquote>
  <p>As to rtt-decay, the default value should be fine unless you have a mobile network.  Increase it in order to react faster to RTT variations, at the risk of route flaps every time the network has a hiccup.</p>
</blockquote>

<p>Wahl: Im Zweifelsfall: Nehme den Standardwert. Wenn du mobil (beispielsweise Mobilfunk oder Bahn-WLAN) unterwegs bist, empfiehlt es sich, das Decay zu erhöhen (beispielsweise auf 150).</p>

<h2 id="konfiguration">Konfiguration</h2>

<p>In folgenden Beispielen wird das Interface <code class="language-plaintext highlighter-rouge">crxnbandura</code> mit <code class="language-plaintext highlighter-rouge">rtt-min</code> von 10ms, <code class="language-plaintext highlighter-rouge">rtt-max</code> mit 1000ms bzw. 1s, <code class="language-plaintext highlighter-rouge">rtt-cost</code> von 192 und <code class="language-plaintext highlighter-rouge">rtt-decay</code> von 60 konfiguriert.</p>

<p>bird:</p>
<pre><code class="language-bird">protocol babel {
    interface "crxnbandura" {
        type tunnel;
        rtt cost 192;
        rtt min 10 ms;
        rtt max 1000 ms;
        rtt decay 60;
    };

    ipv6 { /* Stuff */ }
}
</code></pre>
<p>Bei bird besteht die Möglichkeit als Einheit entweder <code class="language-plaintext highlighter-rouge">ms</code> für Milisekunden oder <code class="language-plaintext highlighter-rouge">s</code> für Sekunden zu wählen. Es ist verpflichtend eine Einheit anzugeben.</p>

<p>babeld:</p>
<pre><code class="language-babeld">interface crxnbandura type tunnel
interface crxnbandura rtt-min 10
interface crxnbandura rtt-max 1000
interface crxnbandura max-rtt-penality 192
</code></pre>
<p>Oder in Kurzform:</p>
<pre><code class="language-babeld">interface crxnbandura type tunnel rtt-min 10 rtt-max 1000 max-rtt-penality 192
</code></pre>

<h2 id="andere-netzwerke">Andere Netzwerke</h2>

<h3 id="re6st-mesh-network">re6st mesh network</h3>

<p>Das <a href="https://lab.nexedi.com/nexedi/re6stnet">re6st mesh network</a> <a href="https://alioth-lists.debian.net/pipermail/babel-users/2024-July/004176.html">verwendet</a> den Standardwert (10ms) als <code class="language-plaintext highlighter-rouge">rtt-min</code>, 500ms als <code class="language-plaintext highlighter-rouge">rtt-max</code>, 5000 als <code class="language-plaintext highlighter-rouge">rtt-cost</code> und 125 als <code class="language-plaintext highlighter-rouge">rtt-decay</code>. Dabei sollte man, wie von <a href="https://alioth-lists.debian.net/pipermail/babel-users/2024-July/004179.html">Juliusz angemerkt</a>, bedenken, dass 5000 als <code class="language-plaintext highlighter-rouge">rtt-cost</code> recht hoch ist.</p>

<h2 id="meine-werte">Meine Werte</h2>

<p>Da ich aktuell selber noch experimentiere, kann ich selber nur schlecht eine Empfehlung abgeben. <strong>Aktuell</strong> verwende ich im CRXN 10ms als <code class="language-plaintext highlighter-rouge">rtt-min</code>, 1000ms als <code class="language-plaintext highlighter-rouge">rtt-max</code> und 2 * 96 als <code class="language-plaintext highlighter-rouge">rtt-cost</code>. Dazu sollte man sagen, dass der <code class="language-plaintext highlighter-rouge">rtt-max</code> Wert vermutlich übertrieben ist, da ich als maximale Link RTT etwa 350ms habe. Des Weiteren überlege ich auf 3 * 96 für <code class="language-plaintext highlighter-rouge">rtt-cost</code> umzusteigen, um die tatsächliche RTT besser widerzuspiegeln.</p>]]>
        </content>
    </entry>
    
    <entry>
        <title>Babel&apos;s RTT options</title>
        
        <link rel="alternate" href="https://mk16.de/blog/babel-rtt-options-en/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/babel-rtt-options-en/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/babel-rtt-options-en/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/babel-rtt-options-en/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/babel-rtt-options-en/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/babel-rtt-options-en/" hreflang="en" />

        <id>/blog/babel-rtt-options-en/</id>
        <published>2024-07-18T00:00:00+02:00</published>
        <updated>2024-07-18T00:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>How can I set babel&apos;s RTT function? What do `rtt-min`, `rtt-max` and `rtt-cost` / `max-rtt-penalty` mean?</summary>
        <content type="html">
            <![CDATA[<p>Babel is a “plug-and-play” routing protocol and has a Round Trip Time (RTT) feature. This feature makes it possible to determine the costs to the neighboring nodes based on the RTT. The RTT is the time it takes a packet to get to the other node and back.</p>

<h2 id="encounter-with-rtt">Encounter with RTT</h2>

<p>When I first heard about it, I naturally thought it was a great feature and didn’t think you could set more than “off” or “on” for the RTT feature. However, there are actually four adjustable options with documentation. However, this documentation can be difficult to understand for beginners like me. However, there is a mailing list where you can get help.</p>

<h2 id="birds-babel-implementation-gets-the-rtt-feature">bird’s babel implementation gets the RTT feature</h2>

<p>The RTT feature was initially only available in babeld - the reference implementation. However, it was also <a href="https://bird.network.cz/pipermail/bird-users/2023-October/017164.html">implemented in bird with version 2.14</a>. I myself was interested in babel RTT because I use bird as routing daemon and babel as IGP in dn42. I am also a participant in CRXN, which relies entirely on babel as its primary routing protocol.</p>

<h2 id="rx-costs--rtt">RX costs = RTT</h2>

<p>There are a total of four configuration options: <code class="language-plaintext highlighter-rouge">rtt-min</code>, <code class="language-plaintext highlighter-rouge">rtt-max</code>, <code class="language-plaintext highlighter-rouge">rtt-cost</code> in bird or <code class="language-plaintext highlighter-rouge">max-rtt-penalty</code> in babeld and <code class="language-plaintext highlighter-rouge">rtt-decay</code>.</p>

<p>When I configured the RTT for the CRXN for the first time, I was very happy to have found the following snippet (unfortunately I can no longer remember where): <code class="language-plaintext highlighter-rouge">rxcost 1 rtt-min 1 rtt-max 1001 max-rtt-penalty 1000</code>. In short, this line has the effect that the RX costs are equal to the round trip time and do not go any higher from an RTT of 1s.</p>

<p>Juliusz, the developer of babel, <a href="https://bird.network.cz/pipermail/bird-users/2023-October/017216.html">wrote about this</a>:</p>
<blockquote>
  <p>It is of course possible that these values are not appropriate in your network; however, I fear that the extremely aggressive values that you describe above may cause unnecessary oscillations, especially since BIRD does not yet implement hysteresis on metrics.</p>
</blockquote>

<p>From that moment on, I have been thinking about which values would be more practical or better. However, as I am quite lazy and can’t play with networks all day, it took me some time to ask further.</p>

<h2 id="configuration-options">Configuration options</h2>

<p>In order to choose good parameters, however, it is necessary to understand what the individual configuration options mean:</p>

<h3 id="rtt-min"><code class="language-plaintext highlighter-rouge">rtt-min</code></h3>

<p><code class="language-plaintext highlighter-rouge">rtt-min</code> specifies the RTT from which the RTT should influence the RX costs. So if the current RTT is below <code class="language-plaintext highlighter-rouge">rtt-min</code>, the RX costs are not changed.</p>

<p>Juliusz <a href="https://bird.network.cz/pipermail/bird-users/2024-July/017764.html">said</a>:</p>
<blockquote>
  <p>rtt-min should be chosen so that all links below rtt-min are local – the idea is that below rtt-min, it’s not worth optimising the path. The default value (10ms) should be fine in most networks.</p>
</blockquote>

<p>Example: An <code class="language-plaintext highlighter-rouge">rtt-min</code> of 10ms means that the RTT only influences the RX costs when the RTT is greater than 10ms. So if the RTT is currently 6ms, the RX costs remain unaffected by the RTT. However, if the current RTT is 30ms, for example, the costs are increased.</p>

<p>Choice: The <code class="language-plaintext highlighter-rouge">rtt-min</code> should be chosen so that it is not smaller than the minimum local RTT. So if two computers in the same data center have an RTT of 4ms, the <code class="language-plaintext highlighter-rouge">rtt-min</code> should not be less than 4ms. I personally would not make the <code class="language-plaintext highlighter-rouge">rtt-min</code> smaller than the default value of 10ms.</p>

<h3 id="rtt-max"><code class="language-plaintext highlighter-rouge">rtt-max</code></h3>

<p><code class="language-plaintext highlighter-rouge">rtt-max</code> specifies from which RTT onwards the RX costs should no longer be influenced - i.e. from which RTT onwards the maximum RTT-specific costs should be added.</p>

<p>Juliusz <a href="https://bird.network.cz/pipermail/bird-users/2024-July/017764.html">said</a>:</p>
<blockquote>
  <p>rtt-max should be larger than all good links in your network – the idea is that above rtt-max, it’s no longer worth optimising the path. In your case, you might want to increase the value to 350ms or so.</p>
</blockquote>

<p>Example: If <code class="language-plaintext highlighter-rouge">rtt-max</code> is 120ms and the current RTT is 150ms, the RX cost is already at its maximum and will not increase. From <code class="language-plaintext highlighter-rouge">rtt-max</code> onwards, all links are “equally bad” and should be avoided.</p>

<p>Choice: The <code class="language-plaintext highlighter-rouge">rtt-max</code> should be chosen so that it represents the maximum RTT (preferably with a little room for variation) in the network. So if the maximum RTT in the network varies between 100ms and 130ms, you could choose 150ms as the <code class="language-plaintext highlighter-rouge">rtt-max</code>.</p>

<h3 id="rtt-cost--max-rtt-penalty"><code class="language-plaintext highlighter-rouge">rtt-cost</code> / <code class="language-plaintext highlighter-rouge">max-rtt-penalty</code></h3>

<p>With <code class="language-plaintext highlighter-rouge">rtt-max</code> I wrote that RTL-specific costs are added to the RX costs, but never mentioned how large these actually are. This can be specified with <code class="language-plaintext highlighter-rouge">rtt-cost</code> in bird or <code class="language-plaintext highlighter-rouge">max-rtt-penalty</code> in babeld. If the RTT is greater than <code class="language-plaintext highlighter-rouge">rtt-max</code>, the value of <code class="language-plaintext highlighter-rouge">rtt-cost</code> is added to the RX costs. Otherwise, the value is added linearly scaled starting at the RTT of <code class="language-plaintext highlighter-rouge">rtt-min</code>.</p>

<p>Juliusz <a href="https://bird.network.cz/pipermail/bird-users/2024-July/017766.html">said</a>:</p>
<blockquote>
  <p>Max-rtt-penalty controls how much Babel will prefer routes with many links but low RTT to routes with few links but hight RTT.  The default is fairly conservative, meaning that RTT has only a very small influence on routing. You should set it to 96*n where n is the maximum number of extra links that you’re willing to take in order to avoid one high-RTT link.</p>
</blockquote>

<p>Example:
The RX costs are configured with <code class="language-plaintext highlighter-rouge">96</code>, the <code class="language-plaintext highlighter-rouge">rtt-cost</code> with <code class="language-plaintext highlighter-rouge">100</code> and <code class="language-plaintext highlighter-rouge">rtt-max</code> with <code class="language-plaintext highlighter-rouge">500</code>. The RTT is now 643ms. This means that the RTT is greater than <code class="language-plaintext highlighter-rouge">rtt-max</code> and therefore the full <code class="language-plaintext highlighter-rouge">rtt-cost</code> is added to the RX costs. The effective cost is therefore 96+100=196.</p>

<p>Choice: It applies that <code class="language-plaintext highlighter-rouge">rtt-cost</code>/(<code class="language-plaintext highlighter-rouge">rtt-max</code>-<code class="language-plaintext highlighter-rouge">rtt-min</code>) indicates the RTT interval in which the RX costs increase by one. For example, with <code class="language-plaintext highlighter-rouge">rtt-min</code> of 10, <code class="language-plaintext highlighter-rouge">rtt-max</code> of 1000 and <code class="language-plaintext highlighter-rouge">rtt-cost</code> of 192, the RX costs increase by one every 5ms in the RTT. You should also bear in mind that too high a value and the use of an incomplete RTT implementation could lead to oscillations and, in the worst case, to route flapping. I therefore personally recommend setting the <code class="language-plaintext highlighter-rouge">rtt-cost</code> lower than the <code class="language-plaintext highlighter-rouge">rtt-max</code>.</p>

<h3 id="rtt-decay"><code class="language-plaintext highlighter-rouge">rtt-decay</code></h3>

<p><code class="language-plaintext highlighter-rouge">rtt-decay</code> specifies the extent to which older RTT samples should be taken into account when calculating the RTT. A higher decay means that older RTT samples should be discarded or no longer taken into account. A low decay, on the other hand, means that more older RTT samples should be taken into account.</p>

<p>Juliusz <a href="https://bird.network.cz/pipermail/bird-users/2024-July/017766.html">said</a>:</p>
<blockquote>
  <p>As to rtt-decay, the default value should be fine unless you have a mobile network.  Increase it in order to react faster to RTT variations, at the risk of route flaps every time the network has a hiccup.</p>
</blockquote>

<p>Choice: If in doubt, use the default value. If you are on the move (e.g. mobile phone or WLAN in train), it is advisable to increase the decay (e.g. to 150).</p>

<h2 id="konfiguration">Konfiguration</h2>

<p>In the following examples, the interface <code class="language-plaintext highlighter-rouge">crxnbandura</code> is configured with <code class="language-plaintext highlighter-rouge">rtt-min</code> of 10ms, <code class="language-plaintext highlighter-rouge">rtt-max</code> of 1000ms or 1s, <code class="language-plaintext highlighter-rouge">rtt-cost</code> of 192 and <code class="language-plaintext highlighter-rouge">rtt-decay</code> of 60.</p>

<p>bird:</p>
<pre><code class="language-bird">protocol babel {
    interface "crxnbandura" {
        type tunnel;
        rtt cost 192;
        rtt min 10 ms;
        rtt max 1000 ms;
        rtt decay 60;
    };

    ipv6 { /* Stuff */ }
}
</code></pre>
<p>For bird, it is possible to select either <code class="language-plaintext highlighter-rouge">ms</code> for milliseconds or <code class="language-plaintext highlighter-rouge">s</code> for seconds as the unit. It is mandatory to specify a unit.</p>

<p>babeld:</p>
<pre><code class="language-babeld">interface crxnbandura type tunnel
interface crxnbandura rtt-min 10
interface crxnbandura rtt-max 1000
interface crxnbandura max-rtt-penality 192
</code></pre>
<p>Or in short form:</p>
<pre><code class="language-babeld">interface crxnbandura type tunnel rtt-min 10 rtt-max 1000 max-rtt-penality 192
</code></pre>

<h2 id="other-networks">Other networks</h2>

<h3 id="re6st-mesh-network">re6st mesh network</h3>

<p>The <a href="https://lab.nexedi.com/nexedi/re6stnet">re6st mesh network</a> <a href="https://alioth-lists.debian.net/pipermail/babel-users/2024-July/004176.html">uses</a> the default value (10ms) as <code class="language-plaintext highlighter-rouge">rtt-min</code>, 500ms as <code class="language-plaintext highlighter-rouge">rtt-max</code>, 5000 as <code class="language-plaintext highlighter-rouge">rtt-cost</code> and 125 as <code class="language-plaintext highlighter-rouge">rtt-decay</code>. As noted by <a href="https://alioth-lists.debian.net/pipermail/babel-users/2024-July/004179.html">Juliusz</a>, you should bear in mind that 5000 as <code class="language-plaintext highlighter-rouge">rtt-cost</code> is quite high.</p>

<h2 id="my-values">My values</h2>

<p>As I am currently still experimenting myself, it is difficult for me to make a recommendation. <strong>Currently</strong> I use 10ms as <code class="language-plaintext highlighter-rouge">rtt-min</code>, 1000ms as <code class="language-plaintext highlighter-rouge">rtt-max</code> and 2 * 96 as <code class="language-plaintext highlighter-rouge">rtt-cost</code> in the CRXN. It should be said that the <code class="language-plaintext highlighter-rouge">rtt-max</code> value is probably excessively high, as I have a maximum link RTT of around 350ms. I am also considering switching to 3 * 96 for <code class="language-plaintext highlighter-rouge">rtt-cost</code> to better reflect the actual RTT.</p>]]>
        </content>
    </entry>
    
    <entry>
        <title>Ich trete CRXN bei</title>
        
        <link rel="alternate" href="https://mk16.de/blog/beitritt_crxn/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/beitritt_crxn/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/beitritt_crxn/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/beitritt_crxn/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/beitritt_crxn/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/beitritt_crxn/" hreflang="de" />

        <id>/blog/beitritt_crxn/</id>
        <published>2022-11-22T00:00:00+01:00</published>
        <updated>2022-11-22T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Ich bin dem CRXN Netzwerk beigetreten. Hier zeige ich die Unterschiede zum dn42 und welche Schritte ich gegangen bin, um beizutreten.</summary>
        <content type="html">
            <![CDATA[<p>Hallo,</p>

<p>heute bin ich dem CRXN Netwerk beigetreten. Das CRXN ist ähnlich wie das dn42, jedoch mit vier Unterschieden:</p>
<ul>
  <li>Es verwendet nur IPv6</li>
  <li>Es verwendet das Routing Protokoll babel</li>
  <li>Es ist deutliche klein, was man an der Anzahl der Prefixe sehen kann</li>
  <li>Es wird fastd als Tunnel verwendet</li>
</ul>

<p>Ich wollte das CRXN auf den gleichen Knoten haben, die ich auch das dn42 Netzwerk benutzte, jedoch dürften sich die Routen der beiden Netzwerke nicht vermischen, da ihre Registries nicht miteinander synchronisiert sind, andere Routingprotokolle verwenden und allgemein keine Interkonnectivität zwsichen ihnen vorhanden ist. Dies stellte eine kleine Herausforderung da.</p>

<p>Des Weiteren ist es im CRXN aktuell (noch) nicht möglich die Routen zu validieren, sodass Route Hjacking oder ähnliches recht leicht ist.</p>

<h2 id="es-verwendet-nur-ipv6">Es verwendet nur IPv6</h2>
<p>Ich halte für eine gute Entscheidung, nur IPv6 zu verwenden. Nicht umsonst ist IPv6 die Zukunft. CRXN verwendet den ULA Bereich. Dieser entspricht dem privaten IPv4 Bereich. Dieser Bereich ist so groß, dass es (wenn die Prefixe zufällig generiert werden) im Normalfall zu keiner Kollision zum Beispiel mit dem dn42 kommt.</p>

<h2 id="es-verwendet-das-routingprotokoll-babel">Es verwendet das Routingprotokoll babel</h2>
<p>Babel wird eigentlich für IGP eingesetzt, jedoch ist es auch möglich Netze damit zu verbinden. Das Routing wird so effizienter, da als Auswahlkriterium eine Metric bentzt wird, welche in der Referenzimplementierung (babeld), der Latenz in ms entspricht. Somit wird immer der Pfad mit der kürzesten Latenz verwendet. Auch bird und frr haben babel mittlerweile implementiert, jedoch wird dort die Metric (leider) nicht automatisch berechnet. Ich habe es so gemacht, dass ich diese zu Anfang eines Peering messe und dann manuell eintrage. Das Problem daran ist, dass sich Latenzen bedauerlicherweise verändern können - beispielsweise wenn sich das Underlay-Netzwerk (hier das Clearnet) verändert.</p>

<h2 id="es-ist-klein">Es ist klein</h2>

<p>Wenn man keine strenge Validierung der Routen vornimmt, erhält man ca. 20 IPv6 Prefixe. Wenn man eine strengere Validierung vornimmt, erhält man etwa 10 Prefixe. Bei dn42 (+ NeoNetwork + IC-VPN + ChaosVPN) sind es ca. 600 Prefixe.</p>

<h2 id="es-verwendet-fastd">Es verwendet fastd</h2>
<p>In dn42 wird normalerweise WireGuard zum Peering benutzt, da dies einfach zu konfigurieren ist und eine gute Sicherheit bietet. Davor wurde OpenVPN verwendet. OpenVPN ist einmal langsamer und einmal unsicherer, wenn man es mit einem Preshared Key (PSK) verwendet.
fastd wird auch von Freifunk / IC-VPN verwendet. Es handelt sich dabei auch um eine Tunnellösung, welche auch mit guter Sicherheit verschlüsselt ist. In Gegensatz zu WireGuard operiert es auf Layer 2 des OSI-Modells (MAC-Adressen), wobei WireGuard auf Layer 3 (IP-Adressen) arbeitet.</p>

<h2 id="mehrere-netze-auf-einem-knoten">Mehrere Netze auf einem Knoten</h2>
<p>Mehrere Netze auf einem Knoten - dafür gibt es verschiedene Lösungsansätze.
Eine Lösung wäre mehrere bird Instanzen zu verwenden. Für diese Lösung fällt mir das Wissen und ist damit für mich zu aufwendig.
Ein anderer Lösungsansatz wäre die Verwendung von verschiedenen Routing Tabellen. So könnte man mit <code class="language-plaintext highlighter-rouge">ipv6 table crxn;</code> eine zweite IPv6 Routing Table erstellen. Problematisch wird dieser Ansatz, wenn man - wie ich - mehrere Knoten hat, welche über iBGP verbunden ist. Ich müsste als doppelt so viele BGP Sessions als jetzt haben, da in einer BGP Session nicht mehrere Routing Tabellen übertragen werden können.
Eine andere Möglichkeit ist das “markieren” von Routen. Beim Import würde die dn42 Routen mit “dn42” markiert und die CRXN Routen mit “crxn”. Beim Export könnte man nun jede Route dem entsprechenden Netz zuordnen. Es gibt verschiedene Möglichkeiten, eine Route zu markieren. Die bekannteste ist wohl BGP Communities bzw. Large BGP Communities (bei 32-Bit ASNs) anzuhängen. Da dn42 Peers jedoch nur dn42 Routen erwarten, ist die Information, dass es sich um dn42 Routen handelt, unnötig und würde nur Platz in der Globalen Routing Tabelle einnehmen. Dies bedeutet, man müsste die entsprechenden Communities beim Export löschen.
Das nächste Problem mit Communities war, dass ich zwei AS-Nummern habe. Einmal von dn42 und einmal vom NeoNetwork. Mit welcher sollte ich die Routen markieren? Da es nur intern wäre, wäre es egal.
Eine Alternative zu den BGP communities stellen benutzerdefinierte BGP Attribute dar. Ein BGP Attribut ist beispielsweise <code class="language-plaintext highlighter-rouge">bgp_local_pref</code> oder <code class="language-plaintext highlighter-rouge">bgp_med</code>, jedoch kann man auch selber welche definieren. Für diese Möglichkeit habe ich mich entschieden. Ich habe mit <code class="language-plaintext highlighter-rouge">attribute int netid;</code> ein neues ganzzahliges Attribut mit dem Namen <code class="language-plaintext highlighter-rouge">netid</code> erstellt. Wenn die Route vom dn42 Netz bekommt, bekommt sie die ID <code class="language-plaintext highlighter-rouge">1</code>. Wenn die Route vom CRXN Netz kommt, bekommt sie die ID <code class="language-plaintext highlighter-rouge">2</code>. Entsprechend muss ich das andere Netz beim Export filtern. Somit habe ich alle Routen in einer Routing Tabelle und entsprechend markiert.</p>

<h2 id="fehlende-validierung-der-routen">Fehlende Validierung der Routen</h2>
<p>In dn42 kann man sich aus dem Registry ROA Einträge erzeugen lassen. In diesen steht drin, welches AS, welche Prefixe und mit welcher Länge announcen darf. In CRXN gibt es jedoch keine AS-Nummern, weshalb eine ROA Filterung nicht in Frage kommt. Als Registry gibt es lediglich eine <code class="language-plaintext highlighter-rouge">entitydb</code>, in welcher der Name des Netzwerkes und der Prefix des Netzwerkes angegeben ist. Die Dateien darin befinden sich im JSON Format. Die einzige Validierung, welche ich somit vornehmen kann, ist zu sagen, dass nicht-CRXN Prefixe nicht exportiert werden. Die Prefixe, welche erlaubt sind, parse ich mir wiederum aus der <code class="language-plaintext highlighter-rouge">entitydb</code>. Alle anderen Routen verwerfe ich.</p>

<h2 id="zukunft">Zukunft</h2>
<p>Geplant ist eine eventuelle Vernetzung mit dem dn42. Dabei soll es eine ASN geben, welche die Berechtigung hat, alle CRXN Routen zu exportieren. Mit entsprechender Konfiguration könnte man so mehrere Gateways zwischen dem dn42 und dem CRXN herstellen. Es bleibt spannend.</p>

<h2 id="links">Links</h2>
<ul>
  <li>entitydb: <a href="https://codeberg.org/CRXN/entitydb">https://codeberg.org/CRXN/entitydb</a></li>
  <li>CRXN Dokumentation: <a href="http://deavmi.assigned.network/projects/crxn/">http://deavmi.assigned.network/projects/crxn/</a></li>
  <li>CRXN Dokumentation Mirror: <a href="https://crxn.de/docs/">https://crxn.de/docs/</a></li>
  <li>fastd Dokumentation: <a href="https://fastd.readthedocs.io/en/stable/index.html">https://fastd.readthedocs.io/en/stable/index.html</a></li>
</ul>]]>
        </content>
    </entry>
    
    <entry>
        <title>Wie werden BGP Routen ausgewählt aus und wie kann man dies beeinflussen?</title>
        
        <link rel="alternate" href="https://mk16.de/blog/bgp-route-selection-de/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/bgp-route-selection-de/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/bgp-route-selection-de/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/bgp-route-selection-de/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/bgp-route-selection-de/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/bgp-route-selection-de/" hreflang="de" />

        <id>/blog/bgp-route-selection-de/</id>
        <published>2023-03-05T00:00:00+01:00</published>
        <updated>2023-03-05T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Wie werden in BGP Routen ausgewählt? Wie kann man diese Auswahl beeinflussen? Hier erkläre ich die Kriterien sowie Möglichkeiten zur Manipulation wie Path Prepending, BGP local preference und Multiple Exit Discriminator (MED).</summary>
        <content type="html">
            <![CDATA[<h2 id="wie-werden-bgp-routen-ausgewählt">Wie werden BGP Routen ausgewählt?</h2>

<p>Folgendes sind Kriterien in bird:</p>

<ol>
  <li>Es wird die Route mit dem <strong>höchsten</strong> Gewicht präferiert. Dafür gibt es in bird den Wert <code class="language-plaintext highlighter-rouge">preference</code>. Dieser wird zum Beispiel zur Bevorzugung von IGP über EGP benutzt - so wird beispielsweise Babel über BGP bevorzugt.</li>
  <li>Es wird die Route mit der <strong>höchsten</strong> lokalen Präferenz ausgewählt. Dies kann man im Import-Filter mit der Variable <code class="language-plaintext highlighter-rouge">bgp_local_pref</code> beeinflussen. Die Standardpräferenz ist 100.</li>
  <li>Die Route mit dem <strong>kürzesten</strong> AS Pfad wird bevorzugt. Dies ist in allen Implementierungen ein entscheidenes Kriterium. Der Pfad gibt grundsätzlich (Ausnahme: Path Prepending) an, wie viele autonome Systeme durchquert werden müssen, um das Ziel zu erreichen.</li>
  <li>Danach wird ein sehr selten benutzter Wert überprüft: der Ursprung. Über IGP empfangende Routen werden präferiert. In bird wird mittlerweile jedoch <code class="language-plaintext highlighter-rouge">preference</code> benutzt.</li>
  <li>Falls man mehrere Peerings mit demselben autonomen System hat, wird der <strong>kleinste</strong>  Multiple Exit Discriminator (MED) bevorzugt. Falls man beispielsweise einmal dezidiert und einmal über einen IXP verbunden ist, so möchte man im Allgemeinen die dezidierte Leitung bevorzugen, da diese nicht geteilt werden muss. Daher bekommen Routen, welche darüber empfangen wurde, eine kleinere MED.</li>
  <li>Routen, welche über eBGP empfangen werden, werden über Routen von iBGP bevorzugt. Dies bedeutet, bird neigt dazu, die Routen zu einem externen autonomen System zu benutzten, anstatt sie weiter in das eigene autonome System zu leiten.</li>
  <li>Es werden Routen mit der <strong>geringsten</strong> internen Entfernung zu einem Boundary-Router bevorzugt. Es werden also die Routen bevorzugt, welche von Routern kommen, welche nach außen mit anderen autonomen System verbunden sind.</li>
  <li>Es wird die Route vom Router mit der <strong>niedrigsten</strong> Router ID bevorzugt.</li>
</ol>

<p>Man sollte jedoch beachten, dass die Kriterien Implementationsspezifisch sind. So sind die Kriterien und die Reihenfolge bei FRR beispielsweise teilweise anders.</p>

<h2 id="wie-kann-man-die-routen-auswahl-beeinflussen">Wie kann man die Routen-Auswahl beeinflussen?</h2>

<h3 id="path-prepending">Path Prepending</h3>

<p>Path Prepending ist eine beliebte Taktik, um einen Weg unattraktiver zu machen. Wie man an den Kriterien oben sehen kann, wird die AS Pfad-Länge recht weit oben als Kriterium benutzt. Umso länger der AS-Pfad ist, umso unattraktiver die Route. Möchte man nun nicht, dass eine Route bevorzugt benutzt wird, kann man den AS-Pfad länger machen und damit die Route unattraktiver.
In der Praxis hängt man seine eigene AS-Nummer an den Pfad an. Dies bedeutet aber auch, dass Path Prepending erkennbar ist, welches in gut konfigurierten Systemen jedoch kein Problem darstellt. Um Path Prepending in bird durchzuführen, gibt es die Funktion <code class="language-plaintext highlighter-rouge">prepend</code>:</p>
<pre><code class="language-bird">bgp_path.prepend(64500);
</code></pre>
<p>In diesem Code Abschnitt würde man zum Beispiel die AS Nummer <code class="language-plaintext highlighter-rouge">64500</code> an den Pfad angehängt (prependen). Dabei gibt das Argument <em>nicht</em> die Anzahl der Prepends, sondern die AS-Nummer, welche angehängt werden soll an. In anderen BGP Implementierungen, kann das Argument jedoch auch die Anzahl angeben!
In der Praxis verwendet man ein bis drei Prepends. Danach wird die Route so unattraktiv, dass sie so gut wie nicht mehr verwendet wird.
Path Prepending ist also - wenn richtig eingesetzt - ein starkes Mittel, um eine Route global unattraktiver zu machen. Path Prepending geht über das eigene und die benachbarten autonomen Systeme hinaus.</p>

<h3 id="bgp-lokale-präferenz">BGP lokale Präferenz</h3>

<p>Die lokale Präferenz ist ein sehr starkes Instrument - und könnte beinah als rohe Gewalt bezeichnet werden. Die AS Pfad-Länge oder anderen Kriterien werden nicht mehr überprüft, wenn eine einzige Route zum Ziel eine hohe Präferenz hat. Die lokale Präferenz sollte daher sorgfältig benutzt werden.
Eine gute Anwendungsmöglichkeit wäre, wenn man sicherstellen möchte, dass man Routen von einem benachbarten AS direkt zu diesem sendet.</p>
<pre><code class="language-bird">if (bgp_path.len = 1) then
    bgp_local_pref = bgp_local_pref + 700;
</code></pre>
<p>Sollte die AS Pfad Länge eins sein, bedeutet dies, dass man direkt eine Route zum Ziel AS hat. Mit diesem Codeblock im Import-Filter kann man also erzwingen, dass diese direkte Route dann benutzt wird.</p>

<h4 id="warum-nicht-bgp_pathfirst--bgp_pathlast">Warum nicht <code class="language-plaintext highlighter-rouge">bgp_path.first = bgp_path.last</code>?</h4>

<p>Dies ist einer der Gründe, warum die lokale Präferenz so gefährlich sein kann. Sollte jemand seinen AS-Pfad absichtlich länger machen (Path Prepending), so möchte dieser jemand absichtlich erzielen, dass man eine andere Route wählt. Mit diesem Befehl würde man die Bemühungen wirkungslos machen.
Beispielsweise könnte man ein zwei Punkten mit einem AS gepeert sein. Nun ist eine Verbindung kurzweilig instabil und die andere weiterhin stabil. In der Zeit, wo die eine Verbindung instabil ist, soll die andere Verbindung bevorzugt werden - die instabile Verbindung soll allerdings nicht gekappt werden. Daher macht man mit Path Prepending die instabile Verbindung unattraktiver. Da die lokale Präferenz jedoch vor der AS Pfad-Länge überprüft wird, wird das Path Prepending ignoriert und die instabile Verbindung bevorzugt.</p>

<h4 id="man-kann-nicht-unendlich-weit-sehen">Man kann nicht unendlich weit sehen</h4>

<p>Ein anderer Grund, warum man lokale Präferenzen vermeiden sollte ist, dass man nur die Verbindung zwischen sich und dem benachbarten AS bewerten kann - beispielsweise wie stabil die Verbindung ist oder welche Latenz vorliegt.
Beispiel: Es gibt drei autonome Systeme, welche alle drei miteinander verbunden sind. Die Verbindung zwischen AS A und AS B ist sehr gut. Die Verbindung zwischen AS A und AS C ist zufriendstellend. Die Verbindung zwischen AS B und AS C ist jedoch sehr schlecht. AS A möchte nun AS C erreichen. Die sinnvolle Route wäre, den direkten Weg von AS A zu AS C zu benutzen. Dort wäre die Route zufriedenstellend. AS A hat jedoch für alle Routen von AS B eine hohe lokale Präferenz eingerichtet, da die Verbindung zwischen diesen ASes sehr gut ist. Nach dem Route Selection Algorithmus, wird also die Route über AS B verwendet - AS A -&gt; AS B -&gt; AS C. Da die Verbindung zwischen AS B und AS C jedoch sehr schlecht ist, ist jetzt auch die Verbindung zwischen AS A und AS C sehr schlecht - besser wäre gewesen, wenn AS A eine direkte Verbindung zu AS C aufgebaut hätte, wie es ohne lokale Präferenz gewesen wäre.
Dieses Beispiel zeigt, dass man immer nur die Verbindung zu den benachbarten ASes einschätzen kann und nicht deren Verbindung zu anderen Systemen.</p>

<p><img src="../../assets/img/bgp-route-selection/bgp-local-pref-is-evil.png" alt="Die Oben beschriebene Situation mit AS A, AS B und AS C als Diagramm dargestellt." /></p>

<h4 id="communities">Communities</h4>

<p>In BGP gibt es Communities. So werden in dn42 beispielsweise BGP Communities verwendet, um Bandbreite, Latenz und Verschlüsselung zu übermitteln. Die Implementierung dieser Communities ist jedoch optional. Des Weiteren können und sollen die Communities von jedem AS auf dem Weg bearbeitet werden - so ist zum Beispiel die höchste Bandbreite von einer Route immer die vom Knoten des Pfades mit der geringsten Bandbreite. Dies wirft zwei Probleme auf: Es kann sein, dass ASes die Communities nicht implementieren und daher wichtige Informationen verloren gehen. Des Weiteren kann es sein, dass die Communities aufgrund einer Fehlkonfiguration falsch bearbeitet werden.
Darüber hinaus, würde es sicherheitstechnisch schlecht sein, die lokale Präferenz anhand der Communities festzumachen. Dann könnte ein bösartiger Akteur mithilfe der Communities “behaupten”, er hätte eine sehr gute Verbindung zu allen Systemen. Dies würde dazu führen, dass die lokale Präferenz dafür sorgt, dass jede Route und damit auch der Traffic über den Angreifer geht.</p>

<h2 id="multiple-exit-discriminator-med">Multiple Exit Discriminator (MED)</h2>

<p>Es kann vorkommen, dass zwei autonome Systeme über zwei oder mehr Verbindungen peeren. Ohne MED wird die Route vom Router mit der niedrigsten Router ID ausgewählt. Dies ist kein sinnvolles Auswahlkriterium, jedoch ist es deterministisch (es tritt bei den gleichen Bedingungen immer dasselbe Ergebnis auf). Nun kann man jeder Route ein MED zuweisen. Dabei wird der <strong>kleinste</strong> MED bevorzugt.
Beispiel: Zwei autonome Systeme AS A und AS B sind über zwei Verbindungen miteinander gepeert. Die eine Verbindung ist stabil und die andere instabil. Man möchte die instabile Verbindung jedoch als Backup beibehalten, da sie alternativ Route nicht besser wäre. Wenn man nun die stabile Verbindung bevorzugen möchte, kann man dort eine geringere MED setzen. Dies macht man in bird, indem man <code class="language-plaintext highlighter-rouge">bgp_med</code> auf den entsprechenden Wert setzt.</p>

<p><img src="../../assets/img/bgp-route-selection/bgp-med-is-good.png" alt="Die Oben beschriebene Situation mit AS A und AS B als Diagramm dargestellt." /></p>

<h2 id="router-id">Router ID</h2>

<p>Als letztes Vergleichskriterium wird von vielen BGP Implementierungen, darunter auch bird, die Router ID benutzt. Die Router ID wird im Normalfall als IPv4 dargestellt und dient zur eindeutigen Identifizierung eines Routers unter seinen Nachbarn. Dieses Entscheidungskriterium ist zwar deterministisch, jedoch wenig sinnvoll. In bird kann man dieses letzte Entscheidungskriterium dahingehend ändern, dass die Route nicht nach der Router ID, sondern nach der <strong>ältesten</strong> Route ausgesucht wird.
Um dieses alternative Entscheidungskriterium zu benutzten, benutzt man das Statement <code class="language-plaintext highlighter-rouge">prefer older on;</code> beim Setup seiner BGP Session. Das Statement wird in <code class="language-plaintext highlighter-rouge">protocol bgp</code> geschrieben und gilt dann für die gesamte BGP Session.</p>

<h2 id="links">Links</h2>

<ul>
  <li><a href="http://docs.frrouting.org/en/latest/bgp.html#route-selection">FRR BGP Route Selection</a></li>
  <li><a href="https://bird.network.cz/?get_doc&amp;v=20&amp;f=bird-6.html#ss6.3">Bird BGP</a></li>
  <li><a href="https://bird.network.cz/?get_doc&amp;v=20&amp;f=bird-2.html#ss2.1">Bird Route Selection</a></li>
  <li><a href="https://dn42.dev/howto/Bird-communities">BGP communities in dn42</a></li>
</ul>]]>
        </content>
    </entry>
    
    <entry>
        <title>How are BGP routes selected and how can this be influenced?</title>
        
        <link rel="alternate" href="https://mk16.de/blog/bgp-route-selection-en/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/bgp-route-selection-en/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/bgp-route-selection-en/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/bgp-route-selection-en/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/bgp-route-selection-en/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/bgp-route-selection-en/" hreflang="en" />

        <id>/blog/bgp-route-selection-en/</id>
        <published>2023-03-05T00:00:00+01:00</published>
        <updated>2023-03-05T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>How are routes selected in BGP? How can you influence this selection? Here I explain the criteria as well as possibilities for manipulation like Path Prepending, BGP local preference and Multiple Exit Discriminator (MED).</summary>
        <content type="html">
            <![CDATA[<h2 id="how-are-bgp-routes-selected">How are BGP routes selected?</h2>

<p>The following are criteria in bird:</p>

<ol>
  <li>The route with the <strong>highest</strong> weight is preferred. For this there is the value <code class="language-plaintext highlighter-rouge">preference</code> in bird. This is used for example to prefer IGP over EGP - for example Babel over BGP.</li>
  <li>The route with the <strong>highest</strong> local preference is selected. This can be influenced in the import filter with the variable <code class="language-plaintext highlighter-rouge">bgp_local_pref</code>. The default preference is 100.</li>
  <li>The route with the <strong>shortest</strong> AS path is preferred. This is a decisive criterion in all implementations. The path basically specifies (exception: Path Prepending) how many autonomous systems must be traversed to reach the destination.</li>
  <li>Next, a very rarely used value is checked: the origin. Routes received via IGP are preferred. However, in bird <code class="language-plaintext highlighter-rouge">preference</code> is now used.</li>
  <li>If one has multiple peerings with the same autonomous system, the <strong>smallest</strong> multiple exit discriminator (MED) is preferred. For example, if one is connected once a dedicated line and once via an IXP, one generally wants to prefer the dedicated line since it does not have to be shared. Therefore routes, which were received over it, get a smaller MED.</li>
  <li>Routes received over eBGP are preferred over routes received over iBGP. This means, bird tends to use the routes to an external autonomous system instead of forwarding them further into its own autonomous system.</li>
  <li>Routes with the <strong>least</strong> internal distance to a boundary router are preferred. Thus, routes coming from routers that are externally connected to other autonomous systems are preferred.</li>
  <li>The route from the router with the <strong>lowest</strong> router ID is preferred.</li>
</ol>

<p>However, one should note that the criteria are implementation specific. For example, the criteria and order are a little bit different for FRR.</p>

<h2 id="how-to-influence-the-route-selection">How to influence the route selection?</h2>

<h3 id="path-prepending">Path Prepending</h3>

<p>Path prepending is a popular tactic to make a path less attractive. As you can see from the criteria above, AS path length is used as a criterion quite high up. The longer the AS path, the less attractive the route. Now, if you don’t want a route to be used preferentially, you can make the AS path longer and thus make the route less attractive.
In practice, you append your own AS number to the path. However, this also means that Path Prepending is recognizable, which is not a problem in well-configured systems. To perform path prepending in bird, there is the function <code class="language-plaintext highlighter-rouge">prepend</code>:</p>
<pre><code class="language-bird">bgp_path.prepend(64500);
</code></pre>
<p>In this code section, for example, we would prepend the AS number <code class="language-plaintext highlighter-rouge">64500</code> to the path. Here, the argument <em>does not</em> specify the number of prepends, but the AS number to be prepended. In other BGP implementations, however, the argument can also specify the number!
In practice one uses one to three prepends. After that, the route becomes so unattractive that it is almost never used.
Path prepending is therefore - if used correctly - a strong means to make a route globally less attractive. Path prepending goes beyond one’s own and neighboring autonomous systems.</p>

<h3 id="bgp-local-preference">BGP local preference</h3>

<p>Local preference is a very powerful tool - and could almost be called brute force. The AS path length or other criteria are no longer checked if a single route to the destination has a high preference. Local preference should therefore be used carefully.
A good use of it would be to ensure that routes from a neighboring AS are sent directly to it.</p>
<pre><code class="language-bird">if (bgp_path.len = 1) then
    bgp_local_pref = bgp_local_pref + 700;
</code></pre>
<p>If the AS path length is one, it means that one have a direct route to the destination AS. So with this code block in the import filter one can force that this direct route is used then.</p>

<h4 id="why-not-bgp_pathfirst--bgp_pathlast">Why not <code class="language-plaintext highlighter-rouge">bgp_path.first = bgp_path.last</code>?</h4>

<p>This is one of the reasons why local preference can be so dangerous. If someone intentionally makes they AS path longer (path prepending), this someone intentionally wants to achieve that you choose another route. With this command you would make the effort ineffective.
For example, one could be peered a two points with an AS. Now one connection is temporarily unstable and the other continues to be stable. During the time when one connection is unstable, the other connection should be preferred - but the unstable connection should not be cut. Therefore, Path Prepending makes the unstable connection less attractive. However, since the local preference is checked before the AS path length, Path Prepending is ignored and the unstable connection is preferred.</p>

<h4 id="one-cannot-see-infinitely-far">One cannot see infinitely far</h4>

<p>Another reason to avoid local preferences is that you can only evaluate the connection between yourself and the neighboring AS - for example, how stable the connection is or what the latency is.
Example: there are three autonomous systems, all three are interconnected to each other. The connection between AS A and AS B is very good. The connection between AS A and AS C is okay. However, the connection between AS B and AS C is very poor. AS A now wants to reach AS C. The reasonable route would be to use the direct route from AS A to AS C. The the connection there would be okay. However, AS A has set up a high local preference for all routes from AS B because the connection between these ASes is very good. So according to the route selection algorithm, the route via AS B is used - AS A -&gt; AS B -&gt; AS C. However, since the connection between AS B and AS C is very bad, now the connection between AS A and AS C is also very bad - it would have been better if AS A had established a direct connection to AS C, as it would have been without local preference.
This example shows that one can always only estimate the connection to the neighboring ASes and not their connection to other systems.</p>

<p><img src="../../assets/img/bgp-route-selection/bgp-local-pref-is-evil.png" alt="The situation described above with AS A, AS B and AS C shown as a diagram." /></p>

<h4 id="communities">Communities</h4>

<p>In BGP, there are communities. For example, in dn42, BGP communities are used to communicate bandwidth, latency, and encryption. However, the implementation of these communities is optional. Furthermore, the communities can and should be adjusted by any AS on the path - for example, the highest bandwidth from a route is always that from the node on the path with the lowest bandwidth. This raises two problems: ASes may not implement the communities and therefore important information may be lost. Furthermore, the communities may be processed incorrectly due to misconfiguration.
In addition, basing local preference on the communities would be a bad security practice. Then, a malicious actor could use the communities to “claim” to have a very good connection to all systems. This would result in the local preference ensuring that every route, and thus traffic, goes through the attacker.</p>

<h2 id="multiple-exit-discriminator-med">Multiple Exit Discriminator (MED)</h2>

<p>It may happen that two autonomous systems peer over two or more connections. Without MED, the route is selected by the router with the lowest router ID. This is not a useful selection criterion, but it is deterministic (the same result always occurs for the same conditions). Now you can assign a MED to each route. In this case, the <strong>smallest</strong> MED is preferred.
Example: Two autonomous systems AS A and AS B are peered together by two connections. One connection is stable and the other is unstable. However, one would like to keep the unstable connection as a backup since it would not be better alternate route. If you want to prefer the stable connection, you can set a lower MED there. This is done in bird by setting <code class="language-plaintext highlighter-rouge">bgp_med</code> to the appropriate value.</p>

<p><img src="../../assets/img/bgp-route-selection/bgp-med-is-good.png" alt="The situation described above with AS A and AS B shown as a diagram." /></p>

<h2 id="router-id">Router ID</h2>

<p>As a final comparison criterion, the router ID is used by many BGP implementations, including bird. The router ID is normally represented as IPv4 and is used to uniquely identify a router among its neighbors. This decision criterion is deterministic, but not very useful. In bird, one can modify this last decision criterion to select the route based on the <strong>oldest</strong> route rather than the router ID.
To use this alternative decision criterion, use the statement <code class="language-plaintext highlighter-rouge">prefer older on;</code> when setting up your BGP session. The statement is written to <code class="language-plaintext highlighter-rouge">protocol bgp</code> and then applies to the entire BGP session.</p>

<h2 id="links">Links</h2>

<ul>
  <li><a href="http://docs.frrouting.org/en/latest/bgp.html#route-selection">FRR BGP Route Selection</a></li>
  <li><a href="https://bird.network.cz/?get_doc&amp;v=20&amp;f=bird-6.html#ss6.3">Bird BGP</a></li>
  <li><a href="https://bird.network.cz/?get_doc&amp;v=20&amp;f=bird-2.html#ss2.1">Bird Route Selection</a></li>
  <li><a href="https://dn42.dev/howto/Bird-communities">BGP communities in dn42</a></li>
</ul>]]>
        </content>
    </entry>
    
    <entry>
        <title>Das Präferenz-Attribut in bird</title>
        
        <link rel="alternate" href="https://mk16.de/blog/bird-praeferenz/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/bird-praeferenz/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/bird-praeferenz/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/bird-praeferenz/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/bird-praeferenz/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/bird-praeferenz/" hreflang="de" />

        <id>/blog/bird-praeferenz/</id>
        <published>2023-06-14T01:00:00+02:00</published>
        <updated>2023-06-14T01:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Dieser Blogeintrag handelt um das `preference`-Attribut im bird Routing Dämon. Was ist dieses? Welchen Standardwert hat es? Wie verändert man den Standardwert?</summary>
        <content type="html">
            <![CDATA[<p>Im bird Routing Dämon gibt es ein verstecktes Attribut: das Präferenz-Attribut <code class="language-plaintext highlighter-rouge">preference</code>. “Versteckt”, da es recht schlecht dokumentiert ist. In der Dokumentation wird es zwar erwähnt, jedoch wird weder ausführlich auf die Verwendung eingegangen noch, was die Standardwerte dieses Attributes sind. Je nachdem wie gut man sich die Dokumentation durchgelesen hat, kann das Auslesen dieses Attributs auch etwas versteckt sein.</p>

<h2 id="was-ist-das-präferenz-attribut-und-wofür-wird-es-verwendet">Was ist das Präferenz-Attribut und wofür wird es verwendet?</h2>

<p>Das Präferenz-Attribut wird zur Routen-Auswahl verwendet. Es wird noch vor den Protokollspezifischen-Kriterien (beispielsweise Metrik bei Babel, AS-Pfad Länge und BGP Local Pref bei BGP) ausgewertet. Die Route mit der höchsten Präferenz wird bevorzugt. Wenn es zwei Routen mit der gleichen Präferenz gibt, wird entsprechend das protokollspezifische Auswahlverfahren verwendet.</p>

<h2 id="was-ist-der-standardwert">Was ist der Standardwert?</h2>

<p>Um nun Routen mit dieser Präferenzen manipulieren zu können, ist es sinnvoll deren Standardwerte zu kennen, jedoch steht dazu aktuell nichts in der Dokumentation. Im Quellcode kann man die Präferenzen jedoch schnell finden. Diese befindet sich in <a href="https://gitlab.nic.cz/labs/bird/-/blob/v2.0.12/nest/route.h#L748-755">Zeile 748 bis Zeile 755 in der Datei nest/route.h</a>.</p>

<p>Folgendes sind die Standardwerte:</p>

<table>
  <thead>
    <tr>
      <th>Protokoll</th>
      <th>Präferenz</th>
      <th>Hinweis</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Direct</td>
      <td>240</td>
      <td>Routen, von Interfaces, welche direkt verbunden sind</td>
    </tr>
    <tr>
      <td>Static</td>
      <td>200</td>
      <td>Statische konfigurierte Routen</td>
    </tr>
    <tr>
      <td>OSPF</td>
      <td>150</td>
      <td>IGP; für OSPF intra-area, inter-area und Type 1 externe Routen</td>
    </tr>
    <tr>
      <td>Babel</td>
      <td>130</td>
      <td>IGP</td>
    </tr>
    <tr>
      <td>RIP</td>
      <td>120</td>
      <td>IGP</td>
    </tr>
    <tr>
      <td>BGP</td>
      <td>100</td>
      <td>EGP</td>
    </tr>
    <tr>
      <td>RPKI</td>
      <td>100</td>
      <td>-</td>
    </tr>
    <tr>
      <td>“Ererbt”</td>
      <td>10</td>
      <td>Routen, welche von anderen Routing Dämonen vererbt worden sind</td>
    </tr>
  </tbody>
</table>

<h2 id="wie-kann-man-es-auslesen">Wie kann man es auslesen?</h2>

<p>Sofern man das Präferenz-Attribut nicht im Import-Filter bearbeitet hat, kann man dies in der detaillierten Protokollansicht sehen:</p>
<pre><code class="language-birdc">$ birdc show protocol all kioubit
BIRD 2.0.12 ready.
Name       Proto      Table      State  Since         Info
kioubit    BGP        ---        up     2023-06-11    Established   
  BGP state:          Established
[...]
    Local capabilities
      Multiprotocol
        AF announced: ipv6
[...]
    Neighbor capabilities
      Multiprotocol
        AF announced: ipv4 ipv6
[...]
  Channel ipv6
    State:          UP
    Table:          dn42
    Preference:     100
    Input filter:   (unnamed)
    Output filter:  (unnamed)
[...]
</code></pre>
<p>Mit der Zeile <code class="language-plaintext highlighter-rouge">Preference: 100</code> sieht man, dass alle importierten IPv6 Routen aus diesem Protokoll (wenn nicht im Filter manipuliert) eine Präferenz von 100 bekommen.</p>

<p>Sollte man die Präferenz nicht für ein Protokoll, sondern für eine bestimmte Route auslesen wollen, kann man dies ganz einfach tun, in dem man den Wert in Runden Klammern ausliest:</p>
<pre><code class="language-birdc">$ birdc show route protocol kioubit | head
BIRD 2.0.12 ready.
Table dn42:
fda7:3ae7:e04d::/48  unicast [kioubit 2023-06-11] (100) [AS4242422596i]
    via fe80::ade0 on kioubit
fd23:0:0:f00d::/64   unicast [kioubit 2023-06-11] (100) [AS4242422037i]
    via fe80::ade0 on kioubit
fd66:dca7:46f3::/48  unicast [kioubit 01:32:34.600] (100) [AS4242423772i]
    via fe80::ade0 on kioubit
fd5b:d2c1:2ecc::/48  unicast [kioubit 2023-06-11] (100) [AS4242421832i]
    via fe80::ade0 on kioubit
</code></pre>
<p>Hier sieht man beispielsweise, dass alle vier Routen eine Präferenz von 100 haben.</p>

<h2 id="wann-sollte-man-das-präferenz-attribut-verändern">Wann sollte man das Präferenz-Attribut verändern?</h2>

<p>Die eigentlichen Präferenz-Attribute sind recht gut gewählt, wenn man jedoch Spezialfälle hat wie zum Beispiel das Mischen der Protokolle, kann es sinnvoll sein, die Präferenz zu verändern - möchte man beispielsweise mit einigen Personen BGP und mit anderen Babel als EGP sprechen und hat auch ein Babel für IGP selber, sollen die EGP Babel Routen natürlich die gleiche Präferenz haben wie die BGP Routen und nur die IGP Babel Routen sollten bevorzugt werden.</p>

<h2 id="wie-verändert-man-das-präferenz-attribut">Wie verändert man das Präferenz-Attribut?</h2>

<p>Dies ist recht einfach, indem man die Präferenz im Import-Filter entsprechend setzt:</p>
<pre><code class="language-bird">filter egp_babel_import {
    preference = 100;
}
</code></pre>
<p>Dieser Code orientiert sich am obigen Beispiel.</p>

<p>Dazu sollte man sagen, dass man Babel und BGP nicht einfach als EGP benutzen und mischen sollte, ohne sich vorab entsprechend zu informieren. Dies ist allerdings ein reales Beispiel aus der <a href="/blog/die-crxn-dn42-interconnection-ist-up/">CRXN/dn42 Interconnection</a>.</p>

<h2 id="weiteres">Weiteres</h2>

<p>Da auch für mich das Thema etwas undurchsichtig ist, habe ich den Blog Eintrag nach neuen Informationen aus der Mailingliste noch einmal neugeschrieben. Die Nachfrage an der Mailingliste kann man im <a href="https://bird.network.cz/pipermail/bird-users/2023-June/016988.html">Archiv</a> sehen.</p>]]>
        </content>
    </entry>
    
    <entry>
        <title>The preference attribute in bird</title>
        
        <link rel="alternate" href="https://mk16.de/blog/bird-preference/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/bird-preference/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/bird-preference/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/bird-preference/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/bird-preference/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/bird-preference/" hreflang="en" />

        <id>/blog/bird-preference/</id>
        <published>2023-06-14T01:00:00+02:00</published>
        <updated>2023-06-14T01:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>This blog entry is about the `preference` attribute in the bird routing daemon. What is it? What is its default value? How to change the default value?</summary>
        <content type="html">
            <![CDATA[<p>In the bird routing daemon there is a hidden attribute: the preference attribute <code class="language-plaintext highlighter-rouge">preference</code>. “Hidden” because it is quite poorly documented. The documentation mentions it, but does not go into detail about how to use it, nor what the default values of this attribute are. Depending on how well you read through the documentation, reading this attribute can also be somewhat hidden.</p>

<h2 id="what-is-the-preference-attribute-and-what-is-it-used-for">What is the preference attribute and what is it used for?</h2>

<p>The preference attribute is used for route selection. It is evaluated before the protocol-specific criteria (for example, metric for Babel, AS path length, and BGP Local Pref for BGP). The route with the highest preference is preferred. If there are two routes with the same preference, the protocol-specific selection procedure is used accordingly.</p>

<h2 id="what-is-the-default-value">What is the default value?</h2>

<p>In order to be able to manipulate routes with these preferences, it is useful to know their default values, but there is currently nothing about this in the documentation. However, you can quickly find the preferences in the source code. It can be found in <a href="https://gitlab.nic.cz/labs/bird/-/blob/v2.0.12/nest/route.h#L748-755">line 748 to line 755 in the file nest/route.h</a>.</p>

<p>The following are the default values:</p>

<table>
  <thead>
    <tr>
      <th>Protocol</th>
      <th>Preference</th>
      <th>Note</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Direct</td>
      <td>240</td>
      <td>Routes, from interfaces that are directly connected</td>
    </tr>
    <tr>
      <td>Static</td>
      <td>200</td>
      <td>Static configured routes</td>
    </tr>
    <tr>
      <td>OSPF</td>
      <td>150</td>
      <td>IGP; for OSPF intra-area, inter-area and type 1 external routes</td>
    </tr>
    <tr>
      <td>Babel</td>
      <td>130</td>
      <td>IGP</td>
    </tr>
    <tr>
      <td>RIP</td>
      <td>120</td>
      <td>IGP</td>
    </tr>
    <tr>
      <td>BGP</td>
      <td>100</td>
      <td>EGP</td>
    </tr>
    <tr>
      <td>RPKI</td>
      <td>100</td>
      <td> </td>
    </tr>
    <tr>
      <td>“Inherited”</td>
      <td>10</td>
      <td>Routes inherited from other routing daemons</td>
    </tr>
  </tbody>
</table>

<h2 id="how-to-read-it-out">How to read it out?</h2>

<p>Unless you have edited the preference attribute in the import filter, you can see it in the detailed log view:</p>
<pre><code class="language-birdc">$ birdc show protocol all kioubit
BIRD 2.0.12 ready.
Name       Proto      Table      State  Since         Info
kioubit    BGP        ---        up     2023-06-11    Established   
  BGP state:          Established
[...]
    Local capabilities
      Multiprotocol
        AF announced: ipv6
[...]
    Neighbor capabilities
      Multiprotocol
        AF announced: ipv4 ipv6
[...]
  Channel ipv6
    State:          UP
    Table:          dn42
    Preference:     100
    Input filter:   (unnamed)
    Output filter:  (unnamed)
[...]
</code></pre>
<p>With the line <code class="language-plaintext highlighter-rouge">Preference: 100</code> you can see that all imported IPv6 routes from this protocol (if not manipulated in the filter) get a preference of 100.</p>

<p>If you want to read the preference not for a protocol but for a specific route, you can easily do this by reading the value in round brackets:</p>
<pre><code class="language-birdc">$ birdc show route protocol kioubit | head
BIRD 2.0.12 ready.
Table dn42:
fda7:3ae7:e04d::/48  unicast [kioubit 2023-06-11] (100) [AS4242422596i]
    via fe80::ade0 on kioubit
fd23:0:0:f00d::/64   unicast [kioubit 2023-06-11] (100) [AS4242422037i]
    via fe80::ade0 on kioubit
fd66:dca7:46f3::/48  unicast [kioubit 01:32:34.600] (100) [AS4242423772i]
    via fe80::ade0 on kioubit
fd5b:d2c1:2ecc::/48  unicast [kioubit 2023-06-11] (100) [AS4242421832i]
    via fe80::ade0 on kioubit
</code></pre>
<p>For example, here you can see that all four routes have a preference of 100.</p>

<h2 id="when-should-you-change-the-preference-attribute">When should you change the preference attribute?</h2>

<p>The actual preference attributes are quite well chosen, but if you have special cases like mixing the protocols, it can make sense to change the preference - for example, if you want to talk BGP to some people and Babel to others as EGP and also have a Babel for IGP itself, the EGP Babel routes should of course have the same preference as the BGP routes and only the IGP Babel routes should be preferred.</p>

<h2 id="how-to-change-the-preference-attribute">How to change the preference attribute?</h2>

<p>This is quite simple by setting the preference in the import filter accordingly:</p>
<pre><code class="language-bird">filter egp_babel_import {
    preference = 100;
}
</code></pre>
<p>This code is based on the example above.</p>

<p>It should be said that one should not simply use Babel and BGP as EGP and mix them without informing oneself accordingly in advance. However, this is a real example from the <a href="/blog/the-crxn-dn42-interconnection-is-up/">CRXN/dn42 interconnection</a>.</p>

<h2 id="further">Further</h2>

<p>Since the topic is a bit obscure for me as well, I have rewritten the blog entry after new information from the mailing list. You can see the postings on the mailing list in the <a href="https://bird.network.cz/pipermail/bird-users/2023-June/016988.html">archive</a>.</p>]]>
        </content>
    </entry>
    
    <entry>
        <title>Comparison of self-meshing VPNs</title>
        
        <link rel="alternate" href="https://mk16.de/blog/comparison-of-self-meshing-vpns/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/comparison-of-self-meshing-vpns/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/comparison-of-self-meshing-vpns/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/comparison-of-self-meshing-vpns/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/comparison-of-self-meshing-vpns/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/comparison-of-self-meshing-vpns/" hreflang="en" />

        <id>/blog/comparison-of-self-meshing-vpns/</id>
        <published>2023-12-13T00:00:00+01:00</published>
        <updated>2023-12-13T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>In this blog post, I&apos;ll review various self-meshing VPN solutions such as Tinc, n2n, or Zerotier and take a look at which ones are ready for use on a Virtual Internet Exchange Point (VIXP).</summary>
        <content type="html">
            <![CDATA[<p>SERNET has been operating <a href="https://blog.sherpherd.top/ix.html">SERNET-IX</a> for some time now. This provides a new type of virtual internet exchange point (VIXP). Instead of all participants connecting to a centralized server, modern technology makes it possible for participants to connect to each other automatically. This creates a large Ethernet switch. The operator of the VIXP then only has to provide the route server (RS) and the coordinator for this technology.</p>

<p>There are various modern solutions for self-meshing networks - unfortunately not all of them are usable (according to my criteria). I have therefore looked at some of these technologies and compared them with each other.</p>

<table>
  <thead>
    <tr>
      <th>Criteria</th>
      <th>Tinc (There Is No Cabal)</th>
      <th>n2n</th>
      <th>Tailscale/Headscale</th>
      <th>Zerotier</th>
      <th>Nebula</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>License</td>
      <td>GPL-2.0</td>
      <td>GPL-3.0</td>
      <td>BSD</td>
      <td>BSL (proprietary)</td>
      <td>MIT</td>
    </tr>
    <tr>
      <td>Free software</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>No</td>
      <td>Partly</td>
    </tr>
    <tr>
      <td>Android support</td>
      <td>Yes</td>
      <td>Yes (<a href="https://github.com/switch-iot/hin2n/issues/82">uses proprietary component</a>)</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes (source-available, <a href="https://github.com/DefinedNet/mobile_nebula/issues/44">no free software</a>)</td>
    </tr>
    <tr>
      <td>- In F-Droid</td>
      <td>Yes</td>
      <td>No</td>
      <td>Yes</td>
      <td>No</td>
      <td>No</td>
    </tr>
    <tr>
      <td>- In IzzyOnDroid</td>
      <td>No</td>
      <td>No</td>
      <td>No</td>
      <td>Yes</td>
      <td>No</td>
    </tr>
    <tr>
      <td>Windows support</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Linux support</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>- In Arch Linux</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>- In Debian packages</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>No</td>
      <td>No</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>- In Alpine Linux</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>No</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Centralized server</td>
      <td>No</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Federated server</td>
      <td>-</td>
      <td>Yes</td>
      <td>No</td>
      <td>No</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Self hosting of the server</td>
      <td>-</td>
      <td>Yes</td>
      <td>with headscale</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Dezentalized</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>No</td>
      <td>No</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Authentication and encryption methods</td>
      <td>RSA/Blowfish/SHA-1 (<a href="https://tinc-vpn.org/documentation-1.1/Legacy-authentication-protocol.html">Legacy</a>), TLS1.2-Like Ed25519/AES-256-CTR/SHA-256 (<a href="https://tinc-vpn.org/documentation-1.1/Simple-Peer_002dto_002dPeer-Security.html">SPTPS</a>)</td>
      <td><a href="https://github.com/ntop/n2n/blob/dev/doc/Crypto.md">Various</a>, including AES-256-CTS or ChaCha20</td>
      <td>WireGuard</td>
      <td><a href="https://docs.zerotier.com/protocol/#cryptography">Curve25519/Ed25519, Salsa20/Poly1305</a></td>
      <td><a href="https://github.com/slackhq/nebula#curve-p256-and-boringcrypto">Curve25519</a>, <a href="https://github.com/slackhq/nebula#technical-overview">AES-256-GCM</a></td>
    </tr>
    <tr>
      <td>- perfect forward secrecy (PFS)</td>
      <td>Yes (SPTPS)</td>
      <td><a href="https://discord.com/channels/726054161547919440/744490128260726865/1184496039118516285">?</a></td>
      <td>Yes</td>
      <td>No<a href="https://github.com/zerotier/ZeroTierOne/issues/204#issuecomment-1853964308">?</a></td>
      <td><a href="https://github.com/slackhq/nebula/discussions/1041">?</a></td>
    </tr>
    <tr>
      <td>Tunnel</td>
      <td>Layer 2/3</td>
      <td>Layer 2</td>
      <td>Layer 3</td>
      <td>Layer 2</td>
      <td>Layer 3</td>
    </tr>
    <tr>
      <td>Tunnel over</td>
      <td>TCP/UDP</td>
      <td>TCP/UDP (IPv4-only)</td>
      <td>UDP</td>
      <td>UDP</td>
      <td>UDP</td>
    </tr>
    <tr>
      <td>Full IPv6-Support</td>
      <td>Yes</td>
      <td>No</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Full IPv4-Support</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
  </tbody>
</table>

<p>Since a table with a lot of entries quickly becomes ugly, here are a few remarks:</p>
<ul>
  <li>Tinc fulfills many criteria, but is no longer <a href="https://github.com/gsliepen/tinc/issues/443">being actively developed</a>. The main developer still processes pull requests, but is no longer actively working on the code itself due to time constraints. Issues are therefore only sporadically processed or fixed.</li>
  <li>Tinc version 1.0 uses a cryptography system that is not modern. In the new pre-release version, Tinc uses the specially developed <a href="https://tinc-vpn.org/documentation-1.1/Simple-Peer_002dto_002dPeer-Security.html">SPTPS</a> protocol, which even implements PFS. The development of the pre-release version Tinc 1.1 has been going on for a very long time. Tinc 1.1 is in almost no official package sources. It can be obtained from <a href="https://pkgs.alpinelinux.org/packages?name=tinc-pre&amp;branch=edge&amp;repo=&amp;arch=&amp;maintainer=">Alpine Linux via the community repository</a> or from <a href="https://aur.archlinux.org/packages/tinc-pre-git">Arch Linux via AUR</a>.</li>
  <li>n2n can <a href="https://github.com/ntop/n2n/issues/857">only transport data via IPv4</a>. This makes it impossible for IPv6-only nodes to participate in an n2n network.</li>
  <li>n2n also has an Android app. This is itself under a free license, but <a href="https://github.com/switch-iot/hin2n/issues/82">includes many non-free, privacy-unfriendly components</a>.</li>
  <li>The Tailscale Client itself is free software. However, the actual Tailscale server is proprietary. However, an alternative server implementation is available under the name <a href="https://github.com/juanfont/headscale">headscale</a>.</li>
  <li>Tailscale also has an Android app, which is even available in <a href="https://f-droid.org/en/packages/com.tailscale.ipn/">F-Droid</a>. Unfortunately, the app has an anti-feature - it sends debug logs to the developers without consent.</li>
  <li>Zerotier was available for some time under the GPL 3 license. However, the developers have changed to the proprietary BSL license. It allows the use of Zerotier for non-commercial or non-profit purposes.</li>
  <li>Nebula is unfortunately only partially free software. The Linux client is FLOSS and even in the Debian package sources. However, the Android app, which is produced by the same developers, <a href="https://github.com/DefinedNet/mobile_nebula/issues/44">is proprietary</a>.</li>
</ul>

<p>My criteria are actually quite simple:</p>
<ul>
  <li>I want full IPv6 support. This is particularly necessary in networks like the dn42, where there are also IPv6-only networks.</li>
  <li>I want full IPv4 support. Although IPv4 is a legacy protocol, it is still used by many - including me - in the dn42.</li>
  <li>The VPN solution should use a fairly secure encryption. This can also include PFS. PFS means that if the long-term key for the connection is compromised, not all old connections can be subsequently decrypted.</li>
  <li>The software should be completely FLOSS. For me, this means preferably under a license that is recognized by both the FSF and the OSI. There are various reasons for this. In this context, it means above all that you can compile it yourself, the security can be audited independently and simple packaging for operating systems is possible.</li>
  <li>I mainly use Debian on my servers. Therefore, the software must be available for Debian. Since I don’t just want to determine the freedom of the software myself, the software must also be in the official package sources.</li>
  <li>An Android app could be added as a bonus. However, this is not necessary, especially in the context of a VIXP, but would be more of a gimmick.</li>
</ul>

<table>
  <thead>
    <tr>
      <th>Criteria</th>
      <th>Tinc</th>
      <th>n2n</th>
      <th>Tailscale/Headscale</th>
      <th>Zerotier</th>
      <th>Nebula</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Full IPv6-Support?</td>
      <td>Yes</td>
      <td>No</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Full IPv4-Support?</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Uses modern cryptography?</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>PFS?</td>
      <td>Yes</td>
      <td>?</td>
      <td>Yes</td>
      <td>?</td>
      <td>?</td>
    </tr>
    <tr>
      <td>Is Free Software?</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>No</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Is Fully FLOSS?</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>No</td>
      <td>No</td>
      <td>No</td>
    </tr>
    <tr>
      <td>In Debian?</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>No</td>
      <td>No</td>
      <td>Yes</td>
    </tr>
  </tbody>
</table>

<p>In conclusion, it can be said that only Tinc in the pre-release version fully meets my criteria.</p>]]>
        </content>
    </entry>
    
    <entry>
        <title>Contiki NG, Linux WPAN und 6LoWPAN</title>
        
        <link rel="alternate" href="https://mk16.de/blog/contiki-ng-linux-wpan-6lowpan-de/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/contiki-ng-linux-wpan-6lowpan-de/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/contiki-ng-linux-wpan-6lowpan-de/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/contiki-ng-linux-wpan-6lowpan-de/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/contiki-ng-linux-wpan-6lowpan-de/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/contiki-ng-linux-wpan-6lowpan-de/" hreflang="de" />

        <id>/blog/contiki-ng-linux-wpan-6lowpan-de/</id>
        <published>2023-01-09T00:00:00+01:00</published>
        <updated>2023-01-09T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Können Contiki NG und Linux über 6LoWPAN / IEEE 802.15.4 kommunizieren?</summary>
        <content type="html">
            <![CDATA[Neulich bin ich in Wikipedia auf eine Liste von Betriebssystemen gestoßen und dort habe ich Contiki NG wiederentdeckt. Ich hatte schon früher gesehen, dass Contiki NG ein Betriebssystem für kleine Embedded Systeme ist, jedoch hatte ich nie Hardware, mit welcher ich es ausprobieren konnte. Contiki NG hat eine sehr schöne [Dokumentation](https://docs.contiki-ng.org/en/develop/) und in dieser habe ich gesehen, dass es [6LoWPAN](https://en.wikipedia.org/wiki/6LoWPAN) unterstützt - ein IPv6-only low-rate low-power 2.4 GHz Netzwerk-Standard - als IPv6-Fan hat sich dies natürlich super angehört. 6LoWPAN wurde eigentlich für IoT-Geräte entwickelt, aber wer sagt, dass ich es nicht zum Spaß ausprobieren kann?

## Hardware

Danach habe ich geschaut, auf welcher Hardware Contiki NG funktioniert und bin dabei auf NRF-Chips gestoßen, welche ich schon von Meshtastic kannte. Des Weiteren waren sie die einzigen günstigen, einfach Verfügbaren. Ich habe mich für den recht kompakten [nRF52840 MDK USB Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/) entschieden, welchen es Online sogar mit Hülle gab.

![Man sieht eine Art USB-Stick auf einer Holzoberfläche liegen. Es ist ein "M" als Logo draufgedruckt. Des Weiteren ist eine kleine Ausstanzung für einen Schlüsselanhänger zu sehen. Darüberhinaus sieht man einen kleinen Button sowie eine LED (bzw. das es einen grauen Punkt gibt, welcher leuchten kann). Der USB-Dongle ist weiß.](/assets/img/contiki-ng-linux-wpan-6lowpan/nrf52840-mdk-usb-dongle.jpg)

Des Weiteren habe ich natürlich auch geschaut, ob Linux 6LoWPAN unterstützt und siehe da - es ist sogar im Mainline Kernel. Die Linux Implementierung wird vom [Linux WPAN Projekt](https://linux-wpan.org/) angetrieben. Alles, was Linux braucht, ist eine unterstützte Hardware. Auf der Website des Projektes gibt es eine Liste von diesen mit Links zu Online Shops (viele davon führen zu 404-Seiten). Ich habe als Nächstes geschaut, welches es davon in USB-Format und möglichst günstig gibt. Ich habe mich daraufhin für ATUSB-Dongle entschieden - leider gab es dazu keine Hülle.

![Man sieht eine Platine. An der rechts Seite ist ein USB-Anschluss. An der linken Seite kann man eine eingebaute Antenne in der Platine sehen. Auf der Platine sieht man zwei markante schwarze Chips. Die Platine selber ist grün.](/assets/img/contiki-ng-linux-wpan-6lowpan/atusb-dongle.jpg)

## Linux WPAN

Als Erstes ist er ATUSB-Dongle bei mir angekommen. Es erscheint automatisch ein `wpan0`-Interface mit einem MTU von 123 (und damit nicht IPv6-fähig). Auf der Website ist eine Anleitung, wie man davon ein `lowpan0`-Interface herleiten kann, welches mit einer MTU von 1280 auch IPv6 unterstützt. Um die Pakete trotzdem möglichst kleinzuhalten, wird eine Header-Komprimierung verwendet. Darüber hinaus werden, wenn nötig, die Pakete auf MAC-Ebene fragmentiert. Um also eine möglichst gute Performance zu erhalten, ist es dennoch empfohlen, die Pakete möglichst kleinzuhalten.
Folgendes sind die Befehle, welche ich zum Konfigurieren verwendet habe:
```
sudo ip link set dev wpan0 down
sudo iwpan dev wpan0 set pan_id 0xbada
sudo iwpan phy phy0 set channel 0 26
sudo ip link set dev wpan0 up
sudo ip link add link wpan0 name lowpan0 type lowpan
sudo ip link set dev lowpan0 up
```
Dabei ist `0xbada` die PAN ID (vergleichbar mit einer SSID beim WLAN) und `0 26` der Channel (Standard Channel bei Contiki NG).

## Firmware

Ein Tag nach dem ATUSB-Dongle kam auch der nRF52840 MDK USB Dongle an. Dieser wird mit dem UF2-Bootloader ausgeliefert, welchen ich auch schon von Meshtastic kannte. Dieser ist recht einfach zu bedienen und schützt (zumindest teilweise) vor einem Briek des Gerätes. Um eine Firmware zu flashen, schaltet man mithilfe des Button das Gerät in den Bootloader Modus und mountet es als Dateisystem. Danach kann man einfach die Firmware (ausgeliefert als .uf2-Datei) auf das Gerät kopieren und die Firmware wird automatisch installiert.

Ich habe also wie in der Dokumentation beschrieben versucht Contiki NG zu kompilieren - jedoc ohne Erfolg: Meine GCC Version war zu neu. Contiki NG benötigt aktuell eine GCC Version von 10 (die aktuelle ist 13). Dazu gibt es auch bereits ein [Issue](https://github.com/contiki-ng/contiki-ng/issues/2823). Glücklicherweise konnte man sich die GCC Version 10 installieren. Nachdem ich das Programm mit GCC 10 kompiliert hatte, erhielt ich eine .nrf-Datei. Die Makefile von Contiki NG unterstützten eigentlich auch das Hochladen der Firmware über USB, jedoch wird kein UF2-Bootloader, sondern das Gerät als serielle Schnittstelle erwartet. Da dies bei mir nicht der Fall war, musste ich die .nrf-Datei in eine .uf2-Datei umwandeln. Beim Versuch des Hochladens ist mir aufgefallen, dass Contiki NG im Build-Ordner eine .hex-Datei erzeugt. Diese lässt sich wiederum problemlos mit einem [Python-Tool](https://github.com/makerdiary/uf2utils) in eine .uf2-Datei umwandeln. Ich habe zum Erzeugen der .uf2-Datei also folgende Befehle ausgeführt:
```
make TARGET=nrf BOARD=nrf52840/dongle node.upload
uf2conv build/nrf/nrf52840/dongle/node.hex --family 0xADA52840 --convert --output node.uf2
```
Mein Programm habe ich dabei `node` genannt.

### Quelltext

Contiki-NG liefert einige Beispiele mit. Ich wollte jedoch einfach nur ein Programm, mit welchem ich andere Knoten anpingen kann - ich habe aus den Beispielen also Folgendes verwendet:
`node.c`:
```c
#include "contiki.h"
#include "sys/log.h"

#define LOG_MODULE "Contiki-NG node"
#define LOG_LEVEL LOG_LEVEL_INFO

#include <stdbool.h>

/*---------------------------------------------------------------------------*/
PROCESS(node_process, "Contiki-NG node");
AUTOSTART_PROCESSES(&node_process);
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(node_process, ev, data)
{
    PROCESS_BEGIN();

    LOG_INFO("Contiki-NG node Started\n");

    while (true) {
        PROCESS_WAIT_EVENT();
    }

    PROCESS_END();
}
/*---------------------------------------------------------------------------*/
```

Dieses Programm startet lediglich das Betriebssystem und gibt einmallig beim Start `Contiki-NG node Started` aus.

`project-conf.h`:
```c
#ifndef PROJECT_CONF_H
#define PROJECT_CONF_H

#define IEEE802154_CONF_PANID 0xbada

#define UIP_CONF_ND6_SEND_RA 0

#endif
```
In dieser Datei werden die Projekteinstellungen vorgenommen. In diesem Fall wollte ich, dass Contiki NG weder als radv-Client noch als radv-Server agiert und setze ich PANID entsprechend.

`Makefile`:
```makefile
CONTIKI_PROJECT = node
all: $(CONTIKI_PROJECT)
CONTIKI=../..

include $(CONTIKI)/Makefile.dir-variables

MAKE_MAC = MAKE_MAC_CSMA
MAKE_ROUTING = MAKE_ROUTING_NULLROUTING

MODULES += $(CONTIKI_NG_SERVICES_DIR)/shell

include $(CONTIKI)/Makefile.include
```
In Contiki NG spielen Makefiles tatsächlich eine größere Rolle, da in ihnen auch verschiedene Projekteinstellungen vorgenommen werden.
In diesem Fall habe ich eingestellt, dass nur die grundlegende MAC-Ebene verwendet werden soll und keine Erweiterung wie TSCH, 6TiSCH oder 6top, da Linux WPAN dies (noch) nicht unterstützt. Darüber hinaus erscheinen diese Erweiterungen sehr zentral, was einen Single Point of Failure erzeugt (siehe [hier](https://github.com/contiki-ng/contiki-ng/discussions/2835)). Danach habe ich eingestellt, dass kein Routing-Protokoll verwendet wird. Für 6LoWPAN wird normalerweise RPL als Routing-Protokoll verwendet (auch von Linux unterstützt), jedoch scheint das Routing-Protokoll sehr [zentral]((https://github.com/contiki-ng/contiki-ng/issues/2831)) zu sein. Des Weiteren bietet Contiki NG ein Shell-Modul. Dies stellt später eine serielle Konsole bereit, wo ich verschiedene Aktionen ausführen kann.

## Ping

Nachdem ich sowohl das Linux 6LoWPAN-Interface konfiguriert als auch Contiki NG geflasht hatte, habe ich eine serielle Konsole zu Contiki NG geöffnet:
```
#f4ce.369a.e71b.e9cf> hrelp
Available commands:
'> help': Shows this help
'> reboot': Reboot the board by watchdog_reboot()
ng.
'> mac-addr': Shows the node's MAC address
'> ip-addr': Shows all IPv6 addresses
'> ip-nbr': Shows all IPv6 neighbors
'> ping addr': Pings the IPv6 address 'addr'
'> routes': Shows the route entries
#f4ce.369a.e71b.e9cf> mac-addr
Node MAC address: f4ce.369a.e71b.e9cf
#f4ce.369a.e71b.e9cf> ip-addr
Node IPv6 addresses:
-- fe80::f6ce:369a:e71b:e9cf
#f4ce.369a.e71b.e9cf> ip-nbr
Node IPv6 neighbors: none
```

Danach habe ich versucht, über Contiki NG mein Linux Gerät anzupingen:
```
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Timeout
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Received ping reply from fe80::12e2:d5ff:ff00:463, len 4, ttl 64, delay 39 ms
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Received ping reply from fe80::12e2:d5ff:ff00:463, len 4, ttl 64, delay 46 ms
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Received ping reply from fe80::12e2:d5ff:ff00:463, len 4, ttl 64, delay 15 ms
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Received ping reply from fe80::12e2:d5ff:ff00:463, len 4, ttl 64, delay 23 ms
#f4ce.369a.e71b.e9cf> ip-nbr
Node IPv6 neighbors:
-- fe80::12e2:d5ff:ff00:463 <-> 10e2.d5ff.ff00.0463, router 128, state Reachable 
```
Bei meinen Tests erschien beim ersten Mal immer ein Timeout (vielleicht aufgrund der Neighbor Discovery?!). Danach funktioniert das Ping jedoch problemlos.

Andersherum - von Linux zu Contiki NG funktioniert der Ping auch problemlos:
```
$ ping -c 4 fe80::f6ce:369a:e71b:e9cf%lowpan0
PING fe80::f6ce:369a:e71b:e9cf%lowpan0 (fe80::f6ce:369a:e71b:e9cf%lowpan0) 56 data bytes
64 bytes from fe80::f6ce:369a:e71b:e9cf%lowpan0: icmp_seq=1 ttl=64 time=14.1 ms
64 bytes from fe80::f6ce:369a:e71b:e9cf%lowpan0: icmp_seq=2 ttl=64 time=11.2 ms
64 bytes from fe80::f6ce:369a:e71b:e9cf%lowpan0: icmp_seq=3 ttl=64 time=58.2 ms
64 bytes from fe80::f6ce:369a:e71b:e9cf%lowpan0: icmp_seq=4 ttl=64 time=17.0 ms

--- fe80::f6ce:369a:e71b:e9cf%lowpan0 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 11.226/25.143/58.241/19.218 ms
```]]>
        </content>
    </entry>
    
    <entry>
        <title>Contiki NG, Linux WPAN and 6LoWPAN</title>
        
        <link rel="alternate" href="https://mk16.de/blog/contiki-ng-linux-wpan-6lowpan-en/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/contiki-ng-linux-wpan-6lowpan-en/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/contiki-ng-linux-wpan-6lowpan-en/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/contiki-ng-linux-wpan-6lowpan-en/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/contiki-ng-linux-wpan-6lowpan-en/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/contiki-ng-linux-wpan-6lowpan-en/" hreflang="de" />

        <id>/blog/contiki-ng-linux-wpan-6lowpan-en/</id>
        <published>2023-01-09T00:00:00+01:00</published>
        <updated>2023-01-09T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Can Contiki NG and Linux communicate via 6LoWPAN / IEEE 802.15.4?</summary>
        <content type="html">
            <![CDATA[Recently I came across a list of operating systems in Wikipedia and there I rediscovered Contiki NG. I had seen before that Contiki NG is an operating system for small embedded systems, but I never had any hardware to try it out with. Contiki NG has a very nice [documentation](https://docs.contiki-ng.org/en/develop/) and in it I saw that it supports [6LoWPAN](https://en.wikipedia.org/wiki/6LoWPAN) - an IPv6-only low-rate low-power 2.4 GHz network standard - as an IPv6 fan this of course sounded great. 6LoWPAN was actually designed for IoT devices, but who says I can't try it out for fun?

## Hardware

I then looked at which hardware Contiki NG works on and came across NRF chips, which I already knew from Meshtastic. They were also the only inexpensive, easily available ones. I opted for the fairly compact [nRF52840 MDK USB dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/), which was even available online with a protective case.

![You can see a kind of USB stick lying on a wooden surface. It has an "M" logo printed on it. There is also a small cut-out for a key fob. You can also see a small button and an LED (or that there is a gray dot that can light up). The USB dongle is white.](/assets/img/contiki-ng-linux-wpan-6lowpan/nrf52840-mdk-usb-dongle.jpg)

Of course I also checked whether Linux supports 6LoWPAN and lo and behold - it is even in the mainline kernel. The Linux implementation is driven by the [Linux WPAN project](https://linux-wpan.org/). All Linux needs is supported hardware. The project's website has a list of these with links to online stores (many of which lead to 404 pages). My next step was to see which one was available in USB format and as cheaply as possible. I then decided on the ATUSB dongle - unfortunately there was no protective case available.

![You can see a circuit board. There is a USB port on the right side. On the left side you can see an antenna built into the circuit board. Two distinctive black chips can be seen on the circuit board. The board itself is green.](/assets/img/contiki-ng-linux-wpan-6lowpan/atusb-dongle.jpg)

## Linux WPAN

The first thing I received was the ATUSB dongle. A `wpan0` interface with an MTU of 123 (and therefore not IPv6-capable) appears automatically. There are instructions on the website on how to derive a `lowpan0` interface from this, which also supports IPv6 with an MTU of 1280. In order to keep the packets as small as possible, header compression is used. In addition, the packets are fragmented at MAC level if necessary. In order to achieve the best possible performance, it is still recommended to keep the packets as small as possible.
The following are the commands I used for configuration:
```
sudo ip link set dev wpan0 down
sudo iwpan dev wpan0 set pan_id 0xbada
sudo iwpan phy phy0 set channel 0 26
sudo ip link set dev wpan0 up
sudo ip link add link wpan0 name lowpan0 type lowpan
sudo ip link set dev lowpan0 up
```
Here, `0xbada` is the PAN ID (comparable to an SSID for WLAN) and `0 26` is the channel (standard channel in Contiki NG).

## Firmware

The nRF52840 MDK USB dongle arrived one day after the ATUSB dongle. This comes with the UF2 bootloader, which I already knew from Meshtastic. This is quite easy to use and protects (at least partially) against a briek of the device. To flash a firmware, use the button to switch the device to bootloader mode and mount it as a file system. You can then simply copy the firmware (supplied as an .uf2 file) to the device and the firmware will be installed automatically.

So I tried to compile Contiki NG as described in the documentation - but without success: My GCC version was too new. Contiki NG currently requires a GCC version of 10 (the current one is 13). There is already an [Issue](https://github.com/contiki-ng/contiki-ng/issues/2823). Fortunately, it was possible to install GCC version 10. After I had compiled the program with GCC 10, I received an .nrf file. The Makefile of Contiki NG actually also supports the upload of the firmware via USB, but no UF2 bootloader is expected, but the device as a serial interface. As this was not the case for me, I had to convert the .nrf file into an .uf2 file. When trying to upload it, I noticed that Contiki NG creates a .hex file in the build folder. This in turn can be easily converted into an .uf2 file using a [Python tool](https://github.com/makerdiary/uf2utils). I executed the following commands to create the .uf2 file:
```
make TARGET=nrf BOARD=nrf52840/dongle node.upload
uf2conv build/nrf/nrf52840/dongle/node.hex --family 0xADA52840 --convert --output node.uf2
```
I have named my program 'node'.

### Source code

Contiki-NG provides some examples. However, I just wanted a program with which I could ping other nodes - so I used the following from the examples:
`node.c`:
```c
#include "contiki.h"
#include "sys/log.h"

#define LOG_MODULE "Contiki-NG node"
#define LOG_LEVEL LOG_LEVEL_INFO

#include <stdbool.h>

/*---------------------------------------------------------------------------*/
PROCESS(node_process, "Contiki-NG node");
AUTOSTART_PROCESSES(&node_process);
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(node_process, ev, data)
{
    PROCESS_BEGIN();

    LOG_INFO("Contiki-NG node Started\n");

    while (true) {
        PROCESS_WAIT_EVENT();
    }

    PROCESS_END();
}
/*---------------------------------------------------------------------------*/
```

This program only starts the operating system and displays 'Contiki-NG node Started' once at startup.

`project-conf.h`:
```c
#ifndef PROJECT_CONF_H
#define PROJECT_CONF_H

#define IEEE802154_CONF_PANID 0xbada

#define UIP_CONF_ND6_SEND_RA 0

#endif
```
The project settings are made in this file. In this case, I wanted Contiki NG to act neither as a radv client nor as a radv server and set the PANID accordingly.

`Makefile`:
```makefile
CONTIKI_PROJECT = node
all: $(CONTIKI_PROJECT)
CONTIKI=../..

include $(CONTIKI)/Makefile.dir-variables

MAKE_MAC = MAKE_MAC_CSMA
MAKE_ROUTING = MAKE_ROUTING_NULLROUTING

MODULES += $(CONTIKI_NG_SERVICES_DIR)/shell

include $(CONTIKI)/Makefile.include
```
Makefiles actually play a more important role in Contiki NG, as various project settings are also made in them.
In this case, I have set that only the basic MAC level should be used and no extension such as TSCH, 6TiSCH or 6top, as Linux WPAN does not (yet) support this. In addition, these extensions appear very centralized, which creates a single point of failure (see [here](https://github.com/contiki-ng/contiki-ng/discussions/2835)). I then set that no routing protocol is used. For 6LoWPAN normally RPL is used as routing protocol (also supported by Linux), but the routing protocol seems to be very [centralized]((https://github.com/contiki-ng/contiki-ng/issues/2831)). Furthermore, Contiki NG offers a shell module. This later provides a serial console where I can perform various actions.

## Ping

After I had configured both the Linux 6LoWPAN interface and flashed Contiki NG, I opened a serial console to Contiki NG:
```
#f4ce.369a.e71b.e9cf> hrelp
Available commands:
'> help': Shows this help
'> reboot': Reboot the board by watchdog_reboot()
ng.
'> mac-addr': Shows the node's MAC address
'> ip-addr': Shows all IPv6 addresses
'> ip-nbr': Shows all IPv6 neighbors
'> ping addr': Pings the IPv6 address 'addr'
'> routes': Shows the route entries
#f4ce.369a.e71b.e9cf> mac-addr
Node MAC address: f4ce.369a.e71b.e9cf
#f4ce.369a.e71b.e9cf> ip-addr
Node IPv6 addresses:
-- fe80::f6ce:369a:e71b:e9cf
#f4ce.369a.e71b.e9cf> ip-nbr
Node IPv6 neighbors: none
```

I then tried to ping my Linux device via Contiki NG:
```
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Timeout
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Received ping reply from fe80::12e2:d5ff:ff00:463, len 4, ttl 64, delay 39 ms
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Received ping reply from fe80::12e2:d5ff:ff00:463, len 4, ttl 64, delay 46 ms
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Received ping reply from fe80::12e2:d5ff:ff00:463, len 4, ttl 64, delay 15 ms
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Received ping reply from fe80::12e2:d5ff:ff00:463, len 4, ttl 64, delay 23 ms
#f4ce.369a.e71b.e9cf> ip-nbr
Node IPv6 neighbors:
-- fe80::12e2:d5ff:ff00:463 <-> 10e2.d5ff.ff00.0463, router 128, state Reachable 
```
During my tests, a timeout always appeared the first time (perhaps due to Neighbor Discovery?!). After that, however, the ping worked without any problems.

The other way round - from Linux to Contiki NG the ping also works without any problems:
```
$ ping -c 4 fe80::f6ce:369a:e71b:e9cf%lowpan0
PING fe80::f6ce:369a:e71b:e9cf%lowpan0 (fe80::f6ce:369a:e71b:e9cf%lowpan0) 56 data bytes
64 bytes from fe80::f6ce:369a:e71b:e9cf%lowpan0: icmp_seq=1 ttl=64 time=14.1 ms
64 bytes from fe80::f6ce:369a:e71b:e9cf%lowpan0: icmp_seq=2 ttl=64 time=11.2 ms
64 bytes from fe80::f6ce:369a:e71b:e9cf%lowpan0: icmp_seq=3 ttl=64 time=58.2 ms
64 bytes from fe80::f6ce:369a:e71b:e9cf%lowpan0: icmp_seq=4 ttl=64 time=17.0 ms

--- fe80::f6ce:369a:e71b:e9cf%lowpan0 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 11.226/25.143/58.241/19.218 ms
```]]>
        </content>
    </entry>
    
    <entry>
        <title>Die CRXN/DN42 Interconnection ist up!</title>
        
        <link rel="alternate" href="https://mk16.de/blog/die-crxn-dn42-interconnection-ist-up/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/die-crxn-dn42-interconnection-ist-up/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/die-crxn-dn42-interconnection-ist-up/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/die-crxn-dn42-interconnection-ist-up/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/die-crxn-dn42-interconnection-ist-up/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/die-crxn-dn42-interconnection-ist-up/" hreflang="de" />

        <id>/blog/die-crxn-dn42-interconnection-ist-up/</id>
        <published>2023-01-30T00:00:00+01:00</published>
        <updated>2023-01-30T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Endlich ist die CRXN/DN42 Interconnection up. In diesem Blog Eintrag schreibe ich, wie es zur der Idee gekommen ist, wie man die Zustimmung der dn42 Community fand und wie das ganze implementiert ist.</summary>
        <content type="html">
            <![CDATA[Nach etwa zwei Monaten gibt es nun endlich eine Interconnection zwischen dem CRXN-Netzwerk und dem dn42-Netzwerk.

- Anfang / Idee
- Konsens
- Synchronisierung
- Routen aus CRXN in dn42
- Routen aus dn42 in CRXN

## Anfang

Die Idee kam im [Matrix Raum](https://matrix.to/#/#crxn:matrix.org) von CRXN auf. Das CRXN Netzwerk ist recht klein und hat daher sowohl wenig Routen als auch wenig Inhalt. Das dn42 hat wiederum viel Inhalt zu bieten. Es wäre also schön, wenn man vom CRXN aus im dn42 surfen könnte. Des Weiteren könnte man so auch vom dn42 auf Inhalte im CRXN zugreifen. Darüber hinaus macht es Spaß :-)

## Konsens

Ein recht nerviger, aber notwendiger Schritt. Um größere Dinge umzusetzen, braucht man die Zustimmung der Community. Die CRXN Community (bestehend aus ca. 5 Personen) hat zugestimmt, jedoch ist die dn42 Gemeinschaft viel größer. Daher wurde die Interconnection auf der dn42 Mailing Liste zur Diskussion vorgeschlagen. Des Weiteren gab es ein Meeting auf Mumble. Nach ca. einem Monat wurde ein Konsens erreicht: Die Interconnection darf stattfinden.

## Synchronisierung

Das "Problem" an dieser Interconnection ist, das dn42 und CRXN unterschiedliche Routing Protokolle verwenden. dn42 benutzt das EGP BGP und CRXN das IGP babel. Eine ausführliche Beschreibung, wie die Interconnection funktioniert, kann man auf [CRXN / dn42 gateway documentation](https://crxn.de/docs/additional/dn42-interconnection) in Englisch finden.
Die Zusammenfassung ist, dass die CRXN Routen in dn42 unter der AS-Nummer 4242423182 announct werden.

Entsprechend werden die Präfixe in die dn42 Registry übernommen. Für jedes Präfix wird ein inet6num-Objekt und ein route6-Objekt erstellt. In das route6-Objekt wird - falls angegeben - auch die max-len mit übernommen.
Des Weiteren werden - wenn vorhanden - auch Personen-Objekte übernommen.
Darüber hinaus gibt es ein Community-Objekt (`COMMUNITY-CRXN`) in der dn42 registry. In dem Objekt stehen die verschiedenen Orte (Matrix und IRC) in denen man mit der CRXN Community in Kontakt treten kann.

Die ganze Synchronisierung wurde von jrb0001 implementiert. Vielen Dank dafür!

## Routen aus CRXN in dn42

Um als Gateway zu fungieren, muss man in beiden Netzen präsent sein. Ich beispielsweise erfülle diese Voraussetzung. Dann nimmt man die CRXN Routen und "tut so, als ob sie von AS4242423182 kommen würden" und propagiert sie weiter zu seinen Peers. Wenn ich die CRXN Routen also an einem meiner Gateways empfange, nehme ich sie, hänge an den (noch leeren) AS-Pfad 4242423182 an und verbreite diese weiter in dn42. Damit hat es von außen den Anschein, dass alle CRXN Routen von einem AS kommen würden.

In bird kann man die ganz einfach mit folgendem Absatz im Export-Filter bewirken:
```
if ( is_crxn_net() ) then {
    bgp_path.prepend(CRXNAS);
}
```
Man kann dabei nicht nach Präfix unterscheiden, zu welchem Netz, welche Route gehört, da das dn42 und das CRXN Netzwerk sich den ULA-Raum `fd00::/8` teilen. `is_crxn_net` ist dabei eine Funktion, die überprüft, ob ein Netz zum CRXN-Netzwerk gehört und wenn das der Fall ist, `true` zurückgibt.

Ich habe die Funktion um folgendes erweitert:
```
if ( is_crxn_net() ) then {
    bgp_path.prepend(CRXNAS);
    if (babel_metric > 200) then {
        bgp_path.prepend(CRXNAS);
    }
}
```
Falls die Babel Metrik größer als 200 ist - ich also eine recht schlechte Anbindung zum Präfix habe - mache ich ein Path Prepending. Durch den längeren AS-Pfad mache ich mich dadurch unattraktiver. Sollte ein anderes Gateway nun das Präfix mit einer Metrik von unter 200 empfangen, sollte man über das andere Gateway gehen. Durch den kürzeren AS-Pfad vom anderen Gateway wird das erzielt.

## Routen aus dn42 in CRXN

Die offensichtliche Methode, dies zu tun, ist die Präfixe aus dem dn42 - welche via BGP übertragen wurden - zu nehmen und in das CRXN - welches babel verwendet - zu senden. Dies bedeutet, man propagiert dann auch die dn42 Präfixe und tut damit so als wären es die eigenen Routen. Über die Babel Metrik können dann CRXN Teilnehmer entscheiden, welches Gateway besser ist. Diese Methode habe ich auch zuerst versucht, jedoch hat sie einige Probleme mitgebracht:

1. Enorm, enorm große Tabellen. Groß ist natürlich releativ, aber die dn42 Routing Table über 700 IPv6 Einträge ist größer als die CRXN Routing Table mit ca. 20 Einträgen. Entsprechend ist die CRXN Routing Tabelle sinnbildlich "explodiert". babelweb2 - einer Monitoring und Debugging Software, welche über ein Webinterface erreichbar ist - hat mehrere Minuten gebraucht um die Tabelle zu laden. Dies ist auf Dauer sehr nervtötend. Im CRXN Matrix Raum wurde der Vorschlag genannt, die Software zu optimieren, jedoch fehlt es dafür an Wissen, Kapazitäten und dem Nutzen. Der Vorschlag wurde also verworfen.
2. Es ist enorm schwer, **einfach** Filter für dieses Szenario zu erstellen.
Ohne dn42 hat man ca. 20 Präfixe man filtern kann. Mit dn42 hat man über 700 Präfixe. Möchte man nun einen Filter dafür erstellen, so braucht man sowohl die CRXN Präfixe als auch die dn42 Präfixe. Jedoch ist es wünschenswert, die dn42 Filterung an die Gateways auszulagern. Dies bedeutet, die CRXN Teilnehmer sollten CRXN Routen filtern und keine dn42 Routen. Dies bedeutet, man muss die Präfixe der beiden Netzwerke unterscheiden.
Diese Information kann aufgrund der Natur von Babel nicht von den Gateways mitgeteilt werden. Um das zu lösen, brächten die CRXN Teilnehmer jedoch extra eine Liste aller dn42 Routen. Jedoch gilt es genau dies zu vermeiden.
Komplexere nicht-Ja-Nein-Filter sind des Weiteren nicht in allen Implementierungen möglich. bird stellt umfangreiche Methoden bereit, um Filter zu erstellen. So kann man wie in der Programmierung andere Dateien inkludieren, if-else if-else Anweisungen und Variablen benutzen. In babeld sind dagegen nur Ja-Nein-Filter möglich. babeld arbeitet in dieser Hinsicht ähnlich wie eine Firewall. babeld liest die Regeln von oben nach unten und führt die entsprechende Aktion (allow/deny) aus. Das Speichern von Zwischenergebnissen ist dabei nicht möglich. Damit man also in babeld CRXN und dn42 Routen unterscheiden kann, müsste man alle dn42 Routen in die Konfigurationsdatei schreiben. Wenn man stattdessen einfach alle Routen akzeptiert, kann man keine (maxlen) Filterung der CRXN Routen mehr machen.
    - Die [entitydb](https://codeberg.org/CRXN/entitydb) (das Registry von CRXN) ist frei im Internet zugänglich. Jeder kann die Informationen dort einsehen. Um die Daten aus der entitydb zu holen, kann man das Repository mit Git klonen und als ZIP-Datei herunterladen.
    Bei dem dn42 registry ist das leider etwas anders. Auch das dn42 Registry basiert auf Git, weshalb prinzipiell die gleichen Methoden zum Holen der Informationen zur Verfügung stehen. Jedoch steht das dn42 Registry hinter einer Account-Wall. Man muss sich also zuerst anmelden, um auf das Registry zugreifen zu können. Wenn man das Git Repository klonen möchte, muss man erst einen SSH-Schlüssel hinterlegen und kann dann mit diesem darauf zugreifen. Meiner Meinung nach, ist die ganze Maßnahme sinnlos, dass man über die APIs einen JSON Dump der Registry bekommen kann: [hier](https://explorer.burble.com/api/registry/*/*) oder [hier](https://explorer.dn42.pebkac.gr/api/registry/*/*)

Es gibt verschiedene Möglichkeiten, dieses Problem zu lösen:
1. Präfix-basierte Filterung: Das CRXN-Netzwerk sucht sich ein /32-Präfix im ULA-Bereich aus und allokiert nur noch aus diesem Bereich. Dies würde ein aufwendigen Renumbering zur Folge haben, weshalb diese Option von Teilen der CRXN Community abgelehnt wird.
2. Default-Route: Statt den einzelnen dn42-Präfixen kann man auch eine allgemeine Route - sogenannte Default Route - `fd00::/8` in CRXN annoncen. Wenn kein genaueres Präfix bekannt ist, wird die Anfrage dann an diese Adresse gesendet. `fd00::/8` wird dabei nur von den Gateways propagiert. Diese können die Anfrage dann entsprechend weiter in dn42 leiten.
    - Diese Methode hat zwei Nachteile: Einmal kann der Router von einem CRXN Teilnehmer nicht selbst entscheiden, ob es einen Weg gibt. Eine ICMP Unreachable Nachricht müsste also vom Gateway kommen. Wenn ein Router dann nicht konfiguriert ist, könnte er keine ICMP Nachricht generieren und die Anfrage würde auf ein Timeout hinauflaufen.
    Des Weiteren sollte es die Aufgabe von Routern sein, eine Übersicht über das gesamte Netzwerk zu haben und damit selbst entscheiden zu können, ob ein bestimmtes Präfix erreichbar ist oder nicht - das sollte nicht die Aufgabe der Gateways sein. Die Gateways sind eher als eine Art "Übersetzer" gedacht, welche zwischen den unterschiedlichen Routing Protokollen vermitteln.

Angenommen die Gateways speisen die dn42 in CRXN ein, so ist es einem CRXN Teilnehmer zwar aufgrund der Babel Metrik immer möglich den besten Weg zum nächsten Gateway zu finden, jedoch bedeutet dies nicht zwingend, dass das der beste Weg zum Ziel ist. So kann Gateway A beispielsweise mit dem Ziel AS in dn42 gepeert sein und Gateway B kann das Ziel AS erst über mehrere andere ASs erreichen, jedoch entscheidet sich der CRXN Teilnehmer aufgrund der Babel Metrik für das Gateway B. Dieses Problem könnte man lösen, indem man die Babel Metrik anhand der AS-Pfad-Länge berechnet, jedoch bin ich mir unsicher, ob dies technisch möglich ist, anderseits kann dies die Schleifenfreiheit von Babel gefährden. Dieses Problem, jedoch ohne potenzielle Lösung, tritt auch bei der Default Route auf.

### Abwägung

Eine Abwägung zwischen Full Table und Default Route zu treffen, ist schwer. So haben beide Methoden Vor- und auch Nachteile. Prinzipiell wäre auch eine Mischung möglich - also Full Table + Default Route. Für die CRXN Teilnehmer, welches jedoch Default Route wählen und andere nicht-CRXN-Präfixe filtert, kann die Filterung zu einer höheren CPU Leistung führen. Des Weiteren würde ein solcher Knoten die nicht-CRXN-Präfixe nicht weiterpropagieren. Das Gleiche gilt für Full Table: Ein Knoten, welcher Full Table nimmt, aber die Default-Route ablehnt, würde die Default-Route nicht weiter verteilen.

Ich denke, dass eine optimale Interconnection aufgrund der unterschiedlichen Natur vom EGP BGP und vom IGP Babel nicht möglich ist. Daher sollte man sich fragen, wie die Interconnection dann aussehen könnte. Aufgrund des geringen Aufwandes, bin ich ein Freund der Lösung mit der Default Route - wie oben beschrieben, verschwinden dadurch aber nicht alle Probleme.

So oder so, sind das CRXN und dn42 Projekte zum Lernen und Spaß haben. Habt also schön viel Spaß mit der Interconnection!

Nachtrag 1: Tristan aka deavmi, der Initiator der CRXN, hat auch einen Blogeintrag geschrieben: [Announcing the CRXNxDN42 inter-connect!](http://deavmi.assigned.network/blog/announcing-crxn-dn42-interconnect/)]]>
        </content>
    </entry>
    
    <entry>
        <title>The CRXN/DN42 interconnection is up!</title>
        
        <link rel="alternate" href="https://mk16.de/blog/the-crxn-dn42-interconnection-is-up/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/the-crxn-dn42-interconnection-is-up/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/the-crxn-dn42-interconnection-is-up/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/the-crxn-dn42-interconnection-is-up/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/the-crxn-dn42-interconnection-is-up/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/the-crxn-dn42-interconnection-is-up/" hreflang="de" />

        <id>/blog/the-crxn-dn42-interconnection-is-up/</id>
        <published>2023-01-30T00:00:00+01:00</published>
        <updated>2023-01-30T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Endlich ist die CRXN/DN42 Interconnection up. In diesem Blog Eintrag schreibe ich, wie es zur der Idee gekommen ist, wie man die Zustimmung der dn42 Community fand und wie das ganze implementiert ist.</summary>
        <content type="html">
            <![CDATA[After about two months there is finally an interconnection between the CRXN network and the dn42 network.

- Start / Idea
- Consensus
- Synchronization
- Routes from CRXN into dn42
- Routes from dn42 into CRXN

## Start

The idea came up in the [matrix room](https://matrix.to/#/#crxn:matrix.org) of the CRXN community. The CRXN network is quite small and therefore has both few routes and little content. The dn42 on the other hand has a lot of content to offer. So it would be nice to be able to surf the dn42 from CRXN. Furthermore, this would also allow one to access content in the CRXN from the dn42. Beyond that, it's fun :-)

## Consensus

A rather annoying, but necessary step. To implement bigger things, you need the consensus of the community. The CRXN community (consisting of about 5 people) agreed, but the dn42 community is much larger. Therefore, the interconnection was proposed on the dn42 mailing list for discussion. Furthermore, there was a meeting on Mumble. After about a month a consensus was reached: the interconnection may take place.

## Synchronization

The "problem" with this interconnection is that dn42 and CRXN use different routing protocols. dn42 uses the EGP BGP and CRXN the IGP babel. A detailed description of how the interconnection works can be found on [CRXN / dn42 gateway documentation](https://crxn.de/docs/additional/dn42-interconnection).
The summary is that the CRXN routes in dn42 are announced under the AS number 4242423182.

Accordingly, the prefixes are added to the dn42 registry. For each prefix an inet6num object and a route6 object are created. If specified, the max-len is also included in the route6 object.
Furthermore - if available - also person objects are taken over.
Furthermore there is a community object (`COMMUNITY-CRXN`) in the dn42 registry. This object contains the different places (Matrix and IRC) where you can get in contact with the CRXN community.

The whole synchronization was implemented by jrb0001. Many thanks for that!

## Routes from CRXN to dn42

To act as a gateway, one must be present in both networks. I, for example, meet this requirement. Then you take the CRXN routes and "pretend they came from AS4242423182" and propagate them further to your peers. So when I receive the CRXN routes at one of my gateways, I take them, attach 4242423182 to the (still empty) AS path and propagate them further into dn42. This makes it appear from the outside that all CRXN routes would come from one AS.

In bird you can easily do this with the following paragraph in the export filter:
```
if ( is_crxn_net() ) then {
    bgp_path.prepend(CRXNAS);
}
```
It is not possible to distinguish by prefix to which network, which route belongs, because the dn42 and the CRXN network share the ULA space `fd00::/8`. Here `is_crxn_net` is a function that checks if a network belongs to the CRXN network and if it does, returns `true`.

I have extended the function with the following:
```
if ( is_crxn_net() ) then {
    bgp_path.prepend(CRXNAS);
    if (babel_metric > 200) then {
        bgp_path.prepend(CRXNAS);
    }
}
```
If the Babel metric is greater than 200 - so I have a rather poor connection to the prefix - I do a Path Prepending. The longer AS path makes me less attractive. If another gateway now receives the prefix with a metric below 200, one should go through the other gateway. By the shorter AS path from the other gateway this is achieved.

## Routes from dn42 into CRXN

The obvious way to do this is to take the prefixes from the dn42 - which were transmitted via BGP - and send them into the CRXN - which uses babel. This means you then also propagate the dn42 prefixes and pretend they are your own routes. Using the babel metric, CRXN subscribers can then decide which gateway is better. I also tried this method first, but it brought some problems:

1. enormously, enormously large tables. Large is of course releative, but the dn42 routing table over 700 IPv6 entries is larger than the CRXN routing table with about 20 entries. Accordingly the CRXN routing table "exploded". babelweb2 - a monitoring and debugging software which is accessible via a web interface - took several minutes to load the table. This is very annoying in the long run. In the CRXN Matrix room the suggestion was mentioned to optimize the software, but there is a lack of knowledge, capacity and the benefit. So the suggestion was discarded.
2. it is enormously difficult to create **simple** filters for this scenario.
Without dn42 one has about 20 prefixes one can filter. With dn42 you have over 700 prefixes. If one now wants to create a filter for this, one needs both the CRXN prefixes and the dn42 prefixes. However, it is desirable to outsource the dn42 filtering to the gateways. This means the CRXN members should filter CRXN routes and not dn42 routes. This means one must distinguish the prefixes of the two networks.
This information cannot be communicated by the gateways due to the nature of Babel. However, to solve this, the CRXN members would need an extra list of all dn42 routes. However, this is exactly what needs to be avoided.
Furthermore, more complex non-yes-no filters are not possible in all implementations. bird provides extensive methods to create filters. Like in programming you can include other files, use if-else if-else statements and variables. In babeld, however, only yes-no filters are possible. babeld works similar to a firewall in this aspect. babeld reads the rules from top to bottom and executes the corresponding action (allow/deny). Saving intermediate results is not possible. So in order to distinguish CRXN and dn42 routes in babeld, you would have to write all dn42 routes to the configuration file. If you just accept all routes instead, you can't do any (maxlen) filtering of CRXN routes anymore.
    - The [entitydb](https://codeberg.org/CRXN/entitydb) (the registry of CRXN) is freely available on the Internet. Anyone can view the information there. To get the data from the entitydb, you can clone the repository with Git or download it as a ZIP file.
    With the dn42 registry it is unfortunately somewhat different. The dn42 registry is also based on Git, so in principle the same methods are available to fetch the information. However, the dn42 registry is behind an account wall. So you have to log in first to access the registry. If you want to clone the git repository, you first have to store an SSH key and then you can access it with that key. In my opinion, the whole measure is pointless since you can get a JSON dump of the registry via the APIs: [here](https://explorer.burble.com/api/registry/*/*) or [here](https://explorer.dn42.pebkac.gr/api/registry/*/*).

There are several ways to solve this problem:
1. prefix-based filtering: the CRXN network picks a /32 prefix in the ULA range and allocates only from this range. This would result in costly renumbering, which is why this option is rejected by parts of the CRXN community.
2. default route: Instead of the individual dn42 prefixes, it is also possible to advertise a general route - so-called default route - 'fd00::/8' in CRXN. If no more specific prefix is known, the request will then be sent to this address. `fd00::/8` is only propagated by the gateways. They can then forward the request to dn42 accordingly.
    - This method has two disadvantages: Once the router from a CRXN member cannot decide for itself if there is a path. So an ICMP Unreachable message would have to come from the gateway. Then, if a gateway is not miskonfigured, it could not generate an ICMP message and the request would timeout.
    Furthermore, it should be the job of routers to have an overview of the entire network and thus be able to decide for themselves whether a particular prefix is reachable or not - this should not be the job of gateways. The gateways are rather meant as a kind of "translator", which mediates between the different routing protocols.

Assuming the gateways feed the dn42 into CRXN, it is always possible for a CRXN member to find the best path to the next gateway because of the Babel metric, but this does not necessarily mean that this is the best path to the destination. For example, gateway A may be peered with the destination AS in dn42 and gateway B may reach the destination AS only via several other ASs, but the CRXN member chooses gateway B based on the Babel metric. This problem could be solved by calculating the Babel metric based on the AS path length, but I am unsure if this is technically possible, on the other hand this may compromise the loop-free nature of Babel. This problem, but with no potential solution, also occurs with the Default Route.

### Trade-off

It is difficult to decide between Full Table and Default Route. Both methods have advantages and disadvantages. In principle, a mixture would also be possible - so Full Table + Default Route. However, for CRXN nodes that choose Default Route and filter other non-CRXN prefixes, filtering may lead to higher CPU performance. Furthermore, such a node would not propagate the non-CRXN prefixes. The same is true for Full Table: a node that takes Full Table but rejects the default route would not propagate the default route.

I think that an optimal interconnection is not possible due to the different nature of EGP BGP and IGP Babel. Therefore one should ask how the interconnection could look like then. Because of the low effort, I am a friend of the solution with the default route - but as described above, not all problems disappear.

Either way, these are CRXN and dn42 projects to learn and have fun with. So have fun with the interconnection!

Addendum 1: Tristan aka deavmi, initiator of CRXN, has also written a blog entry: [Announcing the CRXNxDN42 inter-connect!](http://deavmi.assigned.network/blog/announcing-crxn-dn42-interconnect/)]]>
        </content>
    </entry>
    
    <entry>
        <title>Preparations to join the CRXN network</title>
        
        <link rel="alternate" href="https://mk16.de/blog/crxn-preparations/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/crxn-preparations/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/crxn-preparations/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/crxn-preparations/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/crxn-preparations/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/crxn-preparations/" hreflang="en" />

        <id>/blog/crxn-preparations/</id>
        <published>2023-01-24T00:00:00+01:00</published>
        <updated>2023-01-24T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>What preparations do you need to make to join the CRXN network? Here I list a few - from registration to the equipment you need.</summary>
        <content type="html">
            <![CDATA[To join the CRXN network, three things are basically required:

- Fun to learn and motivation
- A network-enabled device that has Internet access
- A registration in the EntityDB

## Prior knowledge

For this blog post, I assume the following prior knowledge:
- Git
  - clone, pull, push, fork, PR
- JSON

## Patience and motivation

IP routing can be very difficult. On the one hand, there are sometimes quite heavy concepts behind it - on the other hand, the practical implementation is often associated with problems. Therefore it can happen that some give up. One should therefore consider in advance whether one really wants to join the network.

## A device

Furthermore, you need a device that has access to the Internet / Clearnet and preferably runs 24/7. For example, an old computer that you still have standing around (attention: power consumption) or a single-board computer such as a Pi. The device should be connected to the Internet via cable. With WiFi / WLAN it can lead to slower speeds and high latencies.
Alternatively, you can rent a virtual machine online. You can see my providers at [byeob.de](https://byeob.de/). A small virtual machine with one CPU core and one gigabyte of memory is usually sufficient. If you rent a VM, you should make sure that it is a VM and not a container. With virtual machines, you install a kernel yourself and can precisely control the routing and kernel parameters accordingly. A container shares the kernel with the host and also "inherits" the settings and modules. For example, if the host does not have a WireGuard module, WireGuard cannot be used in the container without a detour. Containers are often advertised as "LXC", "Docker" or "OpenVZ". Virtual machines, on the other hand, with "KVM", "WMware" and "Hyper-V".

You can now install an operating system on this machine. In general, **no** Windows and **no** Apple will work.

General it is not relevant if you have the device with IPv4, IPv6 or both. Because over the Internet only the connection to other CRXN members will be established. In CRXN itself only IPv6 is used.

## The registration

To participate in the CRXN network you need an entity in the EntityDB short edb. This can be found on [Codeberg](https://codeberg.org/CRXN/entitydb). To register, you can "fork" this. In the fork you create a new JSON file with the name `<entity>.json`. Replace `<entity>` with a name. This can be for example a nickname.
You can use the following as an example:
```json
{
  "person": {
    "name": "Marek Küthe",
    "email": "crxn@mk16.de",
    "gpg": "9AA28159FCEB3CD83BC212017E869146699108C7"
  },
  "route": {
    "fd92:58b6:2b2::/48": {
      "description": "Bandura Communications",
      "max-len": "48",
      "device": {
        "fd92:58b6:2b2::1": {
          "type": "router",
          "description": "My first router"
        }
      }
    }
  }
}
```
An indentation of two spaces is used.
The information under people is **optional**. If you don't want to enter any information, you can omit the people part. You can also use other attributes in the people part. For example, I put my name, my e-mail address and the fingerprint of my GPG key there.

Under `route` you enter the relevant information.
The following rules apply:
- There must be at least one prefix
- There must be at least one device per prefix

For me the prefix would be `fd92:58b6:2b2::/48`. You can use a random prefix from the ULA range `fd00::/8`. For example, on [www.unique-local-ipv6.com](https://www.unique-local-ipv6.com/) one can have ULA prefixes generated. One can **optionally** specify a description. If you give one, it must be at least one character long and (currently) at most 2048 characters long.

You **do not** have to specify `max-len`. However, I **strongly recommend** it. Later, if you have more than one device that is doing routing on the CRXN network, you don't have to request a new prefix for each device, but can split your prefix into small pieces - for example /56 or /60. `max-len` specifies the length of the smallest prefix. The minimum is 64. If you do not specify `max-len`, you may be vulnerable to route hjacking. More information about this can be found at [crxn.de - max-len filter](https://crxn.de/docs/entitydb/maxlen).

`device` must specify at least one (optimally reachable) device. A distinction is made between `generic`, `router` or `server`. If the type is omitted, `generic` is assumed. The description is **voluntary**. Normally a network has at least one device - the router. This is often assigned the address `xxxx::1`. However, you can give the router any other address from your prefix.

After creating the JSON file, you can validate the JSON. For example, you can use `jq` or [JSONLint](https://jsonlint.com/). After that you can check the attributes using the schema file `schema.json`. For this you can use a tool like `check-jsonschema` (Python): `check-jsonschema --schemafile schema.json <entity>.json`.

After that you can create a pull request. Normally an EntityDB administrator will check the request within a week and merge it. After that you are registered.

If you are inactive for a longer time, the network is not reachable and the entity contact is not reachable either, it can happen that the entity is deleted in EntityDB.

## Help

There can always be places where you don't know what to do. In this case you can ask other members of the CRXN network. You can find them on Matrix in the room [`#crxn:matrix.org`](https://matrix.to/#/#crxn:matrix.org).]]>
        </content>
    </entry>
    
    <entry>
        <title>Vorbereitungen, um dem CRXN Netzwerk beizutreten</title>
        
        <link rel="alternate" href="https://mk16.de/blog/crxn-vorbereitungen/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/crxn-vorbereitungen/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/crxn-vorbereitungen/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/crxn-vorbereitungen/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/crxn-vorbereitungen/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/crxn-vorbereitungen/" hreflang="de" />

        <id>/blog/crxn-vorbereitungen/</id>
        <published>2023-01-24T00:00:00+01:00</published>
        <updated>2023-01-24T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Welche Vorbereitungen muss man treffen, um dem CRXN-Netzwerk beizutreten? Hier liste ich ein paar auf - von der Anmeldung bis zu den benötigten Geräten.</summary>
        <content type="html">
            <![CDATA[Um dem CRXN Netzwerk beizutreten, sind grundsätzlich drei Dinge erforderlich:

- Spaß am Lernen und Motivation
- Ein netzwerkfähiges Gerät, welches Internetzugriff hat
- Eine Registrierung in der EntityDB

## Vorwissen

Für diesen Blogeintrag setze ich folgendes Vorwissen voraus:
- Git
  - Clone, Pull, Push, Fork, PR
- JSON

## Geduld und Motivation

IP Routing kann sehr schwierig sein. Einmal sind es teilweise recht schwere Konzepte, welche dahinter sind - andererseits ist die praktische Umsetzung häufig mit Problemen verbunden. Daher kann es vorkommen, dass einige aufgeben. Man sollte sich daher im Vorhinein überlegen, ob man dem Netzwerk wirklich beitreten möchte.

## Ein Gerät

Des Weiteren braucht man ein Gerät, welches Zugriff auf das Internet / Clearnet hat und am besten 24/7 läuft. Dafür eignet sich zum Beispiel ein alter Rechner, welchen man noch herumstehen hat (Achtung: Stromverbrauch) oder ein Einplatinencomputer wie ein Pi. Das Gerät sollte via Kabel an das Internet angebunden sein. Bei WiFi / WLAN kann es zu langsameren Geschwindigkeiten und hohen Latenzen führen.
Alternativ kann man sich online eine virtuelle Maschine mieten. Meine Provider kann man auf [byeob.de](https://byeob.de/) sehen. Eine kleine virtuelle Maschine mit einem CPU-Kern und einem Gigabyte Arbeitsspeicher ist im Normalfall ausreichend. Wenn man sich eine VM mietet, sollte man darauf achten, dass es sich um eine VM und keinen Container handelt. Bei virtuellen Maschinen installiert man selber einen Kernel und kann entsprechend das Routing und die Kernel Parameter genau steuern. Ein Container teilt sich den Kernel mit dem Host und "erbt" dabei auch die Einstellungen und Module. Sollte der Host beispielsweise kein WireGuard Modul haben, so kann man WireGuard im Container nicht ohne Umwege benutzten. Container werden häufig mit "LXC", "Docker" oder "OpenVZ" beworben. Virtuelle Maschinen dagegen mit "KVM", "WMware" und "Hyper-V".

Auf diesem Gerät kann man nun ein Betriebssystem installieren. Im Allgemeinen funktioniert **kein** Windows und **kein** Apple.

General ist es nicht relevant, ob man das Gerät IPv4, IPv6 oder beides hat. Da über das Internet lediglich die Verbindung zu anderen CRXN Teilnehmern aufgebaut wird. In CRXN selber wird nur IPv6 verwendet.

## Die Registrierung

Um im CRXN Netzwerk teilnehmen zu können, braucht man eine Entity in der EntityDB kurz edb. Diese kann man auf [Codeberg](https://codeberg.org/CRXN/entitydb) finden. Um sich zu registrieren, kann man dies "forken". In dem Fork erstellt man eine neue JSON Datei mit dem Namen `<entity>.json`. Dabei ersetzt man `<entity>` durch einen Namen. Dies kann zum Beispiel ein Nickname sein.
Folgendes kann man als Beispiel nehmen:
```json
{
  "person": {
    "name": "Marek Küthe",
    "email": "crxn@mk16.de",
    "gpg": "9AA28159FCEB3CD83BC212017E869146699108C7"
  },
  "route": {
    "fd92:58b6:2b2::/48": {
      "description": "Bandura Communications",
      "max-len": "48",
      "device": {
        "fd92:58b6:2b2::1": {
          "type": "router",
          "description": "My first router"
        }
      }
    }
  }
}
```
Es wird eine Einrückung von zwei Leertasten verwendet.
Die Angaben unter Personen sind **freiwillig**. Wenn man keine Angaben machen möchte, kann man den Teil mit den Personen weglassen. Man kann bei dem Personen-Teil auch andere Attribute verwenden. Ich habe dort beispielsweise meinen Namen, meine E-Mail-Adresse und den Fingerabdruck meines GPG Schlüssel angegeben.

Unter `route` gibt man nun die eigentlich relevanten Informationen an.
Es gelten folgende Regeln:
- Es muss mindestens ein Präfix geben
- Je Präfix muss mindestens ein Gerät vorhanden sein

Bei mir wäre das Präfix `fd92:58b6:2b2::/48`. Man kann ein zufälliges Präfix aus dem ULA-Bereich `fd00::/8` verwenden. Auf [www.unique-local-ipv6.com](https://www.unique-local-ipv6.com/) kann man sich beispielsweise ULA-Präfixe generieren lassen. Man kann **freiwillig** eine Beschreibung angeben. Wenn man eine angibt, muss diese mindestens ein Zeichen lang sein und darf (aktuell) höchstens 2048 Zeichen lang sein.

`max-len` muss man **nicht** angeben. Ich **empfehle** es jedoch stark. Wenn man später mehr als ein Gerät im CRXN Netzwerk hat, welches routet, muss man nicht für jedes Gerät ein neues Präfix anfragen, sondern kann sein Präfix in kleine Stücke aufteilen - beispielsweise /56 oder /60. `max-len` gibt dabei die Länge des kleinsten Präfixes an. Das Minimum ist 64. Wenn man `max-len` nicht angibt, kann es sein, dass man anfällig für Route Hjacking ist. Weitere Informationen dazu kann man auf [crxn.de - max-len filter](https://crxn.de/docs/entitydb/maxlen) finden.

`device` muss mindestens ein (im Optimalfall erreichbares) Gerät angeben. Dabei unterscheidet man zwischen `generic`, `router` oder `server`. Wird der Type weggelassen, wird `generic` angenommen. Die Beschreibung ist **freiwillig**. Im Normalfall hat ein Netzwerk mindestens ein Gerät - den Router. Dieser bekommt häufig die Adresse `xxxx::1` zugewiesen. Man kann dem Router jedoch auch eine beliebige andere Adresse aus deinem Präfix geben.

Nachdem man die JSON Datei erstellt hat, kann man die JSON validieren. Dafür kann man beispielsweise `jq` oder [JSONLint](https://jsonlint.com/) verwenden. Danach kann man mithilfe der Schema-Datei `schema.json` deine Attribute überprüfen. Dafür kann man ein Tool wie `check-jsonschema` (Python) benutzten: `check-jsonschema --schemafile schema.json <entity>.json`

Danach kann man eine Pull Request erstellen. Normalerweise überprüft ein EntityDB Administrator die Anfrage innerhalb von einer Woche und merget diese. Danach ist man registriert.

Wenn man längere inaktiv ist, das Netzwerk nicht erreichbar und die Entity auch nicht, kann es passieren, dass die Entity in der EntityDB gelöscht wird.

## Hilfe

Es kann immer wieder Stellen geben, wo man selber nicht mehr weiterweiß. In diesem Fall kann man andere Mitglieder des CRXN Netzwerkes fragen. Diese findet man auf Matrix im Raum [`#crxn:matrix.org`](https://matrix.to/#/#crxn:matrix.org)]]>
        </content>
    </entry>
    
    <entry>
        <title>Script to determine which AS, do not filter ROA invalid prefixes, based on data from the GRC</title>
        
        <link rel="alternate" href="https://mk16.de/blog/dn42-grc-roa/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/dn42-grc-roa/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/dn42-grc-roa/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/dn42-grc-roa/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/dn42-grc-roa/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/dn42-grc-roa/" hreflang="en" />

        <id>/blog/dn42-grc-roa/</id>
        <published>2023-07-18T01:00:00+02:00</published>
        <updated>2024-07-09T01:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>A small shell command to determine which AS do not filter ROA invalid prefixes in dn42</summary>
        <content type="html">
            <![CDATA[Burble (AS4242422601) announces the prefix `fd42:4242:2601:ffff::/64` with ROA Invalid to test which AS filter ROA Invalid prefix and which do not. The following script identifies some ASs that do not filter. For this it asks the GRC via SSH which AS "pass" the invalid prefix to the GRC and do not filter it out. These and the ASs in the AS path are then output. The origin AS of burble is excluded.
```sh
ssh shell@collector.dn42 "show route for fd42:4242:2601:ffff::/64 where bgp_path.first != 4242422601 all" 2>/dev/null | grep -Po "BGP.as_path: (\d{1,20} ?)+" | grep -Po "\d+" | grep -v "4242422601" | sort | uniq
```]]>
        </content>
    </entry>
    
    <entry>
        <title>Script to test a dn42 peer for clearnet access</title>
        
        <link rel="alternate" href="https://mk16.de/blog/dn42-test-clearnet-access/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/dn42-test-clearnet-access/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/dn42-test-clearnet-access/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/dn42-test-clearnet-access/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/dn42-test-clearnet-access/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/dn42-test-clearnet-access/" hreflang="en" />

        <id>/blog/dn42-test-clearnet-access/</id>
        <published>2023-05-08T01:00:00+02:00</published>
        <updated>2023-05-08T01:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>The following script rambles around dn42 IRC on Hackint every now and then. It is a small bash script which checks if you can reach the clearnet via a certain interface.</summary>
        <content type="html">
            <![CDATA[The following script rambles around dn42 IRC on Hackint every now and then. Therefore I wanted to save it, so I don't have to search for it. I did not write the script.

It is a small bash script which checks if you can reach the clearnet via a certain interface. This should not be the case with peers in dn42. This script checks all peer interfaces and gives a message if a peer can reach the internet.

```sh
ip link | awk -F: '$0 !~ "lo|eth|vir|enp|ens|^[^0-9]"{print $2;getline}' | while read line ; do echo Testing peer on interface: $line; timeout 3 ping -c 1 -I $line 1.1.1.1 > /dev/null; rc=$?;  if [[ $rc -eq 0 ]]; then echo "---> Please alert your peer on interface $line"; fi; done
```]]>
        </content>
    </entry>
    
    <entry>
        <title>Beginner Tips</title>
        
        <link rel="alternate" href="https://mk16.de/blog/dn42-beginner-tips/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/dn42-beginner-tips/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/dn42-beginner-tips/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/dn42-beginner-tips/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/dn42-beginner-tips/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/dn42-beginner-tips/" hreflang="en" />

        <id>/blog/dn42-beginner-tips/</id>
        <published>2023-01-31T12:00:00+01:00</published>
        <updated>2023-01-31T12:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>I was asked some time ago for tips for the dn42. Here they are. Furthermore I present my BGP peer configuration in bird.</summary>
        <content type="html">
            <![CDATA[I was asked for tips in an email. Well why not share them now? These tips are for "newbies" in dn42. I am happy to receive suggestions and ideas for improvement: {{ site.contact.marek }}

(The order is based on what came to my mind first)

## Tips
- Make a backup of everything. [BorgBase](https://www.borgbase.com/) offers free 10GB encrypted backup. It's pretty easy to set up.
- Try to do ROA filtering. I used https://gitlab.com/bauen1/dn42-roagen for this so I don't have to depend on burbles service centrally.
- It doesn't matter if you make mistakes. The dn42 is for learning, not for making everything perfect.
- If you are looking for peers, you can look at https://dn42.us/peers/.
- If you use bird, you can easily enable extended next hop and extended messages. You can enable it by default. If your peer does not support it, it will not be used.
- If you use bird, you can look at
  [https://github.com/jlu5/ansible-dn42/blob/main/roles/config-bird2/config/community_filters.conf](https://github.com/jlu5/ansible-dn42/blob/main/roles/config-bird2/config/community_filters.conf).
- If you enjoy dn42 and are up for an adventure: [IXP frnte](https://p2p-node.de/ixp_frnte.html)
- If you need some filter examples: [BGP Filter Guide - Guidance on BGP Filtering](https://bgpfilterguide.nlnog.net/) (attention, these have to be adapted for dn42); [BGP Graceful Shutdown](https://bgpfilterguide.nlnog.net/guides/graceful_shutdown/) can be used without any changes.
- If you have little patience, don't use IPv4.
- If something does not work, see [Universal Network Requirements](https://dn42.dev/howto/networksettings) and check the firewall. I once had a problem for several hours and in the end it was the firewall.
- If you are looking for hosting recommendations: [IONOS VPS S](https://www.ionos.com/servers/vps) offers a very cheap small server. (all other offers are overpriced). netcup.de offers cheap servers, but they want a ID card verification.
- Use an uptime monitor to monitor your network. [UptimeRobot](https://uptimerobot.com/) or [HetrixTools](https://hetrixtools.com/) offer free plans. I use [Uptime Kuma](https://github.com/louislam/uptime-kuma) (besides UptimeRobot and HetrixTools), but it is for self hosting.
- bird supports by default 65535:65281 (no-export), 65535:65282 (no-advertise) and 65535:65283 (no-export-subconfed) as community. (see
  [User's guide 2.0 /  6. Protocols / BGP](https://bird.network.cz/?get_doc&v=20&f=bird-6.html#ss6.3) / interpret
  communities switch)
- Both I dn42-pingable.byeob.de and Burble (pingable.burble.dn42, 172.20.129.5, fd42:4242:2601:ac05::1) have pingable addresses which you can use to test your connectivity.
- If you want to peer with some at the beginning: [Automatic Peering](https://dn42.dev/services/Automatic-Peering)
- EuServ offers a free (one-time payment) LXE container at [EUserv / VPS / VS2-free](https://www.euserv.com/en/virtual-private-server/root-vserver/v2/vs2-free.php). I would recommend this only for "playing" with it. (I got it at that time without one-time payment). Otherwise, it's junk.
- If you get the message "Kernel dropped some linknet messages" from bird, you can increase (if you use Debian) `net.core.rmem_default`. I have it for example on 4194304.
- If you want to check something on your network there are several
  services: [Diagnose your DN42 router setup](https://dn42.g-load.eu/diagnose/) (unfortunately currently only IPv4), [burble.dn42 / Shell Accounts](https://burble.dn42/services/shell/) Burble provides a shell for each maintainer, or many Looking Glasses (I don't have any) [Looking Glasses](https://dn42.dev/services/Looking-Glasses)
- If you use bird and have a lot of time, I can recommend to have a look at the documentation: [BIRD 2.0 User's Guide](https://bird.network.cz/?get_doc&f=bird.html&v=20) The same goes for VyOS. For the other daemons I don't know the documentation.
- If you need help, ask on IRC. If no one answers there, ask the Mailling list. For questions, it is best to specify the whole context. If you ask in IRC and want to send a log or something similar, use a paste bin (e. g. [https://paste.dn42.us/ui/](https://paste.dn42.us/ui/), [https://paste.debian.net/](https://paste.debian.net/)) service (otherwise it looks very messy in IRC).
- Subscribe to the Mailling list. It is only conditionally active, but responds to questions actually always within a few days. ([https://dn42.dev/contact#contact_mailing-list](https://dn42.dev/contact#contact_mailing-list))
- Use WireGuard Tunnel to get started. Almost everyone uses it, so you can actually peer with everyone. They are simple. They are secure. Do not use OpenVPN. It is slower and only partially secure. IPsec is an adventure in itself (I haven't tried it yet). Try to avoid GRE as this type of tunnel is not encrypted.
- If you are running Debian, have IPv4 and have Anycast IPv4, it can happen that someone sends an Anycast request to server 1 and the response goes through server 2. If anycast is also configured on server 2, it will discard the packet that it has the IP address itself. To disable this behavior, accept_local must be set to 0 on each dn42 interface:
```sysctl
net.ipv4.conf.all.accept_local=1
net.ipv4.conf.eth0.accept_local=0
```

## bird2 configuration
```
template bgp dnpeers {
    local as OWNAS;

    enable extended messages on;
    graceful restart on;
    long lived graceful restart on;
    interpret communities on;
    prefer older on;

    ipv4 {
        extended next hop on;
        
        import where some_stuff();
        export where some_stuff();
        
        import limit 1000 action block;
        import table on;
    };

    ipv6 {   
        import where some_stuff();
        export where some_stuff();
        
        import limit 1000 action block;
        import table on;
    };
}
```
This is a kind of improved version for bird2. I have omitted the import/export filters.

```
enable extended messages on;
```
Enables the extended messages extension. This causes that 65535 bytes can be transmitted in a BGP packet instead of 4096 bytes. This can be handy for many routes.

```
graceful restart on;
long lived graceful restart on;
```
I can explain this only conditionally well. Roughly it causes that routes are not deleted immediately in case of a BGP session went down. Thus a "peaceful" transition is possible. Existing connections can still be handled via this peering before the route is completely deleted. If the administrator of the peer has restarted the BGP daemon, it ensures that there is no interruption in the forwarding table. I also recommend to implement [BGP Filter Guide / BGP Graceful Shutdown](https://bgpfilterguide.nlnog.net/guides/graceful_shutdown/). This ensures that if a graceful shutdown is detected by the peer, it will try to install another route in the forwarding table. This also gives a "peaceful" transition.

```
interpret communities on;
```
This option is on by default. But I like to write it down again to be sure. It makes bird automatically support 65535:65281 (no-export), 65535:65282 (no-advertise) and 65535:65283 (no-export-subconfed) without having to implement them manually.

```
prefer older on;
```
If two routes are equally good, the router ID is used in the decision process. The route of which the router has the smallest ID is preferred. Personally, I find this decision criterion objectionable. With this option the oldest route is used instead of the lowest router ID. Just because this option is on, bird does not skip the other criteria (bgp_local_pref, bgp_med, AS path length, ...).

```
extended next hop on;
```
This is an extension that allows an IPv6 address to be used as a nexthop for an IPv4 route. If this option is active, no IPv4 addresses have to be negotiated in the tunnel (e.g. WireGuard). This "saves" IPv4 addresses in the tunnel.

```
import limit 1000 action block;
```
This ensures that a maximum of 1000 prefixes may be imported. All after that will be rejected. In dn42 there are about 500-700 prefixes. 1000 are therefore more than enough. It is recommended to enable this option. If the peer has misconfigured something, you can protect yourself with it. This option does not protect against route flapping. Route updates are not affected by the limit.

```
import table on;
```
This will also save discarded routes (not import them). If the filter is updated, the routes can be loaded without the peer having to resend them. For example, if you do ROA filtering and the peer exports a new route that is not yet in your ROA filter, the route remains "saved". If you now update your ROA filter, bird can import the new route without the need for the peer to resend the route.]]>
        </content>
    </entry>
    
    <entry>
        <title>Einstieg in Meshtastic</title>
        
        <link rel="alternate" href="https://mk16.de/blog/einstieg-in-meshtastic/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/einstieg-in-meshtastic/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/einstieg-in-meshtastic/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/einstieg-in-meshtastic/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/einstieg-in-meshtastic/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/einstieg-in-meshtastic/" hreflang="de" />

        <id>/blog/einstieg-in-meshtastic/</id>
        <published>2023-12-04T00:00:00+01:00</published>
        <updated>2023-12-04T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Meine erste Erfahrung mit Meshtastic und ob und wie es möglich ist IP-Tunneling über Meshtastic zu betreiben.</summary>
        <content type="html">
            <![CDATA[Neulich bin ich im Fediverse auf ein neues Projekt gestoßen: Meshtastic
Bei [Meshtastic](https://meshtastic.org/) handelt es sich um ein Peer-to-Peer Mesh-Netzwerk auf Basis von LoRa, einer proprietären Funktechnik.

Die Sache, welche mich besonders bei Meshtastic gelockt hat, ist, dass es [IP-Tunneling](https://github.com/meshtastic/python/issues/410) unterstützt. Ich habe immer wieder Spaß neue Routing- und Netzwerk-Technologien kennenzulernen und ich Meshtastic als eine gute Möglichkeit gesehen, etwas Neues zu lernen.

Um dem Netzwerk beizutreten, braucht man entsprechend ein LoRa-fähiges Gerät mit Meshtastic-Unterstützung. Wenn man nun etwas an einen Knoten senden möchte, sendet man dies in Nachrichten. Jede Nachricht hat einen Channel, auf welchen sie gesendet wird, einen Port, ein TTL und einen Inhalt. Die Nachrichten werden mithilfe von [Protobuf](https://protobuf.dev/) kodiert.

## Nachrichten

### Channel
Meshtastic benutzt zur Separierung [Channels](https://meshtastic.org/docs/configuration/radio/channels). Dabei gibt es einen Standard Channel, welcher öffentlich ist. Es können beliebig viele weitere hinzugefügt werden (bedingt durch Hardware aktuell nur acht). Die Channels haben Namen und, wenn gewünscht, ein Password. Die Nachrichten werden dann mit AES256-CTR (manche Geräte haben dafür auch Hardware-Unterstützung) [verschlüsselt](https://meshtastic.org/docs/overview/encryption). Jedes Gerät, welches an dem Channel teilnimmt und entsprechend auch das Password weiß, kann die Nachricht wieder entschlüsseln.
Des Weiteren wird in einen "primären" und mehrere "sekundäre" Channels unterschieden. Der primäre Channel wird für das Verteilen von Knoteninformationen (Laufzeit, Standort, Akkustand, ...) und für IP-Tunneling verwendet. Auf den sekundären Channels können lediglich Nachrichten ausgetauscht werden.

### Port
Jede Meshtastic-Nachricht beinhaltet des Weiteren einen [Port](https://github.com/meshtastic/python/issues/465#issuecomment-1824842585). Dieser gibt darüber Auskunft, was die Nachricht beinhaltet (Textnachricht, Knoteninformation, IP-Paket, ...)

### TTL
Der Mesh-Algorithmus ist recht einfach und implementiert das Prinzip des "Floodings". Jeder Knoten sendet jedes Paket an seinen benachbarten Knoten. Dieser sendet das Paket dann entsprechend weiter. Bei der Weiterleitung des Paketes wird das TTL um eins reduziert. Das Standardmäßige TTL ist 3 - es kann aber bis auf 7 gesetzt werden. Ein Paket mit einer TTL von 0 wird nicht weitergeleitet.

## Hardware
Ich persönlich bin eher der Software-Mensch statt Hardware. Ich finde, Hardware kann recht unberechenbar sein und das Schlimmste von allem: Es zeigt nicht einmal Fehlermeldungen an.
Ich brauchte also ein recht einfaches Gerät. Da ich IP-Tunneling machen wollte, brauchte ich sogar mindestens zwei. Da ich aktuell Student bin, müssen die beiden Geräte auch günstig sein. Und da ich ein recht unordentlicher Mensch bin, brauche ich auch eine Hülle bzw. ein Gehäuse für die Geräte. Meshtastic bietet auf ihrer Website eine [Liste mit Geräten](https://meshtastic.org/docs/hardware) an. Ich habe mir die Geräte angeschaut und mich schlussendlich für das [Meshtastic Starter Kit](https://store.rakwireless.com/products/wisblock-meshtastic-starter-kit) von [RAKwireless](https://www.rakwireless.com/) entschieden.

![Man kann weiße Tüten mit braunen Schildern darauf sehen. Auf den braunen Schildern steht unter anderem "RAK".](/assets/img/meshtastic-einstieg/gereate_verpackt_von_hinten.jpg)

![Man sieht zwei weiße Tüten. Sie sind vor der Seite des Fotos transparent. In der linken Tüte sieht man einen blauen Zettel, auf welchem eine Platine liegt. In der rechten Tüte sind man etwas Plastik (Batteriehalterung), Pads, welche als Füße fungieren, ein paar Plastik Teile sowie Schrauben.](/assets/img/meshtastic-einstieg/gereate_verpackt_von_vorne.jpg)

Danach kam die Herausforderung des Zusammenbauens. Glücklicherweise bietet RAKwireless eine [Dokumentation](https://docs.rakwireless.com/Product-Categories/Accessories/RAKBox-B5/Installation/) dafür an.

![Man sieht eines der Plastikteile. Es ist unter anderem ein Bereich markiert, in welchem "WisBase" steht.](/assets/img/meshtastic-einstieg/gehause_nackt.jpg)

![Man sieht das Plastikobjekt, wo eine Platine mit Abstandshaltern darauf liegt. An der Platine sind zwei Kabel angebracht, welche wiederum zu jeweils einer kleinen Platine führen (das sind die Antennen).](/assets/img/meshtastic-einstieg/beim_zusammenbauen_von_seite.jpg)

![Man sieht das vorherige Foto aus einer schrägen Perspektive.](/assets/img/meshtastic-einstieg/beim_zusammenbauen_kern_liegt.jpg)

![Man sieht ein transparentes durchsichtiges Acryl-Gehäuse (die Plastikteile von eben) in welchem eine Platine ist. Von der Platine führen zwei Kabel ab, welche jeweils an einer anderen Platine angebracht sind. Die zwei Platinen sind auf dem Boden des Acryl-Gehäuses festgeklebt. Man sieht ein Kabel von der Hauptplatine ausgehen. Auf dieser leuchten zwei LEDs - eine Rote, eine Grüne.](/assets/img/meshtastic-einstieg/eingebaut.jpg)

Nachdem ich die Antennen an dem Board befestigt hatte und in das Gehäuse verschraubt hatte, stellte ich allerdings fest, dass am Gehäuse Schutzfolien waren. Davon war nichts in der Dokumentation erwähnt. Dies bedeutete für mich also, dass ich alles noch einmal abschraubte und wieder zusammenschrauben musste.

Des Weiteren waren nicht genug Schrauben mitgeliefert. Für das Gebäude mit allen möglichen Add-ons braucht man 12 Schrauben, mein Setup ohne Batterie-Halterung braucht 8 Schrauben - mitgeliefert waren aber nur 6 Schrauben.

Ein weiteres Hindernis war die Befestigung der Antennen am Gehäuse. Die Bluetooth-Antenne hat ein kleines Pad mit Kleber, jedoch nicht die LoRa-Antenne. Nachdem ich jedoch wegen der Schutzfolien alles neu zusammengeschraubt war, konnte ich das Klebepad der Bluetooth-Antenne auch nicht mehr verwenden. Ich habe mich dazu entschieden, etwas Heißkleber zu verwenden und die Antennen damit zu verfestigen. Da ich mich nicht mit Hardware auskenne, weiß ich nicht, wie gut diese Idee war - ich weiß nur, dass es bei mir glücklicherweise nichts kaputt gemacht hat.

## Einrichtung

Danach war die Einrichtung der Geräte dran. Meshtastic bietet dazu ein in Python geschriebenes [Kommandozeilen-Tool](https://meshtastic.org/docs/software/python/cli), eine [Weboberfläche](https://meshtastic.org/docs/software/web-client) (die funktionierte bei mir nicht) und eine [Android-App](https://meshtastic.org/docs/category/android-app). Ich habe mich für das Kommandozeilen-Tool entschieden.
Das Gerät wurde jedoch nicht richtig durch das Tool erkannt. Die Android-App konnte mir auch den Grund erklären: Die Firmware ist zu alt.

Ich habe daraufhin ein "Factory Reset" gemacht und danach die aktuelle Firmware hochgeladen. Mit dem "Factory Reset" wollte ich sicherstellen, dass keine alten Konfigurationsschnipsel von RAKwireless da waren.

## IP-Tunneling

Nachdem ich alles eingerichtet habe, war es an der Zeit, die IP-Tunneling-Funktion auszuprobieren:
```
$ sudo meshtastic --tunnel
Connected to radio
INFO file:tunnel.py __init__ line:83 Starting IP to mesh tunnel (you must be root for this *pre-alpha* feature to work).  Mesh members:
INFO file:tunnel.py __init__ line:95 Node !5687b499 has IP address None.180.153
INFO file:tunnel.py __init__ line:95 Node !148bfdc5 has IP address None.253.197
sh: line 1: ifconfig: command not found
Aborting due to: 
```

[Auf meinem Computer war ifconfig nicht installiert](https://github.com/meshtastic/python/issues/467). ifconfig ist ein altes Tool, welches früher zur Netzwerkkonfiguration verwendet wurde. Heutzutage wird allerdings empfohlen, `ip` zu verwenden.

Nachdem ich ifconfig installiert hatte, habe ich das Ganze noch einmal ausprobiert:
```
$ sudo meshtastic --tunnel
Connected to radio
INFO file:tunnel.py __init__ line:83 Starting IP to mesh tunnel (you must be root for this *pre-alpha* feature to work).  Mesh members:
INFO file:tunnel.py __init__ line:95 Node !5687b499 has IP address None.180.153
INFO file:tunnel.py __init__ line:95 Node !148bfdc5 has IP address None.253.197
None.180.153: Unknown host
ifconfig: `--help' gives usage information.
Aborting due to: ifconfig command failed.
```

Scheinbar wird ifconfig aufgefordert die IP-Adresse `None.180.153` zu konfigurieren. [Dies liegt daran, dass die App fehlerhaft ist](https://github.com/meshtastic/python/issues/468). Hintergrund ist, dass das Kommandozeilenargument zur Festlegung des Subnets verwendet wird. Wenn man allerdings kein Subnet angibt, ist das Argument `None`.

Ein möglicher Workaround ist:
```
$ sudo meshtastic --tunnel --subnet 10.115
```

Daraufhin konnte das Interface gestartet werden:
```
$sudo meshtastic --tunnel --subnet 10.115
Connected to radio
INFO file:tunnel.py __init__ line:83 Starting IP to mesh tunnel (you must be root for this *pre-alpha* feature to work).  Mesh members:
INFO file:tunnel.py __init__ line:95 Node !148bfdc5 has IP address 10.115.253.197
INFO file:tunnel.py __init__ line:95 Node !5687b499 has IP address 10.115.180.153
```
```
$ ip address show mesh0
22: mesh0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 200 qdisc fq_codel state UNKNOWN group default qlen 500
    link/none 
    inet 10.115.253.197/16 scope global mesh0
       valid_lft forever preferred_lft forever
```

Ich habe zur Behebung des Fehlers daraufhin eine PR gestellt.

Das Subnet `10.115.0.0/16` ist im privaten Bereich und kann daher eigentlich ohne Probleme verwendet werden. Da das [dn42](https://dn42.dev/Home) und die damit verbundenen Netzwerk jedoch auch Netze in diesem Bereich verwenden, habe ich nach Kollisionen gesucht und leider eine gefunden. Das Freifunk Cuxhaven verwendet ungünstigerweise auch diesen Netzbereich. Da diese jedoch nicht mehr im IC-VPN sind, habe ich eine [PR](https://github.com/freifunk/icvpn-meta/pull/635) erstellt, um das Netzwerk zu entfernen. Mein eigenes dn42-Netzwerk habe ich angewiesen, den Bereich `10.115.0.0/16` als ungültig zu betrachten und damit nicht zu routen.

Ein weiteres Hindernis beim IP-Tunneling mit Meshtastic ist das kleine MTU von 200 Bytes.
Dies verhindert automatisch IPv6, da IPv6 eine MTU von mindestens 1280 Bytes erfordert. Es ist jedoch möglich, IPv6 über IPv4 zu tunneln (GRE, WireGuard, ...).

Für das IP-Tunneling [wird immer der primäre Channel verwendet](https://github.com/meshtastic/python/issues/465). Eine Änderung ist aktuell nicht geplant. Dies bedeutet jedoch, dass eine Verschlüsselung schwierig wird:
- Wenn man einen privaten Channel mit zufälligem Password als primären Channel festlegt, sind die IP-Pakete zwar verschlüsselt, jedoch bekommen andere Knoten keine Knoteninformationen mehr, welche auch über Broadcast verteilt werden.
- Wenn man die IP-Pakete im öffentlichen Standard-Channel tunnelt, teilt und bekommt man zwar weiterhin die Knoteninformationen, die IP-Pakete sind aber nicht verschlüsselt.
    - Eine Möglichkeit dieses Problem zu umgehen und IPv6 zu verwenden, wäre einen WireGuard Tunnel (oder vergleichbares) zu verwenden.

Ein Weiteres Problem beim IP-Tunneling über Meshtastic die die hohe Latenz und der Paketverlust:
```
$ time ping 10.115.253.197 -c 20 -W 600
PING 10.115.253.197 (10.115.253.197) 56(84) bytes of data.
64 bytes from 10.115.253.197: icmp_seq=1 ttl=64 time=12612 ms
64 bytes from 10.115.253.197: icmp_seq=2 ttl=64 time=15457 ms
64 bytes from 10.115.253.197: icmp_seq=4 ttl=64 time=14536 ms
64 bytes from 10.115.253.197: icmp_seq=5 ttl=64 time=14669 ms
64 bytes from 10.115.253.197: icmp_seq=6 ttl=64 time=14803 ms
64 bytes from 10.115.253.197: icmp_seq=8 ttl=64 time=17756 ms
64 bytes from 10.115.253.197: icmp_seq=9 ttl=64 time=19245 ms
64 bytes from 10.115.253.197: icmp_seq=10 ttl=64 time=19234 ms
64 bytes from 10.115.253.197: icmp_seq=11 ttl=64 time=19280 ms
64 bytes from 10.115.253.197: icmp_seq=12 ttl=64 time=19564 ms
64 bytes from 10.115.253.197: icmp_seq=14 ttl=64 time=21571 ms
64 bytes from 10.115.253.197: icmp_seq=16 ttl=64 time=21286 ms
64 bytes from 10.115.253.197: icmp_seq=17 ttl=64 time=21525 ms
64 bytes from 10.115.253.197: icmp_seq=20 ttl=64 time=29039 ms

--- 10.115.253.197 ping statistics ---
20 packets transmitted, 14 received, 30% packet loss, time 19209ms
rtt min/avg/max/mdev = 12611.611/18612.706/29038.582/4040.933 ms, pipe 16

real	0m50.196s
user	0m0.007s
sys	0m0.068s
```

Auch eine UDP-Übertragung war auch problemlos möglich:
```
$ ncat --verbose --wait 10m --udp 10.115.180.153 8886
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Connected to 10.115.180.153:8886.
Hello World!
^C
```
```
$ ncat --listen --verbose --udp --source-port 8886
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:8886
Ncat: Listening on 0.0.0.0:8886
Ncat: Connection from 10.115.253.197:47920.
Hello World!
^C
```

Eine einfache TCP-Verbindung war jedoch nicht möglich. Somit kann Meshtastic auch nicht für das dn42 eingesetzt werden. Einfache Chat-Anwendungen, welche auf UDP basieren und hohe Latenzen vertragen, sollten allerdings möglich sein.
```
$ ncat --verbose --wait 10m 10.115.180.153 8886
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Connection timed out.
```

![Man kann einen Ausschnitt aus WireShark sehen. Es zeigt eine einige TCP retransmissions zwischen 10.115.180.153 und 10.115.253.197 an.](/assets/img/meshtastic-einstieg/wireshark_tcp_retransmission.png)]]>
        </content>
    </entry>
    
    <entry>
        <title>FLOSS communities: Meshtastic</title>
        
        <link rel="alternate" href="https://mk16.de/blog/floss-communities-meshtastic/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/floss-communities-meshtastic/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/floss-communities-meshtastic/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/floss-communities-meshtastic/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/floss-communities-meshtastic/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/floss-communities-meshtastic/" hreflang="en" />

        <id>/blog/floss-communities-meshtastic/</id>
        <published>2026-02-13T00:00:00+01:00</published>
        <updated>2026-02-13T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>A review of the meshtastic community</summary>
        <content type="html">
            <![CDATA[Over the years, I have had various experiences in FLOSS communities. My experiences range from very pleasantly surprised to catastrophically negative.
I personally mostly acted as a requestor, reporting bugs or creating feature requests.

And since the behavior of one or the other really upsets me internally, why not write about it?

Meshtastic is a great project in itself. Meshtastic is a mesh network that connects nodes via LoRa (radio technology). It enables the exchange of messages, but also IP tunneling. I've even written a [blog post](/blog/meshname-en/) about Meshtastic itself.

I belong to the IPv6 enthusiasts group. Accordingly, I notice when something does not support IPv6, and the great thing about FLOSS projects is that you can and are allowed to express your desire for it in the form of a feature request... at least in theory.

Meshtastic consists of several parts - first, the firmware that runs on the LoRa devices and a client. The client can connect via Bluetooth or IP. There is also meshtasticd, a Linux server application that provides Meshtastic if the server supports LoRa (for example, using a hat on the Pi).

Tristan, who is the founder of CRXN and with whom I had connected CRXN to dn42, had already noticed the lack of IPv6 support in the firmware and opened a feature request - interestingly, completely independently of me.: [[Feature Request]: IPv6 SLAAC support #7951](https://github.com/meshtastic/firmware/issues/7951)

The only thing missing from the Android app is IPv6, right? And why not take this opportunity to add support for domain names as well?

I have already summarized the events in a discussion on the Meshtastic discussion forum on GitHub:
> Hi, maybe someone can help me. It's about an issue I created for the Meshtastic app. It was repeatedly closed for no reason (or rather, a reason was given, but in my opinion it wasn't valid). I would ask about it in the issue, but that was also blocked for no reason. Is this normal? Could someone unblock it? [[Feature Request]: Meshtastic App only supports legacy IPv4 and does not support IPv6 or domain names #4190](https://github.com/meshtastic/Meshtastic-Android/issues/4190)
>
> To elaborate a little further (assuming this discussion isn't also blocked):
> 1. I noticed a lack of IPv6 and domain name support in the Android Meshtastic app. Since I assumed that the lack of IPv6 support in 2026 was a bug, I opened a bug report: [https://github.com/meshtastic/Meshtastic-Android/issues/4156](https://github.com/meshtastic/Meshtastic-Android/issues/4156)
> 2. AAA then pointed out that, in the maintainers' view, this falls more into the category of a feature than a bug. Accordingly, I opened an FR: [https://github.com/meshtastic/Meshtastic-Android/issues/4157](https://github.com/meshtastic/Meshtastic-Android/issues/4157)
> 3. I had expected a constructive discussion afterwards and, somewhat optimistically, a low-priority FR that would simply remain unresolved for some time.
> 4. BBB then said that IPv6 support in the Meshtastic app was useless because the firmware does not support IPv6. That makes sense, but Meshtastic now also offers the option of running meshtasticd on Linux, and Linux has IPv6. This means that meshtasticd is also compatible with IPv6. My personal use case would be to connect to my meshtasticd node via IPv6.
> 5. I had requested that the issue and thus the FR be reopened. Instead, the issue was closed. For no reason. Somewhat confusing. I am one of those people who likes to know the background to things, and when people ignore me, I can be persistent. Perhaps typically autistic.
Since I couldn't ask any questions (as the issue was locked), I opened a new issue: [https://github.com/meshtastic/Meshtastic-Android/issues/4189](https://github.com/meshtastic/Meshtastic-Android/issues/4189)
> 6. However, this issue was closed and locked as a duplicate without any comments. It is indeed a duplicate, but my original question still hasn't been answered. A major disadvantage that I personally see in such an issue drama is that clarity is lost (because you have to click through several issues). Since I still had no reason, I created a new issue: [https://github.com/meshtastic/Meshtastic-Android/issues/4190](https://github.com/meshtastic/Meshtastic-Android/issues/4190)
> 7. In this issue, CCC repeated the same argument about the firmware - but did not respond to my objection about meshtasticd. So there is still no reasonable reason to close the issue.
> 8. AAA closed this issue, but unfortunately without giving a reason. Since @AAA seems nice, I asked them about the reason. He responded with the firmware argument, but without addressing my counterargument.
> 9. Then DDD threatened me and the issue was locked.

> This raises several questions for me: What is the reason? Why was the issue closed in the first place? Why was the issue locked? This makes communication difficult.
> I could imagine that meshtasticd is based on the firmware and therefore cannot support IPv6 because the firmware cannot. However, this was not stated, and since I am not a Meshtastic developer, I cannot confirm this.
> I could imagine that the issue was closed prematurely and now no one wants to admit that they were wrong.
> I could imagine that I may have overlooked an important aspect – but I don't know what it is and I can't ask because the issue has been locked.

> Something I did poorly, and which may have been overlooked as a result, is that my original FR also expressed the desire for domain names to be supported. Domain names also support IPv4. Despite my inquiry, this aspect was not addressed. Should I have made this into two separate FRs? Probably. However, I am afraid to open an FR for domain names due to the threat from DDD.
> Another option would have been to ask for the reason on Discord. However, I belong to the group of people who believe that discussions belong in the right place (in this case, the issue tracker). Furthermore, I don't like Discord very much because of its proprietary nature.

> I have replaced the names with letters.

An IPv6 enthusiast wrote in response:
> It's unbelievable. A "mesh" without the right protocol .

A person unknown to me replies:
> At this point you are spamming/harassing over this issue. The question has been asked and answered repeatedly. 7 day timeout.

I actually received an email from GitHub in response:
> Hi Marek Küthe,

> A maintainer of the @meshtastic organization has blocked you for 7 days because of this content: [https://github.com/orgs/meshtastic/discussions/445]( Issues are closed and locked without reason #445 ).

> For more information please see, [https://github.com/meshtastic/Discussions/blob/master/CODE_OF_CONDUCT.md](https://github.com/meshtastic/Discussions/blob/bc5dda200df8c21418bfe84ed3b54cf570baf8b0/CODE_OF_CONDUCT.md), orcontact the maintainer at contact@meshtastic.org.

> Thanks,
> The GitHub Team

Meshtastic also has a Discord server. I asked there as well. I was told that meshtasticd is not very popular. Another user noted that domain name support already exists in the Apple app - which I found interesting. After a few minutes, however, I received the following message: "Timed Out You can not chat, reply, react, or join voice channels during timeout."

On the positive side, I can still read along on both GitHub and Discord.

One standard that Meshtastic says it tries to implement is the [Contributor Covenant Code of Conduct](https://github.com/meshtastic/Discussions/blob/master/CODE_OF_CONDUCT.md). This is a template with rules of conduct that I actually use myself.
It actually states:

> 1. Correction
> Community Impact: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
>
> Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.

The first thing that happens when you violate the community rules, and that is what I am accused of in the email, would be an explanation of what I did wrong. And, if necessary, a demand for an apology from me. Personally, I could not find any explanation in the communication history of what I did wrong.

I then wrote to one of the moderators on Discord. A few years ago, there was a discussion about the use of "Meshtastic" name without the addition of "Local Group" or similar, and thus whether unofficial Meshtastic user groups were allowed to call themselves "Meshtastic" as this could give the impression that they were officially affiliated with Meshtastic. This particular moderator made a positive impression on me at the time, as he tried to de-escalate the situation and approach the matter objectively.
After some back and forth, the situation turned out as follows (in my own words):
It was a communication problem. What the maintainers actually wanted to say was:
> IPv6 has little to no priority. We currently have no interest in implementing IPv6 ourselves, nor do we foresee doing so in the foreseeable future.

And even though I may not like to hear this answer, it is a fact that maintainers are not obligated to anyone.
Instead, I was expected to read "between the lines" - which is an impossible task for me as an autistic person.

This was a very negative aspect of the Meshtastic community, or rather some of the maintainers who represent the community. Nevertheless, there are also very positive aspects:
- There are also maintainers in the Meshtastic community who are easy to talk to and who can correctly classify issues (after some back and forth): [https://github.com/meshtastic/firmware/issues/9170]([Bug]: meshtastic --export-config hangs on native meshtasticd #9170)
  Here, I was even given valuable tips on how to use X11 forwarding without an X11 server on the server, or a workaround to get around the bug.
- Another example, with a beginner's suggestion from me, was moderated better - there, arguments were made instead of locking, so I realized myself that my feature request didn't add much value and closed it myself: [https://github.com/meshtastic/firmware/issues/9195]([Feature Request]: New role between ROUTER and CLIENT_BASE / Role that also forwards broadcast messages #9195)
- An issue was closed very shortly after it was created, with no moderation necessary, as an maintainer was extremely quick to fix the bug: [https://github.com/meshtastic/Meshtastic-Android/issues/4149]([Bug]: Copying and pasting node attributes is no longer possible after the latest update #4149)
- In another case, when I found the bot's response strange, I received friendly assistance: [https://github.com/meshtastic/Meshtastic-Android/issues/4150]([Bug]: -0 degrees doesn't make sense, show decimal places? #4150)

Therefore: The majority of issue handling was very friendly and, in some cases, even very professional. Unfortunately, however, and I am no exception here, the negative aspects often overshadow the positive ones.

Update: As of today (February 13, 2026), I have not received a reply to the email I answered.]]>
        </content>
    </entry>
    
    <entry>
        <title>FLOSS communities: WireGuard</title>
        
        <link rel="alternate" href="https://mk16.de/blog/floss-communities-wireguard/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/floss-communities-wireguard/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/floss-communities-wireguard/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/floss-communities-wireguard/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/floss-communities-wireguard/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/floss-communities-wireguard/" hreflang="en" />

        <id>/blog/floss-communities-wireguard/</id>
        <published>2026-01-12T00:00:00+01:00</published>
        <updated>2026-01-12T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>A negative review of the WireGuard community, particularly its main developer.</summary>
        <content type="html">
            <![CDATA[Over the years, I have had various experiences in FLOSS communities. My experiences range from very pleasantly surprised to catastrophically negative.
I personally mostly acted as a requestor, reporting bugs or creating feature requests.

And since the behavior of one or the other really upsets me internally, why not write about it?

I have started developing UnCiv mods. The UnCiv developers make this very easy by declaring the new units, nations, and items as JSON5 files. As part of this, I also created the “Logicians” mod. In it, I incorporated several FLOSS programs as units. For example, Docker's Moby mascot is available as a ship unit. Since I use WireGuard a lot, among other things in the context of dn42, I also wanted to include the WireGuard dragon as a unit. My only question was, what is it called? I searched the internet and looked, but couldn't find an answer. So I thought, if you don't know something, you can always ask. No sooner said than done, I wrote an email to the WireGuard mailing list. As with many mailing lists, I received an email saying “Your message to WireGuard awaits moderator approval.” However, I received a rejection from the moderator without any meaningful explanation (unfortunately, I seem to have deleted the email and can no longer find the exact wording).
Maybe it was a mistake? So I sent the email again—with the same result.
However, other people didn't seem to have any problem sending emails to the mailing list.
So I simply picked a few people who had successfully sent an email and asked if they could forward my question. Fortunately, someone was kind enough to do so.

https://lists.zx2c4.com/pipermail/wireguard/2023-July/008108.html
> Hi,
> 
> I was asked to forward this to the list because the author has
> problems reaching it.
> It looks legit:
> 
> ---------- Forwarded message ---------
> From: Marek Küthe <m.k at mk16.de>
> 
> Hello,
> 
> the WireGuard logo is a Chinese dragon. Does he/she have a name? What
> could you call him/her? The background is that I want to make a mod for
> [UnCiv](https://github.com/yairm210/Unciv) and would like to incorporate
> there various motifs of computer life - among others, the dragon of
> WireGuard. Therefore he/she needs - at least for my purposes - a name.
> What would you call it?
> 
> --
> Marek Küthe
> m.k at mk16.de
> er/ihm he/him

In fact, there was someone who kindly responded and wanted to help me:
> Hi,
> 
> I don't know the origin of the Wireguard logo, but it seems to be a 蛟
>  (Jiao, pronounced j-ee-au), not a 龙 (Long)
> 
> The differences between Long (know as Chinese dragon) and Jiao is
> * Jiao has 2 small straight horns or no horn at all while Long has 2 bigger
>   and forky horns
> * Long has beard while Jiao has no beard
> * Jiao has snake like tail while Long has fish like tail (with round fin)
> * Long has 4 claws while Jiao has only 2 claws (foreclaws), but this feature
>   is usually misused.
> 
> Generally, Jiao is upgraded/developed snake living in rivers; Long is
> upgraded/developed Jiao living in the sea, and Long can fly into the sky.
> 
> Of course you can call the logo  wireguard dragon as you wish.

Then came the main developer of WireGuard, who had already rejected my emails:
> It is absolutely none of those things at all. Please stop speculating
> and keep discussions on this here technical.

Unfortunately, that did not answer my question.

Someone else answered my question, albeit with a noteworthy undertone:
> I mean, the speculating muppets couuld have at least Googled it !
> 
> For the record, for people who are too lazy to Google Jason already answered the question: https://www.reddit.com/r/linux/comments/hzyu8j/comment/fznp3md/

Apparently, I have mutated into a "speculating muppet" or have been given this title (at least if life were a game).

On the other hand, my question was fortunately answered.

In fact, I didn't find this forum post during my search. For a moment, I felt very incompetent at online research.

In retrospect, another person also wrote to me that the WireGuard developer is known for not necessarily being easy to get along with.]]>
        </content>
    </entry>
    
    <entry>
        <title>Geldbetrug - Chat mit &quot;aaronov.&quot;</title>
        
        <link rel="alternate" href="https://mk16.de/blog/geldbetrug/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/geldbetrug/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/geldbetrug/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/geldbetrug/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/geldbetrug/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/geldbetrug/" hreflang="de" />

        <id>/blog/geldbetrug/</id>
        <published>2021-10-18T01:00:00+02:00</published>
        <updated>2021-10-18T01:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        
        <content type="html">
            <![CDATA[Wenn man sich eine Wallet, egal ob Hardware oder Sotware, erstellt, bekommt man entweder 12 oder 24 Wörter. Diese werden als „Recovery Phrase“, „Seed Words“ oder ähnliches bezeichnet. Diese Wörter werden dazu benutzt, um seine endgültigen Schlüssel für seine Wallet zu erstellen. Jeder der Zugriff auf die Wörter hat, hat Zugriff auf das Geld. Man sollte diese Wörter niemals und unter keinen Umständen an dritte Weitergeben.
Dennoch versuchen Betrüger häufig an diese Wörter zu kommen. Da ich mich persönlich sehr für die Blockchain-Technologie interessiere, bin ich auf Discord entsprechenden Channeln beigetreten. Teilweise habe ich irgendwelche lapidare Fragen gestellt oder habe auch nichts getan. Dennoch habe mich einige Betrüger angeschrieben. Diese versuchen dann an meine Wörter zu kommen. Ich habe mir dann für den Chat ein paar neue Wörter generiert und einige Cents darauf überwiesen. Diese Wörter habe ich dann den Betrügern gegeben.


> User: Hallo @mark22k
wie können wir Ihnen helfen?

> Ich: Mich würde interessieren, ob Solflare Open Source ist und wenn ja, wo kann man den Quellcode einsehen kann? [Dies war die eigentliche Frage, weshalb ich dem Discord Server beigetreten bin]

> User: Sie müssen zuerst Ihre Brieftasche validieren, Sir 

> Ich: Wie macht man das?
> User: https://dappswallet.webflow.io/.
Scrollen Sie nach unten, um Ihre Brieftasche zu verbinden
2: Wählen Sie Solflare
3: Bestätigen Sie Ihre Brieftasche
4: Folgen Sie den Anweisungen 
5: Lassen Sie es validieren
Lass mich wissen, wenn du fertig bist

> Ich: [Nachdem ich dies getan habe (mit den extra dafür generierten Wörtern). Erschien ein QR-Code. Diesen habe ich dem User gesendet]
Jetzt erscheint dieses Bild.

> User: Sie müssen jetzt eine Einzahlung auf Sol vornehmen, Sir.
Dies müssen Sie für den Start tun? [Zu diesem Zeitpunkt hatte ich zwar die Wörter erstellt, allerdings kein Guthaben auf das Konto gesendet]

> Ich: Ich habe nun einige Sol an die Adresse Ds1DReXKqrhsEdUgrXsuLMDHyz2bDnSRqkdHM7N6kw8j übermittelt, damit wir die Validierung abschließen können.

> User: Dies ist nicht die Brieftasche, die Sie validiert haben, Sir
FMK9QWsdHxDRN2sqP9ktUNAsvnxuKaPEhhQ9EZpiVGVu ist die Adresse zu Ihrer Wallet.
Auf Ds1DReXKqrhsEdUgrXsuLMDHyz2bDnSRqkdHM7N6kw8j wirst du keinen Zugriff haben.
[Woran liegt es, dass der Betrüger und Ich zwei verschiedene Adressen raus hatten? Naja, aus den 12 bzw. 24 Wörtern wird eine Adresse generiert. Es ist allerdings praktischer mehrere Adresse aus diesen Wörter zu erstellen. Dafür gibt es sogenannte "Pfade". Ich habe den Pfad von Solflare (einer Solana Wallet) benutzt und er von sollet.io (einer anderen Solana Wallet). Warum er den Pfad von sollet.io benutzt hat, weiß ich nicht; besonders nicht vor dem Hintergrund das er wusste, dass ich Solflare benutze.]

> Ich: Um genau zu sein, habe ich Zugang zu beiden Adressen. Allerdings verwendet Solflare einen anderen Pfad als sollet.io. Ich habe den Pfad von Solflare verwendet und Sie haben den Pfad von sollet.io verwendet ...
Vielen Dank für Ihre Hilfe bei der Validierung der Wallet.

> User: Gern geschehen, Sie können jetzt Ihre Brieftasche benutzen.
Einen schönen Tag noch, Sir.


Wäre ich wirklich darauf reingefallen, hätte ich vermutlich mein ganzes Geld verloren. Ich vermute, dass der Betrüger darauf gewartet hätte, dass ich eine höhrere Einzahlung vornehme und dann hätte der Betrüger das Geld auf sein eigenes Konto überwiesen. Da Blockchains dezentral sind und es keine zentrale Verwaltung gibt, hätte ich mein Geld endgültig verloren.

Dies ist eine Übersetzung des Gespräches, welches ich auf Englisch mit dem Betrüger geführt hatte. Orginalgespräch: [geldbetrug_aaronov.txt](/assets/docs/geldbetrug_aaronov.txt)]]>
        </content>
    </entry>
    
    <entry>
        <title>Getting started with Meshtastic</title>
        
        <link rel="alternate" href="https://mk16.de/blog/getting-started-with-meshtastic/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/getting-started-with-meshtastic/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/getting-started-with-meshtastic/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/getting-started-with-meshtastic/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/getting-started-with-meshtastic/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/getting-started-with-meshtastic/" hreflang="en" />

        <id>/blog/getting-started-with-meshtastic/</id>
        <published>2023-12-04T00:00:00+01:00</published>
        <updated>2023-12-04T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>My first experience with Meshtastic and if and how it is possible to do IP tunneling via Meshtastic.</summary>
        <content type="html">
            <![CDATA[I recently came across a new project in Fediverse: Meshtastic
[Meshtastic](https://meshtastic.org/) is a peer-to-peer mesh network based on LoRa, a proprietary wireless technology.

The thing that particularly attracted me to Meshtastic is that it supports [IP tunneling](https://github.com/meshtastic/python/issues/410). I always enjoy learning about new routing and networking technologies and I saw Meshtastic as a great way to learn something new.

To join the network, you need a LoRa-enabled device with Meshtastic support. If you want to send something to a node, you send it in messages. Each message has a channel on which it is sent, a port, a TTL and a content. The messages are encoded using [Protobuf](https://protobuf.dev/).

## Messages

### Channel
Meshtastic uses [Channels](https://meshtastic.org/docs/configuration/radio/channels) for separation. There is a standard channel, which is public. Any number of additional channels can be added (currently only eight due to hardware). The channels have names and, if desired, a password. The messages are then [encrypted](https://meshtastic.org/docs/overview/encryption) using AES256-CTR (some devices also have hardware support for this). Any device that participates in the channel and knows the password can decrypt the message.
Furthermore, a distinction is made between a "primary" and several "secondary" channels. The primary channel is used for distributing node information (runtime, location, battery status, etc.) and for IP tunneling. Only messages can be exchanged on the secondary channels.

### Port
Each Meshtastic message also contains a [port](https://github.com/meshtastic/python/issues/465#issuecomment-1824842585). This provides information about what the message contains (text message, node information, IP packet, ...)

### TTL
The mesh algorithm is quite simple and implements the principle of "flooding". Each node sends each packet to its neighboring node. This node then forwards the packet accordingly. When the packet is forwarded, the TTL is reduced by one. The default TTL is 3 - but it can be set up to 7. A packet with a TTL of 0 is not forwarded.


## Hardware
Personally, I'm more of a software person than a hardware person. I find hardware can be quite unpredictable and worst of all, it doesn't even display error messages.
So I needed a fairly simple device. Since I wanted to do IP tunneling, I even needed at least two. As I'm currently a student, the two devices also had to be inexpensive. And since I'm a rather messy person, I also need a cover or case for the devices. Meshtastic offers a [list of devices](https://meshtastic.org/docs/hardware) on their website. I had a look at the devices and finally decided on the [Meshtastic Starter Kit](https://store.rakwireless.com/products/wisblock-meshtastic-starter-kit) from [RAKwireless](https://www.rakwireless.com/).

![You can see white bags with brown signs on them. The brown signs say "RAK", among other things.](/assets/img/meshtastic-einstieg/gereate_verpackt_von_hinten.jpg)

![You can see two white bags. They are transparent in front of the side of the photo. In the left bag you can see a blue piece of paper. There is a circuit board on top of it. In the right bag you can see some plastic (battery holder), pads that act as feet, a few plastic parts and screws](/assets/img/meshtastic-einstieg/gereate_verpackt_von_vorne.jpg)

Then came the challenge of assembly. Fortunately, RAKwireless offers [documentation](https://docs.rakwireless.com/Product-Categories/Accessories/RAKBox-B5/Installation/) for this.

![You can see one of the plastic parts. Among other things, there is an area marked with "WisBase".](/assets/img/meshtastic-einstieg/gehause_nackt.jpg)

![You can see the plastic object with a circuit board with spacers on it. Two cables are attached to the board, which in turn lead to a small board (these are the antennas).](/assets/img/meshtastic-einstieg/beim_zusammenbauen_von_seite.jpg)

![You can see the previous photo from an oblique perspective.](/assets/img/meshtastic-einstieg/beim_zusammenbauen_kern_liegt.jpg)

![You can see a transparent acrylic housing (the plastic parts from before) in which there is a circuit board. Two cables lead from the circuit board, each of which is attached to a different circuit board. The two circuit boards are glued to the bottom of the acrylic housing. You can see a cable leading from the main board. Two LEDs light up on it - one red, one green.](/assets/img/meshtastic-einstieg/eingebaut.jpg)

After I had attached the antennas to the board and screwed them into the housing, I noticed that there were protective films on the housing. None of this was mentioned in the documentation. This meant that I had to unscrew everything again and screw it back together.

Furthermore, not enough screws were included. For the building with all possible add-ons you need 12 screws, my setup without battery holder needs 8 screws - but only 6 screws were supplied.

Another obstacle was attaching the antennas to the housing. The Bluetooth antenna has a small pad with adhesive, but not the LoRa antenna. However, after I had reassembled everything because of the protective film, I could no longer use the adhesive pad for the Bluetooth antenna. I decided to use some hot glue and fix the antennas with it. Since I'm not familiar with hardware, I don't know how good this idea was - I just know that it didn't break anything in my case, fortunately.

## Setup

Then it was time to set up the devices. Meshtastic offers a [command line tool](https://meshtastic.org/docs/software/python/cli) written in Python, a [web interface](https://meshtastic.org/docs/software/web-client) (which didn't work for me) and an [Android app](https://meshtastic.org/docs/category/android-app). I opted for the command line tool.
However, the device was not recognized correctly by the tool. The Android app was also able to explain the reason to me: The firmware is too old.

I then carried out a "factory reset" and then uploaded the latest firmware. With the factory reset, I wanted to make sure that there were no old configuration snippets from RAKwireless.

## IP tunneling

Once I had set everything up, it was time to try out the IP tunneling function:
```
$ sudo meshtastic --tunnel
Connected to radio
INFO file:tunnel.py __init__ line:83 Starting IP to mesh tunnel (you must be root for this *pre-alpha* feature to work).  Mesh members:
INFO file:tunnel.py __init__ line:95 Node !5687b499 has IP address None.180.153
INFO file:tunnel.py __init__ line:95 Node !148bfdc5 has IP address None.253.197
sh: line 1: ifconfig: command not found
Aborting due to: 
```

[ifconfig was not installed on my computer](https://github.com/meshtastic/python/issues/467). ifconfig is an old tool that used to be used for network configuration. Nowadays, however, it is recommended to use `ip`.

After I had installed ifconfig, I tried the whole thing again:
```
$ sudo meshtastic --tunnel
Connected to radio
INFO file:tunnel.py __init__ line:83 Starting IP to mesh tunnel (you must be root for this *pre-alpha* feature to work).  Mesh members:
INFO file:tunnel.py __init__ line:95 Node !5687b499 has IP address None.180.153
INFO file:tunnel.py __init__ line:95 Node !148bfdc5 has IP address None.253.197
None.180.153: Unknown host
ifconfig: `--help' gives usage information.
Aborting due to: ifconfig command failed.
```

Apparently ifconfig is prompted to configure the IP address `None.180.153`. [This is because the app is faulty](https://github.com/meshtastic/python/issues/468). The background is that the command line argument is used to specify the subnet. However, if no subnet is specified, the argument is `None`.

A possible workaround is:
```
$ sudo meshtastic --tunnel --subnet 10.115
```

The interface could then be started:
```
$sudo meshtastic --tunnel --subnet 10.115
Connected to radio
INFO file:tunnel.py __init__ line:83 Starting IP to mesh tunnel (you must be root for this *pre-alpha* feature to work).  Mesh members:
INFO file:tunnel.py __init__ line:95 Node !148bfdc5 has IP address 10.115.253.197
INFO file:tunnel.py __init__ line:95 Node !5687b499 has IP address 10.115.180.153
```
```
$ ip address show mesh0
22: mesh0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 200 qdisc fq_codel state UNKNOWN group default qlen 500
    link/none 
    inet 10.115.253.197/16 scope global mesh0
       valid_lft forever preferred_lft forever
```

The subnet `10.115.0.0/16` is in the private area and can therefore actually be used without any problems. However, since the [dn42](https://dn42.dev/Home) and the networks connected to it also use networks in this range, I looked for collisions and unfortunately found one. Unfortunately, Freifunk Cuxhaven also uses this network range. However, since they are no longer in the IC-VPN, I created a [PR](https://github.com/freifunk/icvpn-meta/pull/635) to remove the network. I have instructed my own dn42 network to consider the range `10.115.0.0/16` as invalid and therefore not to route it.

Another obstacle to IP tunneling with Meshtastic is the small MTU of 200 bytes.
This automatically prevents IPv6, as IPv6 requires an MTU of at least 1280 bytes. However, it is possible to tunnel IPv6 via IPv4 (GRE, WireGuard, ...).


For IP tunneling [the primary channel is always used](https://github.com/meshtastic/python/issues/465). No change is currently planned. However, this means that encryption will be difficult:
- If you specify a private channel with a random password as the primary channel, the IP packets are encrypted, but other nodes no longer receive any node information, which is also distributed via broadcast.
- If you tunnel the IP packets in the public standard channel, you still share and receive the node information, but the IP packets are not encrypted.
    - One way to get around this problem and use IPv6 would be to use a WireGuard tunnel (or similar).

Another problem with IP tunneling via Meshtastic is the high latency and packet loss:
```
$ time ping 10.115.253.197 -c 20 -W 600
PING 10.115.253.197 (10.115.253.197) 56(84) bytes of data.
64 bytes from 10.115.253.197: icmp_seq=1 ttl=64 time=12612 ms
64 bytes from 10.115.253.197: icmp_seq=2 ttl=64 time=15457 ms
64 bytes from 10.115.253.197: icmp_seq=4 ttl=64 time=14536 ms
64 bytes from 10.115.253.197: icmp_seq=5 ttl=64 time=14669 ms
64 bytes from 10.115.253.197: icmp_seq=6 ttl=64 time=14803 ms
64 bytes from 10.115.253.197: icmp_seq=8 ttl=64 time=17756 ms
64 bytes from 10.115.253.197: icmp_seq=9 ttl=64 time=19245 ms
64 bytes from 10.115.253.197: icmp_seq=10 ttl=64 time=19234 ms
64 bytes from 10.115.253.197: icmp_seq=11 ttl=64 time=19280 ms
64 bytes from 10.115.253.197: icmp_seq=12 ttl=64 time=19564 ms
64 bytes from 10.115.253.197: icmp_seq=14 ttl=64 time=21571 ms
64 bytes from 10.115.253.197: icmp_seq=16 ttl=64 time=21286 ms
64 bytes from 10.115.253.197: icmp_seq=17 ttl=64 time=21525 ms
64 bytes from 10.115.253.197: icmp_seq=20 ttl=64 time=29039 ms

--- 10.115.253.197 ping statistics ---
20 packets transmitted, 14 received, 30% packet loss, time 19209ms
rtt min/avg/max/mdev = 12611.611/18612.706/29038.582/4040.933 ms, pipe 16

real	0m50.196s
user	0m0.007s
sys	0m0.068s
```

UDP transmission was also possible without any problems:
```
$ ncat --verbose --wait 10m --udp 10.115.180.153 8886
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Connected to 10.115.180.153:8886.
Hello World!
^C
```
```
$ ncat --listen --verbose --udp --source-port 8886
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:8886
Ncat: Listening on 0.0.0.0:8886
Ncat: Connection from 10.115.253.197:47920.
Hello World!
^C
```

However, a simple TCP connection was not possible. This means that Meshtastic cannot be used for the dn42. However, simple chat applications that are based on UDP and tolerate high latencies should be possible.
```
$ ncat --verbose --wait 10m 10.115.180.153 8886
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Connection timed out.
```

![You can see an excerpt from WireShark. It shows some TCP retransmissions between 10.115.180.153 and 10.115.253.197.](/assets/img/meshtastic-einstieg/wireshark_tcp_retransmission.png)]]>
        </content>
    </entry>
    
    <entry>
        <title>Join Google Mailling List without Google Account</title>
        
        <link rel="alternate" href="https://mk16.de/blog/google-mailling-list/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/google-mailling-list/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/google-mailling-list/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/google-mailling-list/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/google-mailling-list/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/google-mailling-list/" hreflang="en" />

        <id>/blog/google-mailling-list/</id>
        <published>2023-05-15T01:00:00+02:00</published>
        <updated>2023-05-15T01:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>How to join a Google Mailling list without a Google Account? Yes, it works and it&apos;s easy.</summary>
        <content type="html">
            <![CDATA[This is actually easier than you think. However, since I found the solution only after a long search on the Internet, I write it here briefly:
One sends an e-mail to: `[group name]+subscribe@googlegroups.com`. This can be empty.
After that you will receive an e-mail in which you have to confirm your membership. You can do this by replying to the e-mail or by sending an e-mail to `[group name]+subconfirm@googlegroups.com`.]]>
        </content>
    </entry>
    
    <entry>
        <title>Install AmneziaWG kernel module on Debian</title>
        
        <link rel="alternate" href="https://mk16.de/blog/install-amneziawg-kernel-module-on-debian/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/install-amneziawg-kernel-module-on-debian/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/install-amneziawg-kernel-module-on-debian/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/install-amneziawg-kernel-module-on-debian/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/install-amneziawg-kernel-module-on-debian/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/install-amneziawg-kernel-module-on-debian/" hreflang="en" />

        <id>/blog/install-amneziawg-kernel-module-on-debian/</id>
        <published>2026-02-01T00:00:00+01:00</published>
        <updated>2026-02-01T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Brief installation guide on how to install the AmneziaWG kernel module on Debian 13 (deb822) or Debian 12 (traditional package format).</summary>
        <content type="html">
            <![CDATA[[AmneziaWG](https://amnezia.org/) is an extension of the WireGuard protocol to overcome DPI.

AmneziaWG also offers a [kernel module](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module) for Linux, allowing you to use the same speed distribution as with Vanilla WireGuard.
Unfortunately, the official installation guide uses outdated methods, and [my PR](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/pull/27) to update them was rejected, even though the arguments for the rejection are debatable. Therefore, here is a blog entry on how to add AmneziaWG as a Debian repository. These instructions are not official.

Debian 13 (Trixie) / deb822:
```
$ sudo apt-get install --yes gnupg2 apt-transport-https
$ gpg --keyserver keyserver.ubuntu.com --recv-keys 75c9dd72c799870e310542e24166f2c257290828
$ gpg --export 75c9dd72c799870e310542e24166f2c257290828 | sudo tee /usr/share/keyrings/amnezia.gpg > /dev/null
sudo tee /etc/apt/sources.list.d/amnezia.sources <<EOF
Types: deb deb-src
URIs: https://ppa.launchpadcontent.net/amnezia/ppa/ubuntu
Suites: focal
Components: main
Signed-By: /usr/share/keyrings/amnezia.gpg
EOF
$ sudo apt-get update
$ sudo apt-get install --yes amneziawg amneziawg-tools
```

Debian 12 (Bookworm) / traditional package format:
```
$ sudo apt-get install --yes gnupg2 apt-transport-https
$ gpg --keyserver keyserver.ubuntu.com --recv-keys 75c9dd72c799870e310542e24166f2c257290828
$ gpg --export 75c9dd72c799870e310542e24166f2c257290828 | sudo tee /usr/share/keyrings/amnezia.gpg > /dev/null
$ echo "deb [signed-by=/usr/share/keyrings/amnezia.gpg] https://ppa.launchpadcontent.net/amnezia/ppa/ubuntu focal main" | sudo tee -a /etc/apt/sources.list.d/amnezia.list
$ echo "deb-src [signed-by=/usr/share/keyrings/amnezia.gpg] https://ppa.launchpadcontent.net/amnezia/ppa/ubuntu focal main" | sudo tee -a /etc/apt/sources.list.d/amnezia.list
$ sudo apt-get update
$ sudo apt-get install --yes amneziawg amneziawg-tools
```

I took the opportunity to create a [new PR](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/pull/134) - let's see if it gets merged.]]>
        </content>
    </entry>
    
    <entry>
        <title>I join CRXN</title>
        
        <link rel="alternate" href="https://mk16.de/blog/join_crxn/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/join_crxn/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/join_crxn/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/join_crxn/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/join_crxn/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/join_crxn/" hreflang="en" />

        <id>/blog/join_crxn/</id>
        <published>2022-11-22T00:00:00+01:00</published>
        <updated>2022-11-22T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>I have joined the CRXN network. Here I show the differences to dn42 and what steps I took to join.</summary>
        <content type="html">
            <![CDATA[Hello,

today I joined the CRXN netwerk. The CRXN is similar to the dn42, but with four differences:
- It uses IPv6 only
- It uses the babel routing protocol
- It is small, which can be seen by the number of prefixes
- It uses fastd as a tunnel

I wanted to have the CRXN on the same nodes as I have the dn42 network, but the two networks should not mix because their registries are not synced, use different routing protocols, and are not interconnected to each other. This presented a bit of a challenge.

Furthermore, in CRXN it is currently not (yet) possible to validate the routes, so route hijacking or similar is quite easy.

## It uses only IPv6
I think it is a good decision to only use IPv6. Not for nothing IPv6 is the future. CRXN uses the ULA range. This corresponds to the private IPv4 range. This range is so large that (if the prefixes are randomly generated) there is normally no collision for example with the dn42.

## It uses the babel routing protocol
Babel is actually used for IGP, but it is also possible to connect networks with it. The routing becomes so more efficient that as selection criterion a metric is used, which corresponds in the reference implementation (babeld) to the latency in ms. Thus, the path with the shortest latency is always used. Also bird and frr have implemented babel in the meantime, but there the metric is (unfortunately) not calculated automatically. I made it so that I measure this at the beginning of a peering and then enter it manually. The problem is that latencies can change - for example when the underlay network (here the clearnet) changes.

## It is small
If you don't do strict validation of routes, you get about 20 IPv6 prefixes. If you do more strict validation, you get about 10 prefixes. With dn42 (+ NeoNetwork + IC-VPN + ChaosVPN) there are about 600 prefixes.

## It uses fastd
In dn42, WireGuard is normally used for peering, as it is easy to configure and provides good security. Before that OpenVPN was used. OpenVPN is slower and less secure when used with a preshared key (PSK).
fastd is also used by Freifunk / IC-VPN. It is also a tunnel solution, which is also encrypted with good security. In contrast to WireGuard, it operates on layer 2 of the OSI model (MAC addresses), whereas WireGuard operates on layer 3 (IP addresses).

## Multiple nets on one node
Multiple nets on one node - there are different solutions for this. One solution would be to use multiple bird instances. For this solution I don't have the knowledge and it is too complex for me.
Another solution would be to use different routing tables. So you could use `ipv6 table crxn;` to create a second IPv6 routing table. This approach becomes problematic if you - like me - have several nodes which are connected via iBGP. I would have to have twice as many BGP sessions as I do now, since multiple routing tables cannot be transmitted in one BGP session.
Another option is to "tag" routes. On import, the dn42 routes would be tagged with "dn42" and the CRXN routes would be tagged with "crxn". When exporting, you could now assign each route to the appropriate network. There are several ways to tag a route. The most common one is to append BGP Communities or Large BGP Communities (for 32-bit ASNs) to the route. However, since dn42 peers only expect dn42 routes, the information that these are dn42 routes is unnecessary and would only take up space in the Global Routing Table. This means you would have to delete the corresponding communities on export.
The next problem with communities was that I have two AS numbers. One from dn42 and one from NeoNetwork. Which one should I use to tag the routes? Since it would be internal only, it wouldn't matter.
An alternative to the BGP communities are user defined BGP attributes. A BGP attribute is for example `bgp_local_pref` or `bgp_med`, but you can also define them yourself. I decided to use this option. I created a new integer attribute named `netid` with `attribute int netid;`. If the route gets from the dn42 net, it gets the id `1`. If the route comes from the CRXN network, it gets the ID `2`. Accordingly I have to filter the other network during export. So I have all routes in one routing table and tagged them accordingly.

## Missing validation of routes
In dn42 you can generate ROA entries from the registry. In these it is written, which AS, which prefixes and with which length may announce. In CRXN, however, there are no AS numbers, which is why ROA filtering is out of the question. As registry there is only an `entitydb`, in which name of the network and prefix of the network is indicated. The files in it are in JSON format. The only validation I can do is to say that non-CRXN prefixes are not exported. The prefixes which are allowed I parse again from the `entitydb`. All other routes I discard.

## Future
A possible networking with the dn42 is planned. There should be an ASN, which has the authorization to export all CRXN routes. With appropriate configuration, one could establish so several gateways between the dn42 and the CRXN. It remains exciting.

## Links
- entitydb: [https://codeberg.org/CRXN/entitydb](https://codeberg.org/CRXN/entitydb)
- CRXN documentation: [http://deavmi.assigned.network/projects/crxn/](http://deavmi.assigned.network/projects/crxn/)
- CRXN documentation mirror: [https://crxn.de/docs/](https://crxn.de/docs/)
- fastd documentation: [https://fastd.readthedocs.io/en/stable/index.html](https://fastd.readthedocs.io/en/stable/index.html)]]>
        </content>
    </entry>
    
    <entry>
        <title>Ich habe bei Jugend Forscht teilgenommen</title>
        
        <link rel="alternate" href="https://mk16.de/blog/jugend_forscht/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/jugend_forscht/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/jugend_forscht/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/jugend_forscht/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/jugend_forscht/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/jugend_forscht/" hreflang="de" />

        <id>/blog/jugend_forscht/</id>
        <published>2022-02-11T00:00:00+01:00</published>
        <updated>2022-02-11T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        
        <content type="html">
            <![CDATA[Hallo, ich habe mit meinem Projekt "Banduras Lan Messenger" an Jugend Forscht teilgenommen und einen dritten Platz bekommen. Das Programm kann man unter <a href="https://github.com/marek22k/lanmessenger/" target="_blank">https://github.com/marek22k/lanmessenger/</a> finden.

<video controls>
    <source src="/assets/video/jugend_forscht.webm" type="video/webm">
    <source src="/assets/video/jugend_forscht.mp4" type="video/mp4">
</video>
Du kannst das Video auch <a href="/assets/video/jugend_forscht.mp4">hier</a> sehen bzw. herunterladen.<br>
Dies ist ein Ausschnitt aus [Livestream der Feierstunde des Regionalwettbewerbs Jugend forscht Hildesheim](https://www.youtube.com/watch?v=PXFqbvoSOho).]]>
        </content>
    </entry>
    
    <entry>
        <title>Vorstellung meine bird2 Konfiguration für mein Lab AS</title>
        
        <link rel="alternate" href="https://mk16.de/blog/lab-de/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/lab-de/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/lab-de/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/lab-de/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/lab-de/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/lab-de/" hreflang="de" />

        <id>/blog/lab-de/</id>
        <published>2023-01-31T00:00:00+01:00</published>
        <updated>2025-08-07T01:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Hier stelle ich meine dn42-Konfiguration vor, die ich in meinem Lab verwende. Dies ist ein IPv6-only Netzwerk. Es wird der Routing-Daemon bird verwendet. Die Konfiguration enthält Funktionen, verschiedene Filter und Tipps.</summary>
        <content type="html">
            <![CDATA[Ich habe im dn42 ein Lab AS. AS4242422924. Dies ist ein IPv6-only Netzwerk, welches nur auf einem Knoten ist. Ich bentzte es einernseits zum Monitoring meines Hauptnetzwerkes und einernseites um Verbindungen mit GNS3 herzustellen.

Ich bentzte als BGP Daemon bird2. Heute möchte ich meine bird2 Konfiguration "vorstellen".

Ich habe versucht die Konfiguration etwas modularer aufzubauen. Daher besteht die Konfiguration aktuell aus fünf Dateien.
bird.conf:
```bird
log syslog { warning, error, fatal };
log "/var/log/bird/remote.log" { remote };
log "/var/log/bird/bugs.log" { bug };
log "/var/log/bird/trace.log" { trace };
log "/var/log/bird/debug.log" { debug };
log "/var/log/bird/info.log" { info };

include "header.conf";

/* Set router details */
router id RID;
hostname HOSTNAME;

/* Set tables */
ipv6 table dn42;

/* Utility functions */

function is_self_net() -> bool {
  return net ~ OWNNETSET;
}

function is_valid_network() -> bool {
  return net ~ [
    fd00::/8{44,64}
  ];
}

/* ROA */

roa6 table dnroa;

# import roa from file
protocol static {
    roa6 {
        table dnroa;
    };
    include "/var/lib/bird/dn42-roa6.conf";
}

include "filters.conf";

/* Export own net */

protocol static ownnet {
    route OWNNET unreachable;

    ipv6 {
        table dn42;
    };
}

/* Kernel */

protocol kernel {
    metric 500;

    ipv6 {
        table dn42;
        export where kernel_export();
    };
}

/* make unknown routes unreachable */

protocol static {
    route fd00::/8 unreachable;

    ipv6 {
        table dn42;
    };
}

protocol bfd { }
protocol device { }

include "templates.conf";

include "peers/*.conf";
```

```
log syslog { warning, error, fatal };
log "/var/log/bird/remote.log" { remote };
log "/var/log/bird/bugs.log" { bug };
log "/var/log/bird/trace.log" { trace };
log "/var/log/bird/debug.log" { debug };
log "/var/log/bird/info.log" { info };
```
Damit nicht alles in das Syslog geschrieben wird und damit unübersichtlich wird, lasse ich nur Fehlermeldungen und Warnungen in Syslog schreiben. Andere Dinge lasse ich in Logdateien schreiben.

```
include "header.conf";
```
Danach importiere ich `header.conf`.

```
define OWNAS = 4242422924;

define OWNIP = fd00:8e13:ce5d::1;

define OWNNET = fd00:8e13:ce5d::/48;

define OWNNETSET = [fd00:8e13:ce5d::/48+];

define RID = 42.0.29.24;

define HOSTNAME = "srv.dn42-lab.de";

define BANDWIDTH = 25;
define REGION_GEO = 41;
define REGION_COUNTRY = 1276;
```
In dieser sind Lediglich Variablen definiert. Darunter meine AS-Nummer, meine IP-Adresse, mein IP-Prefix, meine Router ID, mein Hostname und Parameter für die BGP communities.
In `REGION_GEO` und `REGION_COUNTRY` stehen die Regionscode gemäß des Wikis.

```
/* Set router details */
router id RID;
hostname HOSTNAME;
```
Diese teile ich in diesen Zeilen bird2 mit. Der Hostname muss nicht zwingend in bird stehen. Man kann es allerdings so einstellen, dass man über BGP den Hostname bekanntgibt. Ich dachte, dass ich dieses Feature mal ausprobieren wollte.

```
/* Set tables */
ipv6 table dn42;
```
Normallerweise bentzt bird die Tabellen master4 für IPv4 und master6 für IPv6 Routen, jedoch wollte ich eine sauberere Trennung und speichere alle dn42 Routen in der Tabelle `dn42`.

```
/* Utility functions */

function is_self_net() -> bool {
  return net ~ OWNNETSET;
}

function is_valid_network() -> bool {
  return net ~ [
    fd00::/8{44,64}
  ];
}
```
Diese zwei Funktionen habe ich aus dem Wiki übernommen. `is_self_net` überprüft, ob ein Prefix im eigenen Netz liegt und mit `is_valid_network` kann man alle nicht IPv6-ULAs aussortieren. Des Weiteren wird auch eine maximale und eine minimale Prefix Länge gesetzt: `/44` bis `/64`.

```
/* ROA */

roa6 table dnroa;

# import roa from file
protocol static {
    roa6 {
        table dnroa;
    };
    include "/var/lib/bird/dn42-roa6.conf";
}
```
Danach erstelle ich eine ROA Tabelle mit dem Namen `dnroa`. Von der ROA Datei `/var/lib/bird/dn42-roa6.conf` importiert bird alles. Dabei verwende ich [dn42-roagen](https://gitlab.com/bauen1/dn42-roagen/-/tree/master/) zur Erzeugung der ROA Dateien.

```
include "filters.conf";
```
Dann binde ich die `filters.conf` ein. In dieser stehen meine Import-/Export Filter.

```
include "community_filter.conf";

define ASN_BLACKLIST = [
0
];

function kernel_export() {
    krt_prefsrc = OWNIP;
    accept;
}

function reject_invalid_roa() {
    if (roa_check(dnroa, net, bgp_path.last) != ROA_VALID) then {
        print "Reject: ROA failed|", net, "|", bgp_path;
        reject;
    }
}

function reject_blacklisted()
int set blacklist;
{
    blacklist = ASN_BLACKLIST;

    if ( bgp_path ~ blacklist ) then {
        print "Reject: blacklisted ASN|", bgp_path;
        reject;
    }
}

function honor_graceful_shutdown() {
    if (65535, 0) ~ bgp_community then {
        bgp_local_pref = 0;
    }
}

function add_own_communities(int link_type) {
    bgp_large_community.add((OWNAS, 5, link_type));
}

function dn_import_filter(int link_latency; int link_bandwidth; int link_crypto; int link_type) {
    if ( net.type != NET_IP6 ) then {
        print "Reject: non-IPv6 on IPv6 Channel|", net, "|", bgp_path;
        reject;
    }

    if ( ! is_valid_network() ) then {
        print "Reject: invalid network|", net, "|", bgp_path;
        reject;
    }

    if ( is_self_net() ) then {
        print "Reject: export our network|", net, "|", bgp_path.first;
        reject;
    }

    if ( bgp_path.len > 25 ) then {
        print "Reject: AS path too long|", net, "|", bgp_path;
        reject;
    }

    reject_blacklisted();
    reject_invalid_roa();

    if (bgp_path.len = 1) then
        bgp_local_pref = bgp_local_pref + 500;

    honor_graceful_shutdown();

    update_flags(link_latency, link_bandwidth, link_crypto);
    add_own_communities(link_type);

    accept;
}

function dn_export_filter(int link_latency; int link_bandwidth; int link_crypto; int link_type) {
    if (source !~ [RTS_STATIC, RTS_BGP]) then
        reject;

    if (bgp_path.last != bgp_path.first) then
        reject;

    update_flags(link_latency, link_bandwidth, link_crypto);
    update_region();

    bgp_med = 0;
    bgp_med = bgp_med + ( ( 4 - ( link_crypto - 30 ) ) * 600 );
    bgp_med = bgp_med + ( ( 9 - ( link_bandwidth - 20 ) ) * 100);
    bgp_med = bgp_med + ( ( link_latency - 1) * 300);
    bgp_med = bgp_med + ( link_type - 1 ) * 10;

    accept;
}

function dn_export_collector() {
    if (source !~ [RTS_STATIC, RTS_BGP]) then
        reject;

    update_region();
    accept;
}

```
Die Filter Datei bindet wiederum `community_filter.conf` ein. Dabei handelt es sich um eine leicht-modifizierte Kopie aus dem Wiki.

```
function update_latency(int link_latency) {
  bgp_community.add((64511, link_latency));
       if (64511, 9) ~ bgp_community then { bgp_community.delete([(64511, 1..8)]); }
  else if (64511, 8) ~ bgp_community then { bgp_community.delete([(64511, 1..7)]); }
  else if (64511, 7) ~ bgp_community then { bgp_community.delete([(64511, 1..6)]); }
  else if (64511, 6) ~ bgp_community then { bgp_community.delete([(64511, 1..5)]); }
  else if (64511, 5) ~ bgp_community then { bgp_community.delete([(64511, 1..4)]); }
  else if (64511, 4) ~ bgp_community then { bgp_community.delete([(64511, 1..3)]); }
  else if (64511, 3) ~ bgp_community then { bgp_community.delete([(64511, 1..2)]); }
  else if (64511, 2) ~ bgp_community then { bgp_community.delete([(64511, 1..1)]); }
}

function update_bandwidth(int link_bandwidth) {
  bgp_community.add((64511, link_bandwidth));
       if (64511, 21) ~ bgp_community then { bgp_community.delete([(64511, 22..29)]); }
  else if (64511, 22) ~ bgp_community then { bgp_community.delete([(64511, 23..29)]); }
  else if (64511, 23) ~ bgp_community then { bgp_community.delete([(64511, 24..29)]); }
  else if (64511, 24) ~ bgp_community then { bgp_community.delete([(64511, 25..29)]); }
  else if (64511, 25) ~ bgp_community then { bgp_community.delete([(64511, 26..29)]); }
  else if (64511, 26) ~ bgp_community then { bgp_community.delete([(64511, 27..29)]); }
  else if (64511, 27) ~ bgp_community then { bgp_community.delete([(64511, 28..29)]); }
  else if (64511, 28) ~ bgp_community then { bgp_community.delete([(64511, 29..29)]); }
}

function update_crypto(int link_crypto) {
  bgp_community.add((64511, link_crypto));
       if (64511, 31) ~ bgp_community then { bgp_community.delete([(64511, 32..34)]); }
  else if (64511, 32) ~ bgp_community then { bgp_community.delete([(64511, 33..34)]); }
  else if (64511, 33) ~ bgp_community then { bgp_community.delete([(64511, 34..34)]); }
}

function update_flags(int link_latency; int link_bandwidth; int link_crypto) {
    if link_bandwidth > BANDWIDTH then link_bandwidth = BANDWIDTH;

    update_latency(link_latency);
    update_bandwidth(link_bandwidth);
    update_crypto(link_crypto);
}

function update_region() {
    if is_self_net() then {
        bgp_community.add((64511, REGION_GEO));
        bgp_community.add((64511, REGION_COUNTRY));
    }
}
```
Ich habe dabei die unnötigen Variables aus der Funktion `update_flags` entfernt sowie die Funktion `update_region` hinzugefügt. Sollte ich eigene Routen exportieren, versehe ich diese mit einer Region Community.

```
define ASN_BLACKLIST = [
0
];
```
Danach definiere ich eine ASN Blacklist für den Fall der Fälle. Da bird mindentes einen Eintrag verlangt, habe ich die AS0 eingetragen.

```
function kernel_export() {
    krt_prefsrc = OWNIP;
    accept;
}
```
Die Funktion `kernel_export` ist der Export Filter, was alles in den Kernel exportiert werden soll. In diesem Fall soll alles in den Kernel exportiert werden und meine IP-Adresse soll als Source IP verwendet werden.

```
function reject_invalid_roa() {
    if (roa_check(dnroa, net, bgp_path.last) != ROA_VALID) then {
        print "Reject: ROA failed|", net, "|", bgp_path;
        reject;
    }
}
```
Die Funktion `reject_invalid_roa` lehnt invalide und unbekannte ROAs ab und schreibt ein Log im Format `Reject: ROA failed|IPv6-Adresse|BGP Pfad`. Die Standard Funktion aus dem Wiki schreibt lediglich die Ursprung des nicht validen ROAs auf. Jedoch ist der AS Pfad auch sehr interessant, denn daran kann man erkennen, wer die nicht valide ROA an einen exportiert hat.

```
function reject_blacklisted()
int set blacklist;
{
    blacklist = ASN_BLACKLIST;

    if ( bgp_path ~ blacklist ) then {
        print "Reject: blacklisted ASN|", bgp_path;
        reject;
    }
}
```
Die Funktion `reject_blacklisted` prüft, ob eine geblacklistete ASN im AS Pfad vorkommt. Wenn ja, wird die Route abgelehnt und eine Log im Format `Reject: blacklisted ASN|AS Pfad` geschrieben.

```
function honor_graceful_shutdown() {
    if (65535, 0) ~ bgp_community then {
        bgp_local_pref = 0;
    }
}
```
Dies ist eine Kopie aus dem BGP Filter Guide. Falls ein Peer einen Graceful Shutdown sendet, werden allen Routen die Community `(65535, 0)` angefügt. Man sollte nun schnell andere Routen finden. Mit `bgp_local_pref = 0` teilen wir bird mit, dass er die Route vermeiden soll. Die local_pref ist ein Entscheidungskreterium bei der Routenauswahl, welches noch vor der AS Pfad Länge evaluiert wird.

```
function add_own_communities(int link_type) {
    bgp_large_community.add((OWNAS, 5, link_type));
}
```
Des Weiteren habe ich noch eine eigene Community, welche ich an Routen abhäfte. Da ich eine 32-bit ASN habe, muss ich Large Communities verwenden. Je nachdem, wie die Verbindung zum anderen Peer hergestellt wird (physische Verbindung, IXP, Tunnel, ...) wird eine andere Community angehäftet.

```
function dn_import_filter(int link_latency; int link_bandwidth; int link_crypto; int link_type) {
```
Der Import Filter ist etwas komplexer.

```
if ( net.type != NET_IP6 ) then {
    print "Reject: non-IPv6 on IPv6 Channel|", net, "|", bgp_path;
    reject;
}
```
Als erstes wird alles abgelehnt, was keine IPv6 ist.

```
if ( ! is_valid_network() ) then {
    print "Reject: invalid network|", net, "|", bgp_path;
    reject;
}
```
Danach wird alles abgelehnt, was nicht in das dn42 Netz gehört oder wo der Prefix zu groß oder zu klein ist.

```
if ( is_self_net() ) then {
    print "Reject: export our network|", net, "|", bgp_path.first;
    reject;
}
```
Für mein Netzwerk kümmer ich mich. Daher muss ich dieses nicht von fremden Peer importieren.

```
if ( bgp_path.len > 25 ) then {
    print "Reject: AS path too long|", net, "|", bgp_path;
    reject;
}
```
Danach lehne ich zu Lange AS Pfade ab.

```
reject_blacklisted();
reject_invalid_roa();
```
Dann rufe ich alle Funktionen von oben auf und lehne Routen mit geblacklisteten ASNs ab, nicht valide ROAs.

```
if (bgp_path.len = 1) then
    bgp_local_pref = bgp_local_pref + 500;
```
Sollte ich direkt mit einem Peer verbunden sein, möchte ich die Route immer bevorzugen. Daher erzwinge ich dies mit local_pref.

```
honor_graceful_shutdown();

update_flags(link_latency, link_bandwidth, link_crypto);
add_own_communities(link_type);

accept;
```
Danach prüfe ich auf Graceful Shutdown und markiere die Routen mit einer Community. Zum Schluss akzeptiere ich sie.

```
function dn_export_filter(int link_latency; int link_bandwidth; int link_crypto; int link_type) {
```
```
if (source !~ [RTS_STATIC, RTS_BGP]) then
    reject;
```
Alle Routen, welche ich nicht selber via BGP bekommen habe oder statisch festgelegt habe, sollen nicht exportiert werden.

```
if (bgp_path.last != bgp_path.first) then
    reject;
```
Da es ein Lab AS ist, möchte ich nur bedingt als Transit auftreten und exportiere daher nur Transit Routen zu meinen direkten Peers.

Es gibt einen Unterschied zwsichen `bgp_path.last != bgp_path.first` und `bgp_path.len = 1`. Es gibt eine Technik seine eigene Routen "unbeliebter" zu machen. Diese besteht im ASN prepending. Dadurch hängt man deine eigene ASN mehrfach in den AS Pfad. Dadurch wird dieser länger und unbeliebter. `bgp_path.len = 1` erkennt direkte Peer ohne ASN prepending und `bgp_path.last != bgp_path.first` erkennt auch direkt Peers mit ASN prepending. Wenn jemand eine Routen künstlich verlängert und dadurch unbeliebter macht, hat dies sicherlich schon einen Sinn. Für Prependete Pfade setzte ich daher keine höhrere local_pref. Jedoch möchte ich als Transit für direkte Peers fungieren - unabhängig vom Prepending. Das Prepending sende ich ja auch wiederum an meine Peers. Dadurch ist der Pfad bei denen genauso unbeliebt wie bei mir.

```
update_flags(link_latency, link_bandwidth, link_crypto);
update_region();
```
Auch hier markiere ich die Routen entsprechend den Communities im Wiki.

```
bgp_med = 0;
bgp_med = bgp_med + ( ( 4 - ( link_crypto - 30 ) ) * 600 );
bgp_med = bgp_med + ( ( 9 - ( link_bandwidth - 20 ) ) * 100);
bgp_med = bgp_med + ( ( link_latency - 1) * 300);
bgp_med = bgp_med + ( link_type - 1 ) * 10;
```
Hier lasse ich semiautomatisch das `bgp_med` berechnen. Wenn zwei Autonome System an zwei Stellen miteinander verbunden sind, ist natürlich die Frage, wohin sollen die Anfragen gesendet werden. Dafür gibt es das `bgp_med`. Wenn es zwei Routen vom selben AS gibt, entscheidet das `bgp_med`, welche Route verwendet wird. Umso kleiner das `bgp_med`, umso besser die Route.
Hier ist die Reihenfolge:
- Bevorzuge immer besser verschlüsselte Verbindungen über schlechter verschlüsselte
- Bevorzuge die Route mit der kleinere Latenz
- Bevorzuge die Route mit der besseren Bandbreite
- Bevorzuge direkte physische Verbindungen, danach Verbindungen über IXPs, danach Verbindungen über Tunnel, danach Verbindungen über virtuelle IXPs, danach andere Verbindungen

```
accept;
```
Danach exportiere ich die Route.

```
function dn_export_collector() {
    if (source !~ [RTS_STATIC, RTS_BGP]) then
        reject;

    update_region();
    accept;
}
```
Einem Route Collector möchte ich möglichst alle Routen unverändert senden, welche ich auch habe. Daher exportiere ich alle Routen, sofern ich sie statische oder über BGP bekommen habe. Die Region Community füge ich auch hinzu.

```
/* Export own net */

protocol static ownnet {
    route OWNNET unreachable;

    ipv6 {
        table dn42;
    };
}
```
Nachdem bird die Filter geladen hat, setze ich eine statische Route für mein eigenes Netzwerk. Dies bewirkt, dass bird nun eine Route zu meinem Netzwerk kennt und sie zu anderen exportiert.

```
/* Kernel */

protocol kernel {
    metric 500;

    ipv6 {
        table dn42;
        export where kernel_export();
    };
}
```
Nun exportiere ich die Routen in den Kernel und damit in die Forwarding Table. Dabei bentzte ich den Filter, welchen ich zuvor definiert hatte. Die Routen bekommen alle die Metric 500 in der Forwarding Table. Dies mache ich, da es Probleme mit direkten Routen gab. Setzt man keine hohe Metric werden die Routen über BGP bevorzugt, obwohl man zum Beispiel wegen einer VPN Verbindung eine direkte Route kennt.

```
/* make unknown routes unreachable */

protocol static {
    route fd00::/8 unreachable;

    ipv6 {
        table dn42;
    };
}
```
Hier definiere ich die dn42 default route. Dies bewirkt, dass ich "unreachable" via ICMP zurückgebe falls ich eine Route nicht weiß, sie aber trotzdem über mich versucht wird zu erreichen.

```
protocol bfd { }
protocol device { }
```
Danach aktiviere ich zwei "spezielle" Protokolle. bfd wird dafür bentzt, um im Milisekundenbereich Verbindungsstörungen zu erkennen. Das device Protokoll wird bentzt, damit bird die IP-Adressen meiner lokalen Interfaces kennt.

```
include "templates.conf";
```
Danach importiere ich die Templates, welche ich zum Peering verwende.

```
template bgp dnpeer {
    local as OWNAS;

    enforce first as on;
    graceful restart on;
    long lived graceful restart on;
    enable extended messages on;
    advertise hostname on;
    prefer older on;

    # defaults
    enable route refresh on;
    interpret communities on;
    default bgp_local_pref 100;

    ipv6 {
        mandatory on;

        table dn42;

        import table;
        import limit 1000 action restart;

        import none;
        export none;
    };

}

template bgp routecollector from dnpeer {
    multihop;

    ipv6 {
        add paths tx;
    };
}
```

```
local as OWNAS;
```
Damit teile ich bird mit, dass meine ASN Nummer für ein Peering verwendet werden soll.

```
enforce first as on;
```
Diese Option bewirkt, dass im AS Pfad von einem Peer, die ASN von Peer zuerst stehen muss. Dies ist außer bei Route Servern eigentlich immer der Fall. Um sich allerdings vor Fehlkonfigurationen seines Peer zu schützen, kann man diese Option aktivieren.

```
graceful restart on;
long lived graceful restart on;
enable extended messages on;

...
    
prefer older on;
```
Danach aktiviere einige Funktionen. Diese habe ich in [Beginner Tips](./beginner_tips/) versucht zu erklären.

```
advertise hostname on;
```
Oben hatte ich ja meinen Hostname festgelegt. Damit dieser via BGP dem Peer mitgeteilt wird, muss diese Option aktiviert sein.

```
# defaults
enable route refresh on;
interpret communities on;
default bgp_local_pref 100;
```
Hier setzte ich zur Sicherheit einige Parameter auf ihren Default Wert. Dies ist eigentlich unnötig.

Im Channel sind einige Funktionen, welche im Wiki nicht sind.
```
import table;
```
Dies hatte ich auch bereits in [Beginner Tips](./beginner_tips/) erklärt.

```
import limit 1000 action restart;
```
Falls der Peer über 1000 Prefixe exportiert, wird die Sitzung neugestartet. Eine Alternative ist die Aktion `block`. Diese blockiert alle weiteren Imports anstatt die BGP Sitzung neuzustarten.

```
mandatory on;
```
Damit teile ich bird mit, dass der Peer zwingend IPv6 unterstützten muss. Wenn er es nicht unterstützt, wird keine BGP Sitzung hergestellt.

```
table dn42;
```
Diese Zeile bewirkt, dass alle Routen aus der Tabelle `dn42` im- bzw. exportiert werden und nicht aus der Standardtabelle `master6`.

```
import none;
export none;
```
Sollten noch keine Parameter angepasst sein, soll weder etwas im- noch exportiert werden. Für jeden Peer lege ich einzeln entsprechende Parameter wie Latenz oder Bandbreite fest.

```
template bgp routecollector from dnpeer {
    multihop;

    ipv6 {
        add paths tx;
    };
}
```
Die Vorlage für den Route Collector "erbt" von der `dnpeer` Vorlage. Hier wird noch die Option `multihop;` aktiviert, welche bewirkt, dass man keinen Tunnel zum Route Collector herstellen muss, sondern ihn "normal" erreichen kann. Die Option `add paths tx;` bewirkt, dass alle Routen (nicht nur die besten) zum Collector exportiert werden.

```
include "peers/*.conf";
```
Danach lade ich meine Peers.

Folgendes ist eine Konfiguration für die BGP Sitzung mit meinem Hauptnetzwerk:
```
protocol bgp bandura from dnpeer {
    neighbor fe80::2923%bandura as 4242422923;

    bfd graceful;

    ipv6 {
        import where dn_import_filter(1, 25, 34, 1);
        export where dn_export_filter(1, 25, 34, 1);
    };
}
```
Mit `dn_import_filter(1, 25, 34, 1)` und `dn_export_filter(1, 25, 34, 1)` lege ich beispielsweise Parameter für die BGP communities fest und aktiviere den Import und Export.
Mit `bfd graceful;` aktiviere ich BfD, welche standardmäßig für BGP Sitzungen deaktiviert ist.

Changelog:
**30.06.2024** bird erfordert nun bei Funktionen, welche Werte zurückgeben, die explizite Angabe eines Return Types mittels `-> [return type]`. Die zwei Funktionen `is_self_net` und `is_valid_network` wurden entsprechend angepasst. Ohne Angabe des Rückwertetypes wird eine Warnung wie `Inferring function [function name] return type from its return value: [return type]` ausgegeben.]]>
        </content>
    </entry>
    
    <entry>
        <title>Introducing my bird2 configuration for my Lab AS</title>
        
        <link rel="alternate" href="https://mk16.de/blog/lab-en/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/lab-en/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/lab-en/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/lab-en/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/lab-en/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/lab-en/" hreflang="en" />

        <id>/blog/lab-en/</id>
        <published>2023-01-31T00:00:00+01:00</published>
        <updated>2025-08-07T01:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Here I present my dn42 configuration, which I use in my lab. This is an IPv6-only network. The routing daemon bird is used. The configuration contains functions, different filters and tips.</summary>
        <content type="html">
            <![CDATA[I have in the dn42 a Lab AS. AS4242422924. This is an IPv6-only network, which is only on one node. I use it on one side to monitor my main network and on the other side to connect prefixes to GNS3.

I use bird2 as BGP daemon. Today I want to "introduce" my bird2 configuration.

I tried to build the configuration a bit more modular than the configuration of my main network. Therefore the configuration currently consists of five files.
bird.conf:
```
log syslog { warning, error, fatal };
log "/var/log/bird/remote.log" { remote };
log "/var/log/bird/bugs.log" { bug };
log "/var/log/bird/trace.log" { trace };
log "/var/log/bird/debug.log" { debug };
log "/var/log/bird/info.log" { info };

include "header.conf";

/* Set router details */
router id RID;
hostname HOSTNAME;

/* Set tables */
ipv6 table dn42;

/* Utility functions */

function is_self_net() -> bool {
  return net ~ OWNNETSET;
}

function is_valid_network() -> bool {
  return net ~ [
    fd00::/8{44,64}
  ];
}

/* ROA */

roa6 table dnroa;

# import roa from file
protocol static {
    roa6 {
        table dnroa;
    };
    include "/var/lib/bird/dn42-roa6.conf";
}

include "filters.conf";

/* Export own net */

protocol static ownnet {
    route OWNNET unreachable;

    ipv6 {
        table dn42;
    };
}

/* Kernel */

protocol kernel {
    metric 500;

    ipv6 {
        table dn42;
        export where kernel_export();
    };
}

/* make unknown routes unreachable */

protocol static {
    route fd00::/8 unreachable;

    ipv6 {
        table dn42;
    };
}

protocol bfd { }
protocol device { }

include "templates.conf";

include "peers/*.conf";
```

```
log syslog { warning, error, fatal };
log "/var/log/bird/remote.log" { remote };
log "/var/log/bird/bugs.log" { bug };
log "/var/log/bird/trace.log" { trace };
log "/var/log/bird/debug.log" { debug };
log "/var/log/bird/info.log" { info };
```
So that not everything is written into the syslog and thus becomes unclear, I let write only error messages and warnings into syslog. Other things I let write in log files.

```
include "header.conf";
```
After that I import `header.conf`.

```
define OWNAS = 4242422924;

define OWNIP = fd00:8e13:ce5d::1;

define OWNNET = fd00:8e13:ce5d::/48;

define OWNNETSET = [fd00:8e13:ce5d::/48+];

define RID = 42.0.29.24;

define HOSTNAME = "srv.dn42-lab.de";

define BANDWIDTH = 25;
define REGION_GEO = 41;
define REGION_COUNTRY = 1276;
```
In this only variables are defined. Among them my AS number, my IP address, my IP prefix, my router ID, my host name and parameters for the BGP communities.
In `REGION_GEO` and `REGION_COUNTRY` are the region codes according to the wiki.

```
/* Set router details */
router id RID;
hostname HOSTNAME;
```
I will tell this to bird2 in these lines. The hostname does not have to be in bird. However, you can set it to announce the hostname via BGP. I thought that I would try this feature.

```
/* Set tables */
ipv6 table dn42;
```
Normally bird uses the tables master4 for IPv4 and master6 for IPv6 routes, but I wanted a cleaner separation and store all dn42 routes in the table `dn42`.

```
/* Utility functions */

function is_self_net() -> bool {
  return net ~ OWNNETSET;
}

function is_valid_network() -> bool {
  return net ~ [
    fd00::/8{44,64}
  ];
}
```
I took these two functions from the wiki. `is_self_net` checks if a prefix is in your own network and with `is_valid_network` you can sort out all non-IPv6 ULAs. Furthermore a maximum and a minimum prefix length is set: `/44` to `/64`.

```
/* ROA */

roa6 table dnroa;

# import roa from file
protocol static {
    roa6 {
        table dnroa;
    };
    include "/var/lib/bird/dn42-roa6.conf";
}
```
After that I create a ROA table with the name `dnroa`. From the ROA file `/var/lib/bird/dn42-roa6.conf` bird imports everything. I use [dn42-roagen](https://gitlab.com/bauen1/dn42-roagen/-/tree/master/) to create the ROA files.

```
include "filters.conf";
```
Then I include the `filters.conf`. This contains my import/export filters.

```
include "community_filter.conf";

define ASN_BLACKLIST = [
0
];

function kernel_export() {
    krt_prefsrc = OWNIP;
    accept;
}

function reject_invalid_roa() {
    if (roa_check(dnroa, net, bgp_path.last) != ROA_VALID) then {
        print "Reject: ROA failed|", net, "|", bgp_path;
        reject;
    }
}

function reject_blacklisted()
int set blacklist;
{
    blacklist = ASN_BLACKLIST;

    if ( bgp_path ~ blacklist ) then {
        print "Reject: blacklisted ASN|", bgp_path;
        reject;
    }
}

function honor_graceful_shutdown() {
    if (65535, 0) ~ bgp_community then {
        bgp_local_pref = 0;
    }
}

function add_own_communities(int link_type) {
    bgp_large_community.add((OWNAS, 5, link_type));
}

function dn_import_filter(int link_latency; int link_bandwidth; int link_crypto; int link_type) {
    if ( net.type != NET_IP6 ) then {
        print "Reject: non-IPv6 on IPv6 Channel|", net, "|", bgp_path;
        reject;
    }

    if ( ! is_valid_network() ) then {
        print "Reject: invalid network|", net, "|", bgp_path;
        reject;
    }

    if ( is_self_net() ) then {
        print "Reject: export our network|", net, "|", bgp_path.first;
        reject;
    }

    if ( bgp_path.len > 25 ) then {
        print "Reject: AS path too long|", net, "|", bgp_path;
        reject;
    }

    reject_blacklisted();
    reject_invalid_roa();

    if (bgp_path.len = 1) then
        bgp_local_pref = bgp_local_pref + 500;

    honor_graceful_shutdown();

    update_flags(link_latency, link_bandwidth, link_crypto);
    add_own_communities(link_type);

    accept;
}

function dn_export_filter(int link_latency; int link_bandwidth; int link_crypto; int link_type) {
    if (source !~ [RTS_STATIC, RTS_BGP]) then
        reject;

    if (bgp_path.last != bgp_path.first) then
        reject;

    update_flags(link_latency, link_bandwidth, link_crypto);
    update_region();

    bgp_med = 0;
    bgp_med = bgp_med + ( ( 4 - ( link_crypto - 30 ) ) * 600 );
    bgp_med = bgp_med + ( ( 9 - ( link_bandwidth - 20 ) ) * 100);
    bgp_med = bgp_med + ( ( link_latency - 1) * 300);
    bgp_med = bgp_med + ( link_type - 1 ) * 10;

    accept;
}

function dn_export_collector() {
    if (source !~ [RTS_STATIC, RTS_BGP]) then
        reject;

    update_region();
    accept;
}

```
The filter file on the other side includes `community_filter.conf`. This is a slightly modified copy from the wiki.

```
function update_latency(int link_latency) {
  bgp_community.add((64511, link_latency));
       if (64511, 9) ~ bgp_community then { bgp_community.delete([(64511, 1..8)]); }
  else if (64511, 8) ~ bgp_community then { bgp_community.delete([(64511, 1..7)]); }
  else if (64511, 7) ~ bgp_community then { bgp_community.delete([(64511, 1..6)]); }
  else if (64511, 6) ~ bgp_community then { bgp_community.delete([(64511, 1..5)]); }
  else if (64511, 5) ~ bgp_community then { bgp_community.delete([(64511, 1..4)]); }
  else if (64511, 4) ~ bgp_community then { bgp_community.delete([(64511, 1..3)]); }
  else if (64511, 3) ~ bgp_community then { bgp_community.delete([(64511, 1..2)]); }
  else if (64511, 2) ~ bgp_community then { bgp_community.delete([(64511, 1..1)]); }
}

function update_bandwidth(int link_bandwidth) {
  bgp_community.add((64511, link_bandwidth));
       if (64511, 21) ~ bgp_community then { bgp_community.delete([(64511, 22..29)]); }
  else if (64511, 22) ~ bgp_community then { bgp_community.delete([(64511, 23..29)]); }
  else if (64511, 23) ~ bgp_community then { bgp_community.delete([(64511, 24..29)]); }
  else if (64511, 24) ~ bgp_community then { bgp_community.delete([(64511, 25..29)]); }
  else if (64511, 25) ~ bgp_community then { bgp_community.delete([(64511, 26..29)]); }
  else if (64511, 26) ~ bgp_community then { bgp_community.delete([(64511, 27..29)]); }
  else if (64511, 27) ~ bgp_community then { bgp_community.delete([(64511, 28..29)]); }
  else if (64511, 28) ~ bgp_community then { bgp_community.delete([(64511, 29..29)]); }
}

function update_crypto(int link_crypto) {
  bgp_community.add((64511, link_crypto));
       if (64511, 31) ~ bgp_community then { bgp_community.delete([(64511, 32..34)]); }
  else if (64511, 32) ~ bgp_community then { bgp_community.delete([(64511, 33..34)]); }
  else if (64511, 33) ~ bgp_community then { bgp_community.delete([(64511, 34..34)]); }
}

function update_flags(int link_latency; int link_bandwidth; int link_crypto) {
    if link_bandwidth > BANDWIDTH then link_bandwidth = BANDWIDTH;

    update_latency(link_latency);
    update_bandwidth(link_bandwidth);
    update_crypto(link_crypto);
}

function update_region() {
    if is_self_net() then {
        bgp_community.add((64511, REGION_GEO));
        bgp_community.add((64511, REGION_COUNTRY));
    }
}
```
I have removed the unnecessary variables from the function `update_flags` and added the function `update_region`. If I export my own routes, I tag them with a region community.

```
define ASN_BLACKLIST = [
0
];
```
After that I define an ASN blacklist just in case. Since bird requires at least one entry, I have entered the AS0.

```
function kernel_export() {
    krt_prefsrc = OWNIP;
    accept;
}
```
The function `kernel_export` is the export filter, what should be exported to the kernel. In this case everything should be exported to the kernel and my IP address should be used as source IP.

```
function reject_invalid_roa() {
    if (roa_check(dnroa, net, bgp_path.last) != ROA_VALID) then {
        print "Reject: ROA failed|", net, "|", bgp_path;
        reject;
    }
}
```
The function `reject_invalid_roa` rejects invalid and unknown ROAs and writes a log in the format `Reject: ROA failed|IPv6 address|BGP path`. The standard function from the wiki only writes down the origin of the non-valid ROA. However, the AS path is also very interesting, because you can see who exported the non-valid ROA to you.

```
function reject_blacklisted()
int set blacklist;
{
    blacklist = ASN_BLACKLIST;

    if ( bgp_path ~ blacklist ) then {
        print "Reject: blacklisted ASN|", bgp_path;
        reject;
    }
}
```
The function `reject_blacklisted` checks whether a blacklisted ASN occurs in the AS path. If so, the route is rejected and a log is written in the format `Reject: blacklisted ASN|AS path`.

```
function honor_graceful_shutdown() {
    if (65535, 0) ~ bgp_community then {
        bgp_local_pref = 0;
    }
}
```
This is a copy from the BGP Filter Guide. If a peer sends a Graceful Shutdown, all routes will have the community `(65535, 0)` appended. bird should now quickly look to find other routes. With `bgp_local_pref = 0` we tell bird to avoid the route. The local_pref is a decision criterion in route selection, which is evaluated before the AS path length.

```
function add_own_communities(int link_type) {
    bgp_large_community.add((OWNAS, 5, link_type));
}
```
Furthermore I have my own community, which I attach to routes. Since I have a 32-bit ASN, I have to use Large Communities. Depending on how the connection to the other peer is established (physical connection, IXP, tunnel, ...), a different community is attached.

```
function dn_import_filter(int link_latency; int link_bandwidth; int link_crypto; int link_type) {
```
The import filter is a bit more complex.

```
if ( net.type != NET_IP6 ) then {
    print "Reject: non-IPv6 on IPv6 Channel|", net, "|", bgp_path;
    reject;
}
```
The first thing to be rejected is anything that is not IPv6.

```
if ( ! is_valid_network() ) then {
    print "Reject: invalid network|", net, "|", bgp_path;
    reject;
}
```
After that, everything that does not belong in the dn42 network or where the prefix is too large or too small is rejected.

```
if ( is_self_net() ) then {
    print "Reject: export our network|", net, "|", bgp_path.first;
    reject;
}
```
I take care of my network. Therefore, I do not need to import this from other peers.

```
if ( bgp_path.len > 25 ) then {
    print "Reject: AS path too long|", net, "|", bgp_path;
    reject;
}
```
After that I reject too long AS paths.

```
reject_blacklisted();
reject_invalid_roa();
```
Then I call all functions from above and reject routes with blacklisted ASNs, non-valid ROAs.

```
if (bgp_path.len = 1) then
    bgp_local_pref = bgp_local_pref + 500;
```
Should I be directly connected to a peer, I always want to prefer the route. Therefore I enforce this with local_pref.

```
honor_graceful_shutdown();

update_flags(link_latency, link_bandwidth, link_crypto);
add_own_communities(link_type);

accept;
```
After that I check for Graceful Shutdown and mark the routes with a community. Finally, I accept them.

```
function dn_export_filter(int link_latency; int link_bandwidth; int link_crypto; int link_type) {
```
```
if (source !~ [RTS_STATIC, RTS_BGP]) then
    reject;
```
All routes, which I did not get myself via BGP or which are statically defined, should not be exported.

```
if (bgp_path.last != bgp_path.first) then
    reject;
```
Since it is a Lab AS, I want to be a transit only to a limited extent, so I only export transit routes to my direct peers.

There is a difference between `bgp_path.last != bgp_path.first` and `bgp_path.len = 1`. There is a technique to make your own routes more "unpopular". This is ASN prepending. Thereby you append your own ASN several times in the AS path. This makes it longer and therefore less popular. `bgp_path.len = 1` detects direct peers without ASN prepending and `bgp_path.last != bgp_path.first` detects also direct peers with ASN prepending. If someone artificially lengthens a route and thus makes it less popular, this certainly already has a purpose. For prepended paths I don't set a higher local_pref, but I want to act as transit for direct peers - independent of prepending. I also send the prepending to my peers. This makes the path just as unpopular for them as it is for me.

```
update_flags(link_latency, link_bandwidth, link_crypto);
update_region();
```
Also here, I tag the routes according to the communities in the wiki.

```
bgp_med = 0;
bgp_med = bgp_med + ( ( 4 - ( link_crypto - 30 ) ) * 600 );
bgp_med = bgp_med + ( ( 9 - ( link_bandwidth - 20 ) ) * 100);
bgp_med = bgp_med + ( ( link_latency - 1) * 300);
bgp_med = bgp_med + ( link_type - 1 ) * 10;
```
Here I let calculate semiautomatically the `bgp_med`. If two Autonomous Systems are connected at two locations, the question is of course, where to send the traffic to. For this there is the `bgp_med`. If there are two routes from the same AS, the `bgp_med` decides which route to use. The smaller the `bgp_med`, the better the route.
Here is the order:
- Always prefer better encrypted connections over worse encrypted ones.
- Prefer the route with the lower latency
- Prefer the route with better bandwidth
- Prefer direct physical connections, then connections via IXPs, then connections via tunnels, then connections via virtual IXPs, then other connections.

```
    accept;
```
After that I export the route.

```
function dn_export_collector() {
    if (source !~ [RTS_STATIC, RTS_BGP]) then
        reject;

    update_region();
    accept;
}
```
I want to send all routes to a route collector unchanged, which I have. Therefore I export all routes, if I got them static or via BGP. I also add the region community.

```
/* Export own net */

protocol static ownnet {
    route OWNNET unreachable;

    ipv6 {
        table dn42;
    };
}
```
After bird has loaded the filters, I set a static route for my own network. This causes bird to now know a route to my network and export it to others.

```
/* Kernel */

protocol kernel {
    metric 500;

    ipv6 {
        table dn42;
        export where kernel_export();
    };
}
```
Now I export the routes into the kernel and thus into the forwarding table. I use the filter I defined before. The routes all get the metric 500 in the forwarding table. I do this because there were problems with direct routes. If you don't set a high metric, the routes over BGP are preferred, although you know a direct route for example because of a VPN connection.

```
/* make unknown routes unreachable */

protocol static {
    route fd00::/8 unreachable;

    ipv6 {
        table dn42;
    };
}
```
Here I define the dn42 default route. This causes me to return "unreachable" via ICMP if I don't know a route, but it still tries to reach it through me.

```
protocol bfd { }
protocol device { }
```
After that I enable two "special" protocols. bfd is used to detect connection problems in the milisecond range. The device protocol is used so that bird knows the IP addresses of my local interfaces.

```
include "templates.conf";
```
After that I import the templates which I use for peering.

```
template bgp dnpeer {
    local as OWNAS;

    enforce first as on;
    graceful restart on;
    long lived graceful restart on;
    enable extended messages on;
    advertise hostname on;
    prefer older on;

    # defaults
    enable route refresh on;
    interpret communities on;
    default bgp_local_pref 100;

    ipv6 {
        mandatory on;

        table dn42;

        import table;
        import limit 1000 action restart;

        import none;
        export none;
    };

}

template bgp routecollector from dnpeer {
    multihop;

    ipv6 {
        add paths tx;
    };
}
```

```
local as OWNAS;
```
With this I inform bird about my ASN number which should be used for peering.

```
enforce first as on;
```
This option causes that in the AS path of a peer, the ASN of peer must be first. This is actually always the case except for route servers. However, to protect against misconfiguration of your peer, you can activate this option.

```
graceful restart on;
long lived graceful restart on;
enable extended messages on;

...
    
prefer older on;
```
After that activate some functions. I have tried to explain these in [Beginner Tips](./beginner_tips/).

```
advertise hostname on;
```
Above I had defined my hostname. This option must be activated so that this is communicated to the peers via BGP.

```
# defaults
enable route refresh on;
interpret communities on;
default bgp_local_pref 100;
```
Here I set some parameters to their default value for safety. This is actually unnecessary.

In the channel are some functions, which are not in the wiki.
```
import table;
```
I had already explained this in [Beginner Tips](./beginner_tips/).

```
import limit 1000 action restart;
```
If the peer exports over 1000 prefixes, the session is restarted. An alternative is the `block` action. This blocks all further imports instead of restarting the BGP session.

```
mandatory on;
```
With this I tell bird that the peer must support IPv6. If it does not support it, no BGP session will be established.

```
table dn42;
```
This line causes all routes to be imported or exported from table `dn42` and not from the default table `master6`.

```
import none;
export none;
```
If no parameters have been adjusted yet, nothing should be imported or exported. For each peer I set the corresponding parameters like latency or bandwidth.

```
template bgp routecollector from dnpeer {
    multihop;

    ipv6 {
        add paths tx;
    };
}
```
The template for the Route Collector "inherits" from the `dnpeer` template. Here the option `multihop;` is activated, which causes that you do not have to create a tunnel to the route collector, but can reach it "normally". The option `add paths tx;` causes that all routes (not only the best ones) are exported to the collector.

```
include "peers/*.conf";
```
After that, I include the configuration files of my peers.

The following is a configuration for the BGP session with my main network:
```
protocol bgp bandura from dnpeer {
    neighbor fe80::2923%bandura as 4242422923;

    bfd graceful;

    ipv6 {
        import where dn_import_filter(1, 25, 34, 1);
        export where dn_export_filter(1, 25, 34, 1);
    };
}
```
For example, with `dn_import_filter(1, 25, 34, 1)` and `dn_export_filter(1, 25, 34, 1)` I set parameters for the BGP communities and enable import and export.
With `bfd graceful;` I enable BfD, which is disabled by default for BGP sessions.

Changelog:
**30.06.2024** bird now requires the explicit declaration of a return type using `-> [return type]` for functions that return values. The two functions `is_self_net` and `is_valid_network` have been adapted accordingly. If the return value type is not specified, a warning such as `Inferring function [function name] return type from its return value: [return type]` is issued.]]>
        </content>
    </entry>
    
    <entry>
        <title>Unanständige Linux-Befehle</title>
        
        <link rel="alternate" href="https://mk16.de/blog/unanstaendige-linux-befehle/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/unanstaendige-linux-befehle/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/unanstaendige-linux-befehle/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/unanstaendige-linux-befehle/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/unanstaendige-linux-befehle/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/unanstaendige-linux-befehle/" hreflang="de" />

        <id>/blog/unanstaendige-linux-befehle/</id>
        <published>2023-07-19T01:00:00+02:00</published>
        <updated>2023-07-19T01:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Kann ein Computer unanständig und anstoßend sein? Mit dem Befehl `man finger` scheinen sie zumindest Männer &amp;quot;fingern&amp;quot; zu können.</summary>
        <content type="html">
            <![CDATA[**CW: Diese Seite enthält anzügliche Linux Befehle.**

Im Internet kreist schon seit Langen ein T-Shirt mit der Aufschrift `unzip | strip | touch | finger | grep | mount | fsck | unmount | sleep` umher. Alles davon sind Linux-Befehle - auch wenn manche eher selten benutzt werden. Jedoch ergeben sie in dieser Reihenfolge keinen inhaltlichen Sinn. Da ich neulich auf einige Threads gestoßen bin, möchte ich eine kleine Liste mit Anweisungen erstellen, welche auf anzüglich sind, jedoch wirklich funktionieren.

## Manuel-Seiten bieten viel Potenzial
```sh
man unzip
```
Die eigentliche Bedeutung ist, dass man die **Man**uel-Seite vom Programm `unzip` aufruft. Die übertragene Bedeutung ist recht offensichtlich und bedeutet, dass man einen Mann auszieht.
Eine Umkehrung des Befehls funktioniert auch:
```sh
unzip man
```
Dieser Befehl würde eine ZIP-Datei mit dem Namen `man`, `man.zip` oder `man.ZIP` extrahieren.

```sh
man cat
```
Auch mit diesem Kommando ruft man die eine Manuel-Seite auf, jedoch diesmal von Programm `cat`, welche für die Ausgabe und Zusammenführung von Datenströmen benutzt wird. Die Übertragene trifft das Wort "Catboy" (also Katzenjunge) vermutlich am besten.

```sh
man touch
```
In diesem Beispiel kommen wieder die schönen Manuel-Seiten auf, diesmal jedoch vom Programm `touch`, welches dazu verwendet werden kann, leere Dateien zu erstellen. Die übertragene Bedeutung ist entsprechend der physischen Berührung eines Mannes.
Eine Umkehrung der Wörter würde auch funktionieren:
```sh
touch man
```
Dies würde eine leere Datei mit dem Namen `man` erstellen. Die übertragene Bedeutung bleibt gleich.

```sh
man finger
```
Eine recht provokante übertragene Bedeutung, einen Mann nicht nur anzupassen, sondern "zu fingern", also eine Stimulation an seinem Gesäß durchzuführen. `finger` ist an sich jedoch ein Programm, um mit dem alten Finger-Protokoll zu interagieren. Dieses ist in RFC1288 beschrieben und verwendet den TCP Port 79. Es wurde verwendet, um Benutzerinformationen abzufragen. Eine Alternative zum Abrufen dieser Informationen ist der grafische Browser [Lagrange](https://gmi.skyjake.fi/lagrange/).
Den Befehl `finger man` gibt es auch. Er ruft Informationen über den Benutzer `man` auf. Die übertragende Bedeutung bleibt gleich.

```sh
man man
```
Hier ist die übertragende Bedeutung wieder recht eindeutig - sie besteht in dem Akt zwischen zwei Personen männlichen Geschlechts. Der Befehl selber ruft die Manuel-Seite über den Befehl `man` auf. Dieser wird wiederum verwendet, um Manuel-Seiten aufzurufen. Den Befehl `man woman` gibt es nicht.

Dazu gibt es auch einen Comic:
![Man sieht einen gezeichneten fünf Panel Comic.
Auf dem ersten Panel sieht man ein Büro mit sehr schlichten Hintergrund (blau und weiß) sowie einen Fuchs mit Brille, welcher an einem Schreibtisch sitzt und auf seinen Computermonitor schaut. An der angedeuteten Tür steht ein Art Tiger und fragt: "How's the new project coming?". Beide Tiere sind anthropomorph (also in Menschengestalt). Der Fuchs hat ein blaues T-Shirt an, der Tiger ein Grünes. Der Tiger hat auch einen Becher in der Hand.
Im zweiten Panel sieht man den Fuchs reingezoomt wie er aufschaut und "hmm?" sagt.
Im dritten Panel sieht man den Fuchs, sich in einer abwehrenden Haltung vom Schreibtisch weg stoßend, wie er sagt "Oh, I was just looking at some ... man ... pages.".
Im vierten Panel sieht man den Tiger lässig an die Tür gelehnt. Den Becher beinah am Mund wie er sagt "Sure! It's always good to brush up on documentation.".
Im fünften Panel sieht man den Fuchs von hinten. Man kann nun sehen, dass auf seinem Monitor ein Tiger in Badeslip am Strand angezeigt wird. Der Tiger auf dem Monitor ist recht muskulös. Man sieht im Hintergrund einen Strand, Meer und zwei angeschnittene Palmen im Hintergrund.
Der Tiger in der Tür lächelt (grinst aber nicht). Der Fuchs vor dem Computer sieht etwas erleichtert und zugleich erschrocken aus. Er sagt "Yes, right.".](/assets/img/manuel-page-joke.png)

Das Bild ist von [Imgur](https://i.imgur.com/X3scSbg.png). Die Kommandos ist von [Reddit](https://www.reddit.com/r/furry/comments/3owhpl/a_stupid_linuxprogramming_comic_about_looking_at/).

## Schlusswort

Wenn ihr weitere witzige Comics zu dem Thema habt oder andere Befehle, schreibt mir gerne eine E-Mail, sodass ich sie hier mit aufnehmen kann.]]>
        </content>
    </entry>
    
    <entry>
        <title>Lewd and naughty Linux commands</title>
        
        <link rel="alternate" href="https://mk16.de/blog/lewd-linux-commands/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/lewd-linux-commands/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/lewd-linux-commands/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/lewd-linux-commands/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/lewd-linux-commands/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/lewd-linux-commands/" hreflang="en" />

        <id>/blog/lewd-linux-commands/</id>
        <published>2023-07-19T01:00:00+02:00</published>
        <updated>2023-07-19T01:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Can a computer be naughty and lewd? With the command `man finger` they seem to be able to &amp;quot;finger&amp;quot; at least men.</summary>
        <content type="html">
            <![CDATA[**CW: This page contains lewd Linux commands.**

For a long time now, a T-shirt with the inscription `unzip | strip | touch | finger | grep | mount | fsck | unmount | sleep` has been circling around the Internet. All of them are Linux commands - even if some are used rather rarely. However, they don't make any substantive sense in that order. Since I recently came across some threads, I'd like to make a small list of instructions which are lewd, but really work.

## Manuel pages offer a lot of potential
```sh
man unzip
```
The real meaning is that one opens the **man**uel page from the program `unzip`. The figurative meaning is quite obvious and means to undress a man.
A reversal of the command also works:
```sh
unzip man
```
This command would extract a ZIP file named `man`, `man.zip` or `man.ZIP`.

```sh
man cat
```
Also with this command one opens a Manuel page, but this time from the program `cat`, which is used for the output and merging of data streams. figurative meaning probably fits the word "catboy" best.

```sh
man touch
```
In this example, the beautiful Manuel pages appear again, but this time from the program `touch`, which can be used to create empty files. The figurative meaning is corresponding to the physical contact with a man.
A reversal of the words would also work:
```sh
touch man
```
This would create an empty file with the name `man`. The figurative meaning remains the same.

```sh
man finger
```
A rather provocative figurative meaning, not only to contact with a man, but to "finger" him, that is, to perform a stimulation on his buttocks. However, `finger` is in itself a program to interact with the old finger protocol. This is described in RFC1288 and uses TCP port 79, and was used to retrieve user information. An alternative for retrieving this information is the graphical browser [Lagrange](https://gmi.skyjake.fi/lagrange/).
The `finger man` command also exists. It retrieves information about the user `man`. The figurative meaning remains the same.

```sh
man man
```
Here again the figurative meaning is quite clear - it consists in the act between two persons of male gender. The command itself opens the Manuel page via the command `man`. This in turn is used to opens Manuel pages. The command `man woman` does not exist.

There is also a comic about this:
![You see a drawn five panel comic.
On the first panel you see an office with a very plain background (blue and white) and a fox with glasses sitting at a desk and looking at his computer monitor. At the implied door stands a kind of tiger asking "How's the new project coming?". Both animals are anthropomorphic (that is, in human form). The fox has a blue t-shirt on, the tiger a green one. The tiger also has a cup in his hand.
In the second panel you see the fox zoomed in as he looks up and says "hmm?".
In the third panel, you see the fox, pushing himself away from the desk in a defensive posture, saying "Oh, I was just looking at some ... man ... pages.".
In the fourth panel, the tiger is seen leaning casually against the door. The mug almost at his mouth as he says "Sure! It's always good to brush up on documentation.".
In the fifth panel you see the fox from behind. You can now see that on his monitor a tiger in swimming briefs is displayed on the beach. The tiger on the monitor is quite muscular. You can see a beach, sea and two cut palm trees in the background.
The tiger in the doorway is smiling (but not grinning). The fox in front of the computer looks a bit relieved and scared at the same time. He says "Yes, right.".](/assets/img/manuel-page-joke.png)

The image is from [Imgur](https://i.imgur.com/X3scSbg.png). The commands are from [Reddit](https://www.reddit.com/r/furry/comments/3owhpl/a_stupid_linuxprogramming_comic_about_looking_at/).

## Conclusion

If you have any other funny comics on the subject or any other commands, feel free to email me so I can include them here.]]>
        </content>
    </entry>
    
    <entry>
        <title>Das meshname DNS-System</title>
        
        <link rel="alternate" href="https://mk16.de/blog/meshname-de/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/meshname-de/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/meshname-de/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/meshname-de/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/meshname-de/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/meshname-de/" hreflang="de" />

        <id>/blog/meshname-de/</id>
        <published>2024-12-04T00:00:00+01:00</published>
        <updated>2024-12-04T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Was ist meshname? Wie löst man .meshname und .meship-Domains auf? Und wie funktioniert das Ganze? Was steckt dahinter?</summary>
        <content type="html">
            <![CDATA[meshname ist ein dezentrales DNS-System. Dezentral bedeutet bei Meshname, dass es ohne zentrale Server wie Root-Server auskommt. Je nach Auflösungsmethode wird auch überhaupt kein Server benötigt.

## Geschichte

Die [erste Implementierung](https://github.com/zhoreeq/meshname/) wurde am 19. Januar 2020 von zhoreeq auf GitHub veröffentlicht und in Go geschrieben. Die Referenzimplementierung stellt lediglich ein Kommandozeilentool zur Auflösung bzw. Kodierung sowie einen einfachen DNS-Server bereit.
Am 24. Oktober 2021 kam die [zweite Implementierung](https://notabug.org/acetone/mario-dns) von acetone mit dem Namen `mario-dns` auf. Diese wurde in C++ mit Qt5 geschrieben und bietet ein Web-Interface mit REST API an.
Die [dritte Implementierung](https://codeberg.org/mark22k/meshname/) habe ich geschrieben und am 4. Januar 2022 erstmalig veröffentlicht. Ich habe diese in Ruby geschrieben und als Bibliothek konzipiert. Sie bietet die Möglichkeit an, meshname Kodierungen und einfache Auflösungen durchzuführen.

## Protokoll

Das [Meshname-Protokoll](https://github.com/zhoreeq/meshname/blob/master/protocol.md) definiert zwei Top-Level-Domains (TLDs):
- `.meshname`
- `.meship`

`.meship`-Domains können nur auf eine IPv6-Adresse aufgelöst werden. `.meshname`-Domains brauchen einen autoritativen DNS-Server, welcher eine erreichbare IPv6-Adresse hat.

Die Auflösung und vollständige Erstellung von Domains unterscheiden sich daher nach TLD, jedoch ist das grundsätzliche Verfahren das Gleiche.

### IP-Adressen in Domain umwandeln

1. Der erste Schritt besteht darin, die IPv6-Adresse in ihre binäre Darstellung umzuwandeln:
  In Ruby kann man dies mit folgendem Befehl machen, wenn man `ipaddr` importiert hat: `IPAddr.new("200:6fc8:9220:f400:5cc2:305a:4ac6:967e").hton`
  Wenn man die vollständige IPv6-Adresse mit führenden Nullen hat, kann man auch POSIX-Werkzeugen verwenden:
  `echo "0200:6fc8:9220:f400:5cc2:305a:4ac6:967e" | tr -d ':' | xxd -revert -plain`
  Das Ergebnis ist `\x02\x00o\xC8\x92 \xF4\x00\\\xC20ZJ\xC6\x96~`.
2. Diesen binären String konvertiert man nun mithilfe von base32 in einen menschenlesbaren String:
  `echo "0200:6fc8:9220:f400:5cc2:305a:4ac6:967e" | tr -d ':' | xxd -revert -plain | base32`
  Das Ergebnis ist hier `AIAG7SESED2AAXGCGBNEVRUWPY======`.
3. Danach entfernt man das Padding (also `=`-Zeichen) und schreibt alle Buchstaben klein:
  `echo "0200:6fc8:9220:f400:5cc2:305a:4ac6:967e" | tr -d ':' | xxd -revert -plain | base32 | tr -d '=' | tr '[:upper:]' '[:lower:]'`
  Das Ergebnis ist dann `aiag7sesed2aaxgcgbnevruwpy`.

### .meship

An das eben gewonnene Ergebnis kann man indessen `.meship` anhängen. Wenn eine `.meship`-Domain aufgelöst wird, wird das Verfahren umgekehrt und immer die kodierte IPv6-Adresse als AAAA-Rekord zurückgegeben. `.meship` benötigen daher keinen zentralen Server, da die IP-Adresse im Domainnamen selber ist.

### .meshname

Man kann an das gewonnene Ergebnis jedoch auch `.meshname` anhängen. In diesem Fall bedeutet dies, dass die kodierte Adresse, die IP-Adresse des autoritativen DNS-Servers ist. Wenn also der TXT-Rekord von `aiag7sesed2aaxgcgbnevruwpy.meshname` abgefragt wird, wird die IP-Adresse dekodiert und der entsprechende DNS-Server `200:6fc8:9220:f400:5cc2:305a:4ac6:967e` nach einem TXT-Rekord für die Domain `aiag7sesed2aaxgcgbnevruwpy.meshname` befragt.

### Auflösung

Um eine meshname-Domain aufzulösen, wird als Erstes die Kodierung umgekehrt:
1. Base32 Dekodierung der Hauptdomain (ohne TLD und Subdomains).
2. Der binäre String, welcher daraus resultiert, wird dann in eine IPv6-Adresse umgewandelt.
3.
    1. Falls die TLD `.meship` ist, wird die IPv6-Adresse als AAAA-Rekord zurückgegeben. Wird ein anderer Rekord oder eine Subdomain angefragt, wird nichts bzw. eine Fehlermeldung zurückgegeben.
    2. Falls die TLD `.meshname` ist, wird der DNS-Server befragt, dessen IP-Adresse in der Domain kodiert ist. Daher sind bei dieser Methode auch andere Rekords als `AAAA` sowie Subdomain möglich.

## Zooko's Dreieck

[Zooko's Dreieck](https://de.wikipedia.org/wiki/Zookos_Dreieck) beschreibt das Trilemma, dass eine Domain immer nur zwei von drei Eigenschaften haben kann:
    - Frei wählbar: Der Domainname ist frei wählbar oder sehr leicht beeinflussbar.
    - Dezentral: Die Auflösung der Domain ist nicht von einem zentralen Server abhängig.
    - Sicher: Es werden die vom Eigentümer hinterlegten Rekords zurückgegeben und dies kann sichergestellt werden.

Beispiele:
- Mit DNSSEC gesicherte Domains sind sowohl frei wählbar als auch sicher, jedoch nicht dezentral, da ihre Auflösung von zentralen Root-Servern abhängt.
- .onion oder .b32.i2p-Domains sind dezentral und sicher, jedoch nicht frei wählbar.

meshname-Domains sind nicht frei wählbar, da sie von einer IP-Adresse abhängen. Durch eine geschickte Wahl der IP-Adresse (beispielsweise, wenn man ein größeres Subnetz hat) kann man zwar einfachere Domains generieren, jedoch hängen diese immer noch stark von der IP-Adresse ab. Darüber hinaus haben meshname-Domains immer dieselbe Länge.
`.meship`-Domains sind sowohl dezentral (da man sie ohne externen Server auflösen kann) als auch sicher (da die IP-Adresse bereits im Namen selber kodiert ist).
`.meshname`-Domains sind dezentral, da keine zentralen Server benötigt werden. Es wird lediglich ein einziger externer Server zur Auflösung benötigt. Jedoch sind `.meshname`-Domains nur bedingt sicher. Es wird zwar immer der richtige autoritative DNS-Server befragt, jedoch kann die Antwort nicht validiert werden. `.meshname`-Domains sind also für Man-in-the-Middle-Angriffe anfällig, wenn jemand die Verbindung zwischen Server, der auflöst und dem autoritativen DNS-Server kontrolliert.

## Praxis

Um die Referenzimplementierung zu bauen, muss diese als erstes heruntergeladen werden:
```
$ git clone https://github.com/zhoreeq/meshname.git
```

Danach kann man das Programm mit `make` bauen:
```
$ cd meshname/
$ make
```

Danach erhält man im selben Verzeichnis eine Datei `meshnamed`, welche ausführbar ist. Startet man diese ohne weitere Argumente, so wird ein DNS-Server auf `[::1]:53535` gestartet, welchen man befragen kann:
```
$ ./meshnamed 
2024/01/31 11:04:13 Listening on: [::1]:53535
```
```
$ dig @::1 -p 53535 aiag7sesed2aaxgcgbnevruwpy.meship AAAA +short
200:6fc8:9220:f400:5cc2:305a:4ac6:967e
```

Um eine IP-Adresse in eine Domain umzuwandeln, kann man folgenden Befehl nutzen:
```
$ ./meshnamed -getname 200:6fc8:9220:f400:5cc2:305a:4ac6:967e
aiag7sesed2aaxgcgbnevruwpy
```

Um die Domain wiederum in eine IP-Adresse umzuwandeln, kann man so etwas ausführen:
```
$ ./meshnamed -getip aiag7sesed2aaxgcgbnevruwpy
200:6fc8:9220:f400:5cc2:305a:4ac6:967e
```

## Anwendungsbereich & Schluss

meshname-Domains sollten laut Protokollspezifikation primär von Maschinen verwendet werden - beispielsweise um sich zu einem Peer oder Server zu verbinden. Dennoch finde ich es persönlich spaßig, meshname-Domains in Netzwerken wie Yggdrasil zu verwenden.

## Links

- [What is correct SOA Record for .meshname domains](https://github.com/zhoreeq/meshname/issues/19)]]>
        </content>
    </entry>
    
    <entry>
        <title>The meshname DNS system</title>
        
        <link rel="alternate" href="https://mk16.de/blog/meshname-en/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/meshname-en/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/meshname-en/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/meshname-en/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/meshname-en/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/meshname-en/" hreflang="en" />

        <id>/blog/meshname-en/</id>
        <published>2024-12-04T00:00:00+01:00</published>
        <updated>2024-12-04T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>What is meshname? How do you resolve .meshname and .meship domains? And how does it all work? What&apos;s behind it?</summary>
        <content type="html">
            <![CDATA[meshname is a decentralized DNS system. With meshname, decentralized means that it works without central servers such as root servers. Depending on the resolution method, no server is required at all.

## History

The [first implementation](https://github.com/zhoreeq/meshname/) was published on January 19, 2020 by zhoreeq on GitHub and is written in Go. The reference implementation provides a command line tool for resolution/encoding and a simple DNS server.
On October 24, 2021, the [second implementation](https://notabug.org/acetone/mario-dns) by acetone was released under the name `mario-dns`. This is written in C++ with Qt5 and offers a web interface with REST API.
I wrote the [third implementation](https://codeberg.org/mark22k/meshname/) and published it on January 4, 2022. I wrote it in Ruby and designed it as a library. It offers the possibility to perform meshname coding and simple resolutions.

## Protocol

The [meshname protocol](https://github.com/zhoreeq/meshname/blob/master/protocol.md) defines two top-level domains (TLDs):
- `.meshname`
- `.meship`

`.meship` domains can only be resolved to one IPv6 address. `.meshname` domains need an authoritative DNS server which has a reachable IPv6 address.

The resolution and complete creation of domains therefore differ depending on the TLD, but the basic procedure is the same.

### Converting IP addresses into domains

1. The first step is to convert the IPv6 address into its binary representation:
  In Ruby you can do this with the following command if you have imported `ipaddr`: `IPAddr.new("200:6fc8:9220:f400:5cc2:305a:4ac6:967e").hton`
  If you have the complete IPv6 address with leading zeros, you can also use POSIX tools:
  `echo "0200:6fc8:9220:f400:5cc2:305a:4ac6:967e" | tr -d ':' | xxd -revert -plain`
  The result is `\x02\x00o\xC8\x92 \xF4\x00\\\xC20ZJ\xC6\x96~`.
2. This binary string is now converted into a human-readable string using base32:
  `echo "0200:6fc8:9220:f400:5cc2:305a:4ac6:967e" | tr -d ':' | xxd -revert -plain | base32`
  The result here is `AIAG7SESED2AAXGCGBNEVRUWPY======`.
3. Then remove the padding (the `=` sign) and write all letters in lower case:
  `echo "0200:6fc8:9220:f400:5cc2:305a:4ac6:967e" | tr -d ':' | xxd -revert -plain | base32 | tr -d '=' | tr '[:upper:]' '[:lower:]'`
  The result is then `aiag7sesed2aaxgcgbnevruwpy`.

### .meship

Meanwhile, you can append `.meship` to the result just obtained. If a `.meship` domain is resolved, the procedure is reversed and the encoded IPv6 address is always returned as the AAAA record. Therefore, `.meship` do not require a central server, as the IP address is in the domain name itself.

### .meshname

However, you can also append `.meshname` to the result obtained. In this case, this means that the encoded address is the IP address of the authoritative DNS server. So if the TXT record of `aiag7sesed2aaxgcgbnevruwpy.meshname` is queried, the IP address is decoded and the corresponding DNS server `200:6fc8:9220:f400:5cc2:305a:4ac6:967e` is queried for a TXT record for the domain `aiag7sesed2aaxgcgbnevruwpy.meshname`.

### Resolution

To resolve a meshname domain, the first step is to reverse the encoding:
1. base32 decoding of the main domain (without TLD and subdomains).
2. The resulting binary string is then converted into an IPv6 address.
3.
    1. If the TLD is `.meship`, the IPv6 address is returned as an AAAA record. If a different record or a subdomain is requested, nothing or an error message is returned.
    2. If the TLD is `.meshname`, the DNS server whose IP address is encoded in the domain is queried. Therefore, records other than `AAAA` and subdomain are also possible with this method.

## Zooko's triangle

[Zooko's triangle](https://en.wikipedia.org/wiki/Zooko%27s_triangle) describes the trilemma that a domain can only ever have two of three properties:
    - Freely selectable: the domain name can be freely selected or very easily influenced.
    - Decentralized: The resolution of the domain is not dependent on a central server.
    - Secure: The records stored by the owner are returned and the records can be verified.

Examples:
- Domains secured with DNSSEC are both freely selectable and secure, but not decentralized, as their resolution depends on central root servers.
- .onion or .b32.i2p domains are decentralized and secure, but not freely selectable.

meshname domains are not freely selectable as they depend on an IP address. A clever choice of IP address (for example, if you have a larger subnet) can generate simpler domains, but these are still heavily dependent on the IP address. In addition, meshname domains always have the same length.
`.meship` domains are both decentralized (as they can be resolved without an external server) and secure (as the IP address is already encoded in the name itself).
`.meshname` domains are decentralized because no central servers are required. Only a single external server is required for resolution. However, `.meshname` domains are only partially secure. Although the correct authoritative DNS server is always queried, the answer cannot be validated. This means that `.meshname` domains are vulnerable to man-in-the-middle attacks if someone controls the connection between the resolving server and the authoritative DNS server.

## Practice

In order to build the reference implementation, it must first be downloaded:
```
$ git clone https://github.com/zhoreeq/meshname.git
```

Then you can build the program with `make`:
```
$ cd meshname/
$ make
```

You will then receive a file `meshnamed` in the same directory, which is executable. If you start this without any further arguments, a DNS server is started at `[::1]:53535`, which you can query:
```
$ ./meshnamed 
2024/01/31 11:04:13 Listening on: [::1]:53535
```
```
$ dig @::1 -p 53535 aiag7sesed2aaxgcgbnevruwpy.meship AAAA +short
200:6fc8:9220:f400:5cc2:305a:4ac6:967e
```

To convert an IP address into a domain, you can use the following command:
```
$ ./meshnamed -getname 200:6fc8:9220:f400:5cc2:305a:4ac6:967e
aiag7sesed2aaxgcgbnevruwpy
```

To convert the domain back into an IP address, you can do something like this:
```
$ ./meshnamed -getip aiag7sesed2aaxgcgbnevruwpy
200:6fc8:9220:f400:5cc2:305a:4ac6:967e
```

## Area of application & conclusion

According to the protocol specification, meshname domains should primarily be used by machines - for example to connect to a peer or server. Nevertheless, I personally find it fun to use meshname domains in networks like Yggdrasil.

## Links

- [What is correct SOA Record for .meshname domains](https://github.com/zhoreeq/meshname/issues/19)]]>
        </content>
    </entry>
    
    <entry>
        <title>mtr metrics</title>
        
        <link rel="alternate" href="https://mk16.de/blog/mtr-metrics/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/mtr-metrics/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/mtr-metrics/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/mtr-metrics/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/mtr-metrics/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/mtr-metrics/" hreflang="de" />

        <id>/blog/mtr-metrics/</id>
        <published>2025-02-17T00:00:00+01:00</published>
        <updated>2025-02-17T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>mtr is a combination of traceroute and ping, and can generate a wide variety of metrics. But how are these calculated and what do they mean?</summary>
        <content type="html">
            <![CDATA[## What is mtr?

mtr is a network diagnostics tool that combines traceroute and ping. It determines the path to a destination and updates it continuously. In addition, mtr determines the round trip time (RTT) for each individual hop on the path. In addition to the RTT, further metrics are calculated for each hop and the destination. These include: the packet loss, the best RTT, the average RTT, the worst RTT, the standard deviation of the RTT, the geometric mean of the RTT, the average jitter, the worst jitter and the interarrival jitter (according to [RFC1889](https://www.rfc-editor.org/rfc/rfc1889.html#appendix-A.8)).

## Metrics in mtr

The metrics can only be calculated if a packet returns. Therefore, lost packets cannot be included in the metrics. The lost packets can only be reflected in the packet loss.

The RTT is the time it takes for a packet to get to a hop **and** back.

*Note*: The RTT describes the time it takes for a packet to get there and back. Latency generally describes the time a packet takes from one hop to another - i.e. only the outbound or return path.

### Jitter

Jitter indicates how far apart the various measured RTTs are and therefore how much this fluctuates. Jitter serves a similar function to the standard deviation. Jitter describes how much the packet transit time varies. A low jitter means that the RTT is fairly constant. A high jitter, on the other hand, means that the RTT fluctuates greatly.
Example 1: Three RTTs were measured: 5ms, 6ms, 3ms. The average jitter would be very low at 1ms. The RTT therefore remained quite stable during the measurements.
Example 2: Three RTTs were measured: 10ms, 100ms, 1ms. The average jitter would be very high at 63ms. The RTT is not stable and changes frequently or often deviates from the previous measurements.

### Standard deviation

The standard deviation of the RTT has a similar function to jitter. In the first example from jitter, the standard deviation would be 1ms and for the second example it would be 54ms.

### Metrics in the source code

In mtr, the values are calculated in [ui/net.c](https://github.com/traviscross/mtr/blob/0f96f02f46d8d72603535c9e1481f245c0ebe2e7/ui/net.c#L269-L316). There, the standard deviation is referred to as the ["sum of squares of differences from the current average"](https://github.com/traviscross/mtr/blob/0f96f02f46d8d72603535c9e1481f245c0ebe2e7/ui/net.c#L57). The interarrival jitter is referred to there as ["estimated variance?"](https://github.com/traviscross/mtr/blob/0f96f02f46d8d72603535c9e1481f245c0ebe2e7/ui/net.c#L66).

## Calculation of the metrics

I personally believe that you get a better feel for the metrics if you know how they are calculated. I have therefore created some C code for the calculation (adapted from ui/net.c) as well as a mathematical notation for the calculation.

### Example values

To illustrate the calculation with concrete values, I carried out a real mtr measurement (10 cycles long). For the fun of it, I did this using an old smartphone on which I had installed Kali NetHunter and which was connected to a Freifunk router. This Freifunk router also had an interconnection to the dn42 through the IC-VPN, so I could ping Burble's pingable address. I recorded the measurement with `tcpdump` and cut out the 20 relevant packets (see below). The corresponding pcapng file can be found [here](/assets/pcap/mtr-metrics-example.pcapng). I executed mtr with the following command:

```
$ mtr -4 --report --json --show-ips --order "LDRSBAWVGMXI" --report-cycles 10 --psize 400 --bitpattern 256 pingable.burble.dn42
```

mtr has output its measurement as JSON (formatted with `jq`):
```json
{
  "report": {
    "mtr": {
      "src": "kali",
      "dst": "pingable.burble.dn42",
      "tos": 0,
      "tests": 10,
      "psize": "400",
      "bitpattern": "rand(0x00-FF)"
    },
    "hubs": [
      {
        "count": 1,
        "host": "gw4.ff3l (10.119.0.5)",
        "Loss%": 10.0,
        "Drop": 1,
        "Rcv": 9,
        "Snt": 10,
        "Best": 34.079,
        "Avg": 36.272,
        "Wrst": 38.427,
        "StDev": 1.473,
        "Gmean": 36.245,
        "Javg": 1.536,
        "Jmax": 3.513,
        "Jint": 10.83
      },
      {
        "count": 2,
        "host": "gw3.ff3l (10.119.0.4)",
        "Loss%": 0.0,
        "Drop": 0,
        "Rcv": 10,
        "Snt": 10,
        "Best": 41.578,
        "Avg": 45.264,
        "Wrst": 50.228,
        "StDev": 2.671,
        "Gmean": 45.194,
        "Javg": 3.176,
        "Jmax": 8.65,
        "Jint": 24.754
      },
      {
        "count": 3,
        "host": "p2pnode.bandura.dn42 (172.22.149.225)",
        "Loss%": 0.0,
        "Drop": 0,
        "Rcv": 10,
        "Snt": 10,
        "Best": 55.045,
        "Avg": 57.674,
        "Wrst": 60.757,
        "StDev": 1.985,
        "Gmean": 57.643,
        "Javg": 1.668,
        "Jmax": 4.043,
        "Jint": 13.546
      },
      {
        "count": 4,
        "host": "pingable.burble.dn42 (172.20.129.5)",
        "Loss%": 0.0,
        "Drop": 0,
        "Rcv": 10,
        "Snt": 10,
        "Best": 60.776,
        "Avg": 63.143,
        "Wrst": 67.09,
        "StDev": 1.927,
        "Gmean": 63.115,
        "Javg": 2.568,
        "Jmax": 6.314,
        "Jint": 18.246
      }
    ]
  }
}
```

The measurement contains the final values, but not the individual RTT values. I extracted these from the pcapng file using WireShark. Since the packets arriving at the interface (recorded by tcpdump) can have a delay until they arrive at mtr and mtr processes them, my final values differ slightly from those of mtr.

![You can see a wooden table on which a smartphone is lying. You can see the smartphone in full, but it is very close to the picture. In the background you can see the top of a laptop. The smartphone is bright pink with a black case. It is from Samsung and has three physical buttons at the bottom. On the screen of the smartphone you can see a terminal in which mtr is running. In the notification bar you can see that a screen recording is currently running, a terminal is running in the background, the smartphone is connected to a VPN and WLAN, that it is over 50% charged and that the current time is 02:49. The terminal is logged in as “root@kali: /”. At the bottom you can see extra keys for operation (Esc, Alt, Ctrl, Tab, PgUp, PgDown, Home, End and the arrow keys). The terminal itself is running mtr version 0.95. The source address is fdc7:3c9d:b889:a272:2974:b580:9586:4bb3 and the destination address is fd42:4242:2601:ac05::1. There are four hops (all with a packet loss of 0%, a little over 65 packets were sent to all of them): 1. fdc7:3c9d:b889:a272::5, 2. fdc7:3c9d:b889:a272::4, 3. fd04:234e:fc31::1, 4. the destination fd42:4242:2601:ac05::1.](/assets/img/mtr-metrics-kali-nethunter.jpg) 

*Note*: The pcapng file also contained other data in addition to the mtr measurement. In addition, the mtr measurement consisted of several measurements towards the different hops. For simplicity, I only selected one hop (the target) for the specific values.

*Note*: I did the measurement once successfully with IPv4 and IPv6. IPv6 also worked except for the reverse DNS.

### C code

```c
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

struct measurement_s
{
    int last; // last rtt
    int jitter; // jitter
    int best; // best rtt
    int worst; // worst rtt
    int jworst; // worst jitter
    int returned; // number of pings
    int avg; // average rtt
    long long ssd; // standard deviation
    int javg; // average jitter
    int jinta; // interarrival jitter
    int gmean; // geometric mean
};

typedef struct measurement_s measurement_t;

static inline int max(int a, int b)
{
    return (a > b ? a : b);
}

static inline int min(int a, int b)
{
    return (a < b ? a : b);
}

void process_ping(measurement_t * const m, const int rtt);
void post_process_measurement(measurement_t * const m);

int main(void)
{
    int rtt[] = {
        61172, 64956, 60273, 66617, 61781, 60847, 63437, 61955, 62766, 62580};
    size_t rtt_n = sizeof(rtt) / sizeof(*rtt); // Number of measurements

    measurement_t m = {.last = 0,
                       .jitter = 0,
                       .best = 0,
                       .worst = 0,
                       .jworst = 0,
                       .returned = 0,
                       .avg = 0,
                       .ssd = 0,
                       .javg = 0,
                       .jinta = 0,
                       .gmean = 0};

    for (size_t i = 0; i < rtt_n; i++)
    {
        process_ping(&m, rtt[i]);
    }

    post_process_measurement(&m);

    printf("last = %d\n"
           "jitter = %d\n"
           "best = %d\n"
           "worst = %d\n"
           "jworst = %d\n"
           "returned = %d\n"
           "avg = %d\n"
           "ssd = %lld\n"
           "javg = %d\n"
           "jinta = %d\n"
           "gmean = %d\n",
           m.last,
           m.jitter,
           m.best,
           m.worst,
           m.jworst,
           m.returned,
           m.avg,
           m.ssd,
           m.javg,
           m.jinta,
           m.gmean);

    return EXIT_SUCCESS;
}

void process_ping(measurement_t * const m, const int rtt)
{
    m->returned++;

    if (m->returned <= 1) // first ping
    {
        m->jitter = m->jworst = m->ssd = m->javg = m->jinta = 0;
        m->best = m->worst = m->avg = m->gmean = rtt;
    }
    else
    {
        // With `.0` a floating point calculation is forced.

        m->jitter = abs(rtt - m->last);
        m->best = min(m->best, rtt);
        m->worst = max(m->worst, rtt);
        m->jworst = max(m->jworst, m->jitter);

        int oldavg = m->avg;
        m->avg += (rtt - m->avg + .0) / m->returned;
        m->ssd += (rtt - oldavg + .0) * (rtt - m->avg + .0);
        m->javg += (m->jitter - m->javg + .0) / m->returned;
        m->jinta += m->jitter - ((m->jinta + 8) >> 4);
        m->gmean =
            (int) (pow(m->gmean + .0, (m->returned - 1.0) / m->returned) *
                   pow(rtt + .0, 1.0 / m->returned));
    }

    m->last = rtt;
}

void post_process_measurement(measurement_t * const m)
{
    m->ssd = sqrt(m->ssd / (m->returned - 1.0));
}
```

The data for the current round is stored in the `struct` `measurement_s`. Each round - i.e. whenever a ping is received - the function `process_ping` is called. This updates the data in the `struct` based on the RTT value it has received. When the first ping is received, the metrics are filled with specific start values. Depending on the metric, this is either 0 and the current (and at that time only) RTT. Otherwise, the new values are calculated using appropriate formulas.
Due to the properties of the standard deviation, it is only calculated indirectly. To actually obtain it, the actual standard deviation is calculated (once) in the `post_process_measurement` function at the end. This calculation is also carried out in mtr each time the standard deviation is to be displayed.
Finally, everything is output once. Although the attributes `last` and `returned` are necessary for the calculation, they are only of limited interest as metrics. `last` stores the last RTT and `returned` the number of successfully received ping replies.

### mathematical formulation

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>n</mi><mo>,</mo><mi>n</mi><mo>∈</mo><msub><mi>ℕ</mi><mn>0</mn></msub></math>
    <span>specifies the number of RTT measurements.</span>
</div>

The unit of time measurement is microseconds.
Conversion:
```
Microseconds (μs) = miliseconds (ms) * 1000
Miliseconds (ms) = microseconds (μs) / 1000
```
Example:
61172μs is 61.172ms; 64.956ms is 64956μs.

<div>
    <span>The</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>min</mi><mo>⁡</mo><mo>(</mo><mi>a</mi><mo>,</mo><mi>b</mi><mo>)</mo></math>
    <span>function returns the minimum of</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>a</mi></math>
    <span>and</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>b</mi></math>
    <span>. The</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>max</mi><mo>⁡</mo><mo>(</mo><mi>a</mi><mo>,</mo><mi>b</mi><mo>)</mo></math>
    <span>function returns the maximum of</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>a</mi></math>
    <span>and</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>b</mi></math>
    <span>.</span>
</div>

#### RTT

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>rtt</mi><mi>k</mi></msub><mo>,</mo><mi>k</mi><mo>∈</mo><msub><mi>ℕ</mi><mn>0</mn></msub></math>
    <span>specifies the RTT for the</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>k</mi></math>
    <span>th measurement. The RTT is given in microseconds. This enables problem-free calculation in whole numbers and thus simplifies machine calculation. </span>
</div>

<div>
    <p>Example:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable><mtr><mtd><mrow><mi>rtt</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>0</mn></msub><mo>=</mo><mn>61172</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>1</mn></msub><mo>=</mo><mn>64956</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>2</mn></msub><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>3</mn></msub><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>4</mn></msub><mo>=</mo><mn>61781</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>5</mn></msub><mo>=</mo><mn>60847</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>6</mn></msub><mo>=</mo><mn>63437</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>7</mn></msub><mo>=</mo><mn>61955</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>8</mn></msub><mo>=</mo><mn>62766</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>9</mn></msub><mo>=</mo><mn>62580</mn></mrow></mtd></mtr></mtable></math>
</div>

#### Jitter

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>jitter</mi><mi>n</mi></msub></math>
    <span>is the jitter of the</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>n</mi></math><span>th measurement.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>jitter</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mi>k</mi></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><msub><mi>rtt</mi><mi>k</mi></msub><mo>−</mo><msub><mi>rtt</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub></mrow><mo>)</mo></mrow></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Example:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>jitter</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>1</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>64956</mn><mo>−</mo><mn>61172</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>3784</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>2</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>−</mo><mn>64956</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>4683</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>3</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>−</mo><mn>60273</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>4</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>61781</mn><mo>−</mo><mn>66617</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>4836</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>5</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60847</mn><mo>−</mo><mn>61781</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>934</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>6</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>63437</mn><mo>−</mo><mn>60847</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>2590</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>7</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>61955</mn><mo>−</mo><mn>63437</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>1482</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>8</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>62766</mn><mo>−</mo><mn>61955</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>811</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>9</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>62580</mn><mo>−</mo><mn>62766</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>186</mn></mrow></mtd></mtr></mtable></math>
</div>

#### Best RTT

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>best</mi><mi>n</mi></msub></math>
    <span>specifies the best RTT.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>best</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>0</mn></msub><mo>=</mo><msub><mi>rtt</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mi>k</mi></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><msub><mi>best</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>,</mo><msub><mi>rtt</mi><mi>k</mi></msub></mrow><mo>)</mo></mrow></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Example:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>best</mi><mn>0</mn></msub><mo>=</mo><mn>61172</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>1</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>61172</mn><mo>,</mo><mn>64956</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>61172</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>2</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>61172</mn><mo>,</mo><mn>60273</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>3</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>66617</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>4</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>61781</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>5</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>60847</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>6</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>63437</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>7</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>61955</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>8</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>62766</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>9</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>62580</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr></mtable></math>
    <p>60273μs is 60.273ms.</p>
</div>

#### Worst RTT

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>worst</mi><mi>n</mi></msub></math>
    <span>specifies the worst RTT.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>worst</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>0</mn></msub><mo>=</mo><msub><mi>rtt</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mi>k</mi></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><msub><mi>worst</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>,</mo><msub><mi>rtt</mi><mi>k</mi></msub></mrow><mo>)</mo></mrow></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Example:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>worst</mi><mn>0</mn></msub><mo>=</mo><mn>61172</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>1</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>61172</mn><mo>,</mo><mn>64956</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>64956</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>2</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>64956</mn><mo>,</mo><mn>60273</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>64956</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>3</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>64956</mn><mo>,</mo><mn>66617</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>4</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>,</mo><mn>61781</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>5</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>,</mo><mn>60847</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>6</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>,</mo><mn>63437</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>7</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>,</mo><mn>61955</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>8</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>,</mo><mn>62766</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>9</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>,</mo><mn>62580</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr></mtable></math>
    <p>66617μs is 66.617ms.</p>
</div>

#### Average RTT

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>avg</mi><mi>n</mi></msub></math>
    <span>specifies the average RTT.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>avg</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>0</mn></msub><mo>=</mo><msub><mi>rtt</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mi>k</mi></msub><mo>=</mo><mo>⌊</mo><mrow><msub><mi>avg</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><mfrac><mrow><msub><mi>rtt</mi><mi>k</mi></msub><mo>−</mo><msub><mi>avg</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub></mrow><mrow><mi>k</mi><mo>+</mo><mn>1</mn></mrow></mfrac></mrow><mo>⌋</mo></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Example:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>avg</mi><mn>0</mn></msub><mo>=</mo><mn>61172</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>1</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>61172</mn><mo>+</mo><mfrac><mrow><mn>64956</mn><mo>−</mo><mn>61172</mn></mrow><mn>2</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>63064</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>2</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>63064</mn><mo>+</mo><mfrac><mrow><mn>60273</mn><mo>−</mo><mn>63064</mn></mrow><mn>3</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62133</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>3</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>62133</mn><mo>+</mo><mfrac><mrow><mn>66617</mn><mo>−</mo><mn>62133</mn></mrow><mn>4</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>63254</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>4</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>63254</mn><mo>+</mo><mfrac><mrow><mn>61781</mn><mo>−</mo><mn>63254</mn></mrow><mn>5</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62959</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>5</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>62959</mn><mo>+</mo><mfrac><mrow><mn>60847</mn><mo>−</mo><mn>62959</mn></mrow><mn>6</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62607</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>6</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>62607</mn><mo>+</mo><mfrac><mrow><mn>63437</mn><mo>−</mo><mn>62607</mn></mrow><mn>7</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62725</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>7</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>62725</mn><mo>+</mo><mfrac><mrow><mn>61955</mn><mo>−</mo><mn>62725</mn></mrow><mn>8</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62628</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>8</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>62628</mn><mo>+</mo><mfrac><mrow><mn>62766</mn><mo>−</mo><mn>62628</mn></mrow><mn>9</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62643</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>9</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>62643</mn><mo>+</mo><mfrac><mrow><mn>62580</mn><mo>−</mo><mn>62643</mn></mrow><mn>10</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62636</mn></mrow></mtd></mtr></mtable></math>
    <p>62636μs is 62.636ms.</p>
</div>

#### Standard deviation

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>σ</mi></math>
    <span>specifies the standard deviation of the RTT.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>ssd</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mi>k</mi></msub><mo>=</mo><mo>⌊</mo><mrow><msub><mi>ssd</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><mrow><mo>(</mo><mrow><msub><mi>rtt</mi><mi>k</mi></msub><mo>−</mo><msub><mi>avg</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><msub><mi>rtt</mi><mi>k</mi></msub><mo>−</mo><msub><mi>avg</mi><mi>k</mi></msub></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo></mrow></mtd></mtr><mtr><mtd><mrow><mi>σ</mi><mo>=</mo><mo>⌊</mo><msqrt><mfrac><msub><mi>ssd</mi><mi>n</mi></msub><mi>n</mi></mfrac></msqrt><mo>⌋</mo></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <span>Example:</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>ssd</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>1</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>0</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>64956</mn><mo>−</mo><mn>61172</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>64956</mn><mo>−</mo><mn>63064</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>7159328</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>2</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>7159328</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>−</mo><mn>63064</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>−</mo><mn>62133</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>12350588</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>3</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>12350588</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>−</mo><mn>62133</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>−</mo><mn>63254</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>27430280</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>4</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>27430280</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>61781</mn><mo>−</mo><mn>63254</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>61781</mn><mo>−</mo><mn>62959</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>29165474</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>5</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>29165474</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>60847</mn><mo>−</mo><mn>62959</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>60847</mn><mo>−</mo><mn>62607</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>32882594</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>6</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>32882594</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>63437</mn><mo>−</mo><mn>62607</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>63437</mn><mo>−</mo><mn>62725</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>33473554</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>7</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>33473554</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>61955</mn><mo>−</mo><mn>62725</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>61955</mn><mo>−</mo><mn>62628</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>33991764</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>8</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>33991764</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>62766</mn><mo>−</mo><mn>62628</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>62766</mn><mo>−</mo><mn>62643</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>34008738</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>9</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>34008738</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>62580</mn><mo>−</mo><mn>62643</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>62580</mn><mo>−</mo><mn>62636</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>34012266</mn></mrow></mtd></mtr><mtr><mtd><mrow><mi>σ</mi><mo>=</mo><mo>⌊</mo><msqrt><mfrac><mn>34012266</mn><mn>9</mn></mfrac></msqrt><mo>⌋</mo><mo>=</mo><mn>1944</mn></mrow></mtd></mtr></mtable></math>
    <p>1944μs is 1.944ms.</p>
</div>

#### geometric mean

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>gmean</mi><mi>n</mi></msub></math>
    <span>specifies the geometric mean of the RTT.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>gmean</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>0</mn></msub><mo>=</mo><msub><mi>rtt</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mi>k</mi></msub><mo>=</mo><mo>⌊</mo><mrow><msup><msub><mi>gmean</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mfrac><mi>k</mi><mrow><mi>k</mi><mo>+</mo><mn>1</mn></mrow></mfrac></msup><mo>⋅</mo><msup><msub><mi>rtt</mi><mi>k</mi></msub><mfrac><mn>1</mn><mrow><mi>k</mi><mo>+</mo><mn>1</mn></mrow></mfrac></msup></mrow><mo>⌋</mo></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Example:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>gmean</mi><mn>0</mn></msub><mo>=</mo><mn>61172</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>1</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>61172</mn><mfrac><mn>1</mn><mn>2</mn></mfrac></msup><mo>⋅</mo><msup><mn>64956</mn><mfrac><mn>1</mn><mn>2</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>63035</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>2</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>63035</mn><mfrac><mn>2</mn><mn>3</mn></mfrac></msup><mo>⋅</mo><msup><mn>60273</mn><mfrac><mn>1</mn><mn>3</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62100</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>3</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>62100</mn><mfrac><mn>3</mn><mn>4</mn></mfrac></msup><mo>⋅</mo><msup><mn>66617</mn><mfrac><mn>1</mn><mn>4</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>63199</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>4</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>63199</mn><mfrac><mn>4</mn><mn>5</mn></mfrac></msup><mo>⋅</mo><msup><mn>61781</mn><mfrac><mn>1</mn><mn>5</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62912</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>5</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>62912</mn><mfrac><mn>5</mn><mn>6</mn></mfrac></msup><mo>⋅</mo><msup><mn>60847</mn><mfrac><mn>1</mn><mn>6</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62563</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>6</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>62563</mn><mfrac><mn>6</mn><mn>7</mn></mfrac></msup><mo>⋅</mo><msup><mn>63437</mn><mfrac><mn>1</mn><mn>7</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62687</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>7</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>62687</mn><mfrac><mn>7</mn><mn>8</mn></mfrac></msup><mo>⋅</mo><msup><mn>61955</mn><mfrac><mn>1</mn><mn>8</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62595</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>8</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>62595</mn><mfrac><mn>8</mn><mn>9</mn></mfrac></msup><mo>⋅</mo><msup><mn>62766</mn><mfrac><mn>1</mn><mn>9</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62613</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>9</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>62613</mn><mfrac><mn>9</mn><mn>10</mn></mfrac></msup><mo>⋅</mo><msup><mn>62580</mn><mfrac><mn>1</mn><mn>10</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62609</mn></mrow></mtd></mtr></mtable></math>
    <p>62609μs is 62.609ms.</p>
</div>

<div>
    <span><em>Note</em>:</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>jbest</mi></math>
    <span>- i.e. a kind of smallest measured jitter - does not exist. As this would often be close to </span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mn>0</mn></math><span>.</span>
</div>

#### worst Jitter

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>jworst</mi><mi>n</mi></msub></math>
    <span>is the worst measured jitter.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>jworst</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mi>k</mi></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><msub><mi>jworst</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>,</mo><msub><mi>jitter</mi><mi>k</mi></msub></mrow><mo>)</mo></mrow></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Example:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>jworst</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>1</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>0</mn><mo>,</mo><mn>3784</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>3784</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>2</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>3784</mn><mo>,</mo><mn>4683</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>4683</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>3</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>6344</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>4</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>4836</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>5</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>934</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>6</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>2590</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>7</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>1482</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>8</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>811</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>9</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>186</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr></mtable></math>
    <p>6344μs is 6.344ms.</p>
</div>

#### average jitter

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>javg</mi><mi>n</mi></msub></math>
    <span>is the average measured jitter.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>javg</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mi>k</mi></msub><mo>=</mo><mo>⌊</mo><mrow><msub><mi>javg</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><mfrac><mrow><msub><mi>jitter</mi><mi>k</mi></msub><mo>−</mo><msub><mi>javg</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub></mrow><mrow><mi>k</mi><mo>+</mo><mn>1</mn></mrow></mfrac></mrow><mo>⌋</mo></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Example:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>javg</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>1</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>0</mn><mo>+</mo><mfrac><mrow><mn>3784</mn><mo>−</mo><mn>0</mn></mrow><mn>2</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>1892</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>2</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>1892</mn><mo>+</mo><mfrac><mrow><mn>4683</mn><mo>−</mo><mn>1892</mn></mrow><mn>3</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>2822</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>3</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>2822</mn><mo>+</mo><mfrac><mrow><mn>6344</mn><mo>−</mo><mn>2822</mn></mrow><mn>4</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>3702</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>4</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>3702</mn><mo>+</mo><mfrac><mrow><mn>4836</mn><mo>−</mo><mn>3702</mn></mrow><mn>5</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>3928</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>5</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>3928</mn><mo>+</mo><mfrac><mrow><mn>934</mn><mo>−</mo><mn>3928</mn></mrow><mn>6</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>3429</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>6</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>3429</mn><mo>+</mo><mfrac><mrow><mn>2590</mn><mo>−</mo><mn>3429</mn></mrow><mn>7</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>3309</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>7</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>3309</mn><mo>+</mo><mfrac><mrow><mn>1482</mn><mo>−</mo><mn>3309</mn></mrow><mn>8</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>3080</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>8</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>3080</mn><mo>+</mo><mfrac><mrow><mn>811</mn><mo>−</mo><mn>3080</mn></mrow><mn>9</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>2827</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>9</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>2827</mn><mo>+</mo><mfrac><mrow><mn>186</mn><mo>−</mo><mn>2827</mn></mrow><mn>10</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>2562</mn></mrow></mtd></mtr></mtable></math>
    <p>2562μs is 2.562ms.</p>
</div>

#### Interarrival jitter

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>jinta</mi><mi>n</mi></msub></math>
    <span> is the measured interarrival jitter. The formula comes from <a href="https://www.rfc-editor.org/rfc/rfc1889.html#appendix-A.8">RFC1889 Appendix A8</a>.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>jinta</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mi>k</mi></msub><mo>=</mo><msub><mi>jinta</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><msub><mi>jitter</mi><mi>k</mi></msub><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><msub><mi>jinta</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Example:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>jinta</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>1</mn></msub><mo>=</mo><mn>0</mn><mo>+</mo><mn>3784</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>0</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>3784</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>2</mn></msub><mo>=</mo><mn>3784</mn><mo>+</mo><mn>4683</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>3784</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>8230</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>3</mn></msub><mo>=</mo><mn>8230</mn><mo>+</mo><mn>6344</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>8230</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>14060</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>4</mn></msub><mo>=</mo><mn>14060</mn><mo>+</mo><mn>4836</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>14060</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>18017</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>5</mn></msub><mo>=</mo><mn>18017</mn><mo>+</mo><mn>934</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>18017</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>17825</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>6</mn></msub><mo>=</mo><mn>17825</mn><mo>+</mo><mn>2590</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>17825</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>19301</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>7</mn></msub><mo>=</mo><mn>19301</mn><mo>+</mo><mn>1482</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>19301</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>19577</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>8</mn></msub><mo>=</mo><mn>19577</mn><mo>+</mo><mn>811</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>19577</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>19164</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>9</mn></msub><mo>=</mo><mn>19164</mn><mo>+</mo><mn>186</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>19164</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>18152</mn></mrow></mtd></mtr></mtable></math>
</div>]]>
        </content>
    </entry>
    
    <entry>
        <title>mtr Metriken</title>
        
        <link rel="alternate" href="https://mk16.de/blog/mtr-metriken/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/mtr-metriken/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/mtr-metriken/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/mtr-metriken/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/mtr-metriken/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/mtr-metriken/" hreflang="de" />

        <id>/blog/mtr-metriken/</id>
        <published>2025-02-17T00:00:00+01:00</published>
        <updated>2025-02-17T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>mtr ist eine Kombination auf Traceroute und Ping, und kann verschiedenste Metriken erzeugen. Aber wie werden diese berechnet und was bedeuten sie?</summary>
        <content type="html">
            <![CDATA[## Was ist mtr?

mtr ist ein Netzwerk-Diagnose-Tool, welches Traceroute und Ping vereint. Es ermittelt den Pfad zu einem Ziel und aktualisiert diesen laufend. Zusätzlich ermittelt mtr die Round-Trip-Time (RTT) zu jedem einzelnen Hop auf dem Pfad. Ergänzend zur RTT werden für jeden Hop und das Ziel weitere Metriken berechnet. Dazu gehören: der Paketverlust, die beste RTT, die durchschnittliche RTT, die schlechteste RTT, die Standardabweichung der RTT, das geometrische Mittel der RTT, den durchschnittlichen Jitter, den schlechtesten Jitter sowie den Interarrival Jitter (nach [RFC1889](https://www.rfc-editor.org/rfc/rfc1889.html#appendix-A.8)).

## Metriken in mtr

Die Metriken können nur berechnet werden, wenn ein Paket hin- und zurückkommt. Daher können verloren gegangene Pakete nicht in die Metriken einfließen. Die verlorenen Pakete findet man lediglich im Paketverlust wieder.

Die RTT ist die Zeit, welche ein Paket zu einem Hop hin **und** zurück braucht.

*Anmerkung*: Die RTT beschreibt die Zeit, welche ein Paket hin und zurück braucht. Die Latenz beschreibt im Allgemeinen die Zeit, welche ein Paket von einem Hop zum anderen braucht - also nur den Hin- oder Rückweg.

### Jitter

Jitter gibt an, wie weit die verschieden gemessenen RTT auseinanderliegen und damit wie sehr dieses schwankt. Das Jitter dient einer ähnlichen Funktion wie die Standardabweichung. Jitter beschreibt, wie sehr die Paket-Laufzeit variiert. Ein niedriger Jitter bedeutet, dass die RTT recht konstant ist. Ein hoher Jitter wiederum bedeutet, dass die RTT sehr schwankt.
Beispiel 1: Es wurden drei RTTs gemessen: 5ms, 6ms, 3ms. Der durchschnittliche Jitter wäre mit 1ms sehr gering. Die RTT ist also während der Messungen recht stabil geblieben.
Beispiel 2: Es wurden drei RTTs gemessen: 10ms, 100ms, 1ms. Der durchschnittliche Jitter wäre mit 63ms sehr hoch. Die RTT ist nicht stabil und verändert sich häufig bzw. weicht oft von den vorherigen Messungen ab.

### Standardabweichung

Die Standardabweichung vom RTT hat eine ähnliche Funktion wie Jitter. Beim ersten Beispiel vom Jitter wäre die Standardabweichung 1ms und beim zweiten Beispiel wäre sie 54ms.

### Metriken im Quellcode

In mtr findet die Berechnung der Werte in [ui/net.c](https://github.com/traviscross/mtr/blob/0f96f02f46d8d72603535c9e1481f245c0ebe2e7/ui/net.c#L269-L316) statt. Dort wird die Standardabweichung als ["sum of squares of differences from the current average"](https://github.com/traviscross/mtr/blob/0f96f02f46d8d72603535c9e1481f245c0ebe2e7/ui/net.c#L57) (zu deutsch: Summe der Quadrate der Abweichungen vom aktuellen Durchschnitt) bezeichnet. Der Interarrival-Jitter wird dort als ["estimated variance?"](https://github.com/traviscross/mtr/blob/0f96f02f46d8d72603535c9e1481f245c0ebe2e7/ui/net.c#L66) (zu deutsch: Geschätzte Varianz?) bezeichnet.

## Berechnung der Metriken

Ich persönlich bin der Meinung, dass man ein besseres Gefühl für die Metriken bekommt, wenn man weiß, wie diese berechnet werden. Ich habe daher einmal C-Code für die Berechnung (angepasst von ui/net.c) sowie eine mathematische Schreibweise für die Berechnung erstellt.

### Beispiel-Werte

Um die Berechnung an konkreten Werten zu illusteren, habe ich eine echte mtr-Messung (10 Runden lang) durchgeführt. Aus Spaß an der Möglichkeit habe ich dies über ein altes Smartphone gemacht, auf welchem ich Kali NetHunter installiert hatte und welches mit einem Freifunk-Router verbunden war. Dieser Freifunk-Router hatte durch das IC-VPN auch eine Interconnection zum dn42, sodass ich Burble’s Pingable-Adresse anpingen konnte. Ich habe die Messung mit `tcpdump` aufgenommen und die 20 relevanten Pakete ausgeschnitten (siehe unten). Die entsprechende pcapng-Datei gibt es [hier](/assets/pcap/mtr-metrics-example.pcapng). mtr habe ich mit folgendem Befehl ausgeführt:

```
$ mtr -4 --report --json --show-ips --order "LDRSBAWVGMXI" --report-cycles 10 --psize 400 --bitpattern 256 pingable.burble.dn42
```

mtr hat seine Messung als JSON ausgegeben (formatiert mit `jq`):
```json
{
  "report": {
    "mtr": {
      "src": "kali",
      "dst": "pingable.burble.dn42",
      "tos": 0,
      "tests": 10,
      "psize": "400",
      "bitpattern": "rand(0x00-FF)"
    },
    "hubs": [
      {
        "count": 1,
        "host": "gw4.ff3l (10.119.0.5)",
        "Loss%": 10.0,
        "Drop": 1,
        "Rcv": 9,
        "Snt": 10,
        "Best": 34.079,
        "Avg": 36.272,
        "Wrst": 38.427,
        "StDev": 1.473,
        "Gmean": 36.245,
        "Javg": 1.536,
        "Jmax": 3.513,
        "Jint": 10.83
      },
      {
        "count": 2,
        "host": "gw3.ff3l (10.119.0.4)",
        "Loss%": 0.0,
        "Drop": 0,
        "Rcv": 10,
        "Snt": 10,
        "Best": 41.578,
        "Avg": 45.264,
        "Wrst": 50.228,
        "StDev": 2.671,
        "Gmean": 45.194,
        "Javg": 3.176,
        "Jmax": 8.65,
        "Jint": 24.754
      },
      {
        "count": 3,
        "host": "p2pnode.bandura.dn42 (172.22.149.225)",
        "Loss%": 0.0,
        "Drop": 0,
        "Rcv": 10,
        "Snt": 10,
        "Best": 55.045,
        "Avg": 57.674,
        "Wrst": 60.757,
        "StDev": 1.985,
        "Gmean": 57.643,
        "Javg": 1.668,
        "Jmax": 4.043,
        "Jint": 13.546
      },
      {
        "count": 4,
        "host": "pingable.burble.dn42 (172.20.129.5)",
        "Loss%": 0.0,
        "Drop": 0,
        "Rcv": 10,
        "Snt": 10,
        "Best": 60.776,
        "Avg": 63.143,
        "Wrst": 67.09,
        "StDev": 1.927,
        "Gmean": 63.115,
        "Javg": 2.568,
        "Jmax": 6.314,
        "Jint": 18.246
      }
    ]
  }
}
```

In der Messung stehen die endgültigen Werte, jedoch nicht die einzelnen RTT-Werte. Diese habe ich mit WireShark aus der pcapng-Datei extrahiert. Da die am Interface ankommenden (durch tcpdump mitgeschnittenen) Pakete eine Verzögerung haben können bis diese bei mtr ankommen und mtr diese verarbeitet unterscheiden sich meine endgültigen Werte etwas von denen von mtr.

![Man sieht eine holzartigen Tisch, auf welchem ein Smartphone liegt. Man kann das Smartphone zwar vollständig sehen, es ist aber sehr nah am Bild. Im Hintergrund sieht man den Anfang eines Laptops. Das Smartphone ist hell pink, mit einer schwarzen Hülle. Es ist von Samsung und hat unten noch drei physische Tasten. Auf dem Bildschirm des Smartphones sieht man ein Terminal, in welchem mtr ausgeführt wird. In der Benachrichtigungsleiste sieht man, dass aktuell eine Bildschirmaufnahme läuft, ein Terminal im Hintergrund läuft, das Smartphone mit einem VPN und WLAN verbunden ist, dass es über 50% geladen ist sowie dass die aktuelle Uhrzeit 02:49 Uhr beträgt. Das Terminal ist als "root@kali: /" eingeloggt. Unten sieht man extra Tasten zur bedinung (Esc, Alt, Ctrl, Tab, PgUp, PgDown, Home, End und die Pfeil-Tasten). Im Terminal selber läuft mtr version 0.95. Die Quell-Adresse ist fdc7:3c9d:b889:a272:2974:b580:9586:4bb3 und die Ziel-Adresse ist fd42:4242:2601:ac05::1. Es gibt vier Hops (alle mit einem Paketverlust von 0%, an alle wurde etwas über 65 Pakete gesendet): 1. fdc7:3c9d:b889:a272::5, 2. fdc7:3c9d:b889:a272::4, 3. fd04:234e:fc31::1, 4. das Ziel fd42:4242:2601:ac05::1.](/assets/img/mtr-metrics-kali-nethunter.jpg) 

*Anmerkung*: In der pcapng-Datei waren neben der mtr-Messung auch andere Daten enthalten. Darüber hinaus bestand die mtr-Messung aus mehreren Messungen zu den verschiedenen Hops hin. Aus Einfachheitsgründen habe ich nur einen Hop (das Ziel) für die konkreten Werte ausgewählt.

*Anmerkung*: Ich habe die Messung einmal erfolgreich mit IPv4 und IPv6 gemacht. IPv6 hat bis auf das reverse DNS auch funktioniert.

### C-Code

```c
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

struct measurement_s
{
    int last; // last rtt
    int jitter; // jitter
    int best; // best rtt
    int worst; // worst rtt
    int jworst; // worst jitter
    int returned; // number of pings
    int avg; // average rtt
    long long ssd; // standard deviation
    int javg; // average jitter
    int jinta; // interarrival jitter
    int gmean; // geometric mean
};

typedef struct measurement_s measurement_t;

static inline int max(int a, int b)
{
    return (a > b ? a : b);
}

static inline int min(int a, int b)
{
    return (a < b ? a : b);
}

void process_ping(measurement_t * const m, const int rtt);
void post_process_measurement(measurement_t * const m);

int main(void)
{
    int rtt[] = {
        61172, 64956, 60273, 66617, 61781, 60847, 63437, 61955, 62766, 62580};
    size_t rtt_n = sizeof(rtt) / sizeof(*rtt); // Number of measurements

    measurement_t m = {.last = 0,
                       .jitter = 0,
                       .best = 0,
                       .worst = 0,
                       .jworst = 0,
                       .returned = 0,
                       .avg = 0,
                       .ssd = 0,
                       .javg = 0,
                       .jinta = 0,
                       .gmean = 0};

    for (size_t i = 0; i < rtt_n; i++)
    {
        process_ping(&m, rtt[i]);
    }

    post_process_measurement(&m);

    printf("last = %d\n"
           "jitter = %d\n"
           "best = %d\n"
           "worst = %d\n"
           "jworst = %d\n"
           "returned = %d\n"
           "avg = %d\n"
           "ssd = %lld\n"
           "javg = %d\n"
           "jinta = %d\n"
           "gmean = %d\n",
           m.last,
           m.jitter,
           m.best,
           m.worst,
           m.jworst,
           m.returned,
           m.avg,
           m.ssd,
           m.javg,
           m.jinta,
           m.gmean);

    return EXIT_SUCCESS;
}

void process_ping(measurement_t * const m, const int rtt)
{
    m->returned++;

    if (m->returned <= 1) // first ping
    {
        m->jitter = m->jworst = m->ssd = m->javg = m->jinta = 0;
        m->best = m->worst = m->avg = m->gmean = rtt;
    }
    else
    {
        // With `.0` a floating point calculation is forced.

        m->jitter = abs(rtt - m->last);
        m->best = min(m->best, rtt);
        m->worst = max(m->worst, rtt);
        m->jworst = max(m->jworst, m->jitter);

        int oldavg = m->avg;
        m->avg += (rtt - m->avg + .0) / m->returned;
        m->ssd += (rtt - oldavg + .0) * (rtt - m->avg + .0);
        m->javg += (m->jitter - m->javg + .0) / m->returned;
        m->jinta += m->jitter - ((m->jinta + 8) >> 4);
        m->gmean =
            (int) (pow(m->gmean + .0, (m->returned - 1.0) / m->returned) *
                   pow(rtt + .0, 1.0 / m->returned));
    }

    m->last = rtt;
}

void post_process_measurement(measurement_t * const m)
{
    m->ssd = sqrt(m->ssd / (m->returned - 1.0));
}
```

Die Daten der aktuellen Runde werden im `struct` `measurement_s` gespeichert. Jede Runde - also immer, wenn ein Ping empfangen würde - wird die Funktion `process_ping` aufgerufen. Diese aktualisiert die Daten im `struct` basiert auf dem RTT-Wert, welchen sie bekommen hat. Wenn der erste Ping eingetroffen ist, werden die Metriken mit bestimmten Start-Werten befüllt. Dies ist - je nach Metrik - entweder 0 und die aktuelle (und zu dem Zeitpunkt einzige) RTT. Ansonsten werden nach speziellen Formeln die neuen Werte berechnet.
Aufgrund der Eigenschaften der Standardabweichung wird diese nur indirekt berechnet. Um sie tatsächlich zu erhalten wird zum Schluss in der Funktion `post_process_measurement` die eigentliche Standardabweichung (einmalig) berechnet. Diese Berechnung wird auch jedes Mal in mtr durchgeführt, wenn die Standardabweichung angezeigt werden soll.
Zum Schluss wird einmal alles ausgegeben. Die Attribute `last` und `returned` sind zwar für die Berechnung notwendig, jedoch als Metrik nur bedingt interessant. `last` speichert die letzte RTT und `returned` die Anzahl der erfolgreich empfangenen Ping Replys.

### mathematische Formulierung

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>n</mi><mo>,</mo><mi>n</mi><mo>∈</mo><msub><mi>ℕ</mi><mn>0</mn></msub></math>
    <span>gibt die Anzahl der RTT-Messungen an.</span>
</div>

Die Einheit der Zeitmessungen ist Mikrosekunden.
Umrechnung:
```
Mikrosekunden (μs) = Milisekunden (ms) * 1000
Milisekunden (ms) = Mikrosekunden (μs) / 1000
```
Beispiel:
61172μs sind 61,172ms; 64,956ms sind 64956μs.

<div>
    <span>Die Funktion</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>min</mi><mo>⁡</mo><mo>(</mo><mi>a</mi><mo>,</mo><mi>b</mi><mo>)</mo></math>
    <span>gibt das Minimum von</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>a</mi></math>
    <span>und</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>b</mi></math>
    <span>zurück. Die Funktion</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>max</mi><mo>⁡</mo><mo>(</mo><mi>a</mi><mo>,</mo><mi>b</mi><mo>)</mo></math>
    <span>gibt das Maximum von</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>a</mi></math>
    <span>und</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>b</mi></math>
    <span>zurück.</span>
</div>

#### RTT

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>rtt</mi><mi>k</mi></msub><mo>,</mo><mi>k</mi><mo>∈</mo><msub><mi>ℕ</mi><mn>0</mn></msub></math>
    <span>gibt die RTT für die</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>k</mi></math>
    <span>-te Messung an. Die RTT wird in Mikrosekunden angegeben. Dies ermöglicht das problemlose Rechnen in ganzen Zahlen und erleichtert somit die maschinelle Berechnung.</span>
</div>

<div>
    <p>Beispiel:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable><mtr><mtd><mrow><mi>rtt</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>0</mn></msub><mo>=</mo><mn>61172</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>1</mn></msub><mo>=</mo><mn>64956</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>2</mn></msub><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>3</mn></msub><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>4</mn></msub><mo>=</mo><mn>61781</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>5</mn></msub><mo>=</mo><mn>60847</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>6</mn></msub><mo>=</mo><mn>63437</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>7</mn></msub><mo>=</mo><mn>61955</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>8</mn></msub><mo>=</mo><mn>62766</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>rtt</mi><mn>9</mn></msub><mo>=</mo><mn>62580</mn></mrow></mtd></mtr></mtable></math>
</div>

#### Jitter

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>jitter</mi><mi>n</mi></msub></math>
    <span>ist das Jitter der</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>n</mi></math><span>-ten Messung.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>jitter</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mi>k</mi></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><msub><mi>rtt</mi><mi>k</mi></msub><mo>−</mo><msub><mi>rtt</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub></mrow><mo>)</mo></mrow></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Beispiel:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>jitter</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>1</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>64956</mn><mo>−</mo><mn>61172</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>3784</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>2</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>−</mo><mn>64956</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>4683</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>3</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>−</mo><mn>60273</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>4</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>61781</mn><mo>−</mo><mn>66617</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>4836</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>5</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60847</mn><mo>−</mo><mn>61781</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>934</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>6</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>63437</mn><mo>−</mo><mn>60847</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>2590</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>7</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>61955</mn><mo>−</mo><mn>63437</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>1482</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>8</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>62766</mn><mo>−</mo><mn>61955</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>811</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jitter</mi><mn>9</mn></msub><mo>=</mo><mi>abs</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>62580</mn><mo>−</mo><mn>62766</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>186</mn></mrow></mtd></mtr></mtable></math>
</div>

#### Beste RTT

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>best</mi><mi>n</mi></msub></math>
    <span>gibt die beste RTT an.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>best</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>0</mn></msub><mo>=</mo><msub><mi>rtt</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mi>k</mi></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><msub><mi>best</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>,</mo><msub><mi>rtt</mi><mi>k</mi></msub></mrow><mo>)</mo></mrow></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Beispiel:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>best</mi><mn>0</mn></msub><mo>=</mo><mn>61172</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>1</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>61172</mn><mo>,</mo><mn>64956</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>61172</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>2</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>61172</mn><mo>,</mo><mn>60273</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>3</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>66617</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>4</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>61781</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>5</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>60847</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>6</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>63437</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>7</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>61955</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>8</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>62766</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>best</mi><mn>9</mn></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>,</mo><mn>62580</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>60273</mn></mrow></mtd></mtr></mtable></math>
    <p>60273μs sind 60,273ms.</p>
</div>

#### Schlechteste RTT

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>worst</mi><mi>n</mi></msub></math>
    <span>gibt die schlechteste RTT an.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>worst</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>0</mn></msub><mo>=</mo><msub><mi>rtt</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mi>k</mi></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><msub><mi>worst</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>,</mo><msub><mi>rtt</mi><mi>k</mi></msub></mrow><mo>)</mo></mrow></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Beispiel:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>worst</mi><mn>0</mn></msub><mo>=</mo><mn>61172</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>1</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>61172</mn><mo>,</mo><mn>64956</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>64956</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>2</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>64956</mn><mo>,</mo><mn>60273</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>64956</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>3</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>64956</mn><mo>,</mo><mn>66617</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>4</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>,</mo><mn>61781</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>5</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>,</mo><mn>60847</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>6</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>,</mo><mn>63437</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>7</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>,</mo><mn>61955</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>8</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>,</mo><mn>62766</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>worst</mi><mn>9</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>,</mo><mn>62580</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>66617</mn></mrow></mtd></mtr></mtable></math>
    <p>66617μs sind 66,617ms.</p>
</div>

#### Durchschnittliche RTT

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>avg</mi><mi>n</mi></msub></math>
    <span>gibt die durchschnittliche RTT an.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>avg</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>0</mn></msub><mo>=</mo><msub><mi>rtt</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mi>k</mi></msub><mo>=</mo><mo>⌊</mo><mrow><msub><mi>avg</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><mfrac><mrow><msub><mi>rtt</mi><mi>k</mi></msub><mo>−</mo><msub><mi>avg</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub></mrow><mrow><mi>k</mi><mo>+</mo><mn>1</mn></mrow></mfrac></mrow><mo>⌋</mo></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Beispiel:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>avg</mi><mn>0</mn></msub><mo>=</mo><mn>61172</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>1</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>61172</mn><mo>+</mo><mfrac><mrow><mn>64956</mn><mo>−</mo><mn>61172</mn></mrow><mn>2</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>63064</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>2</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>63064</mn><mo>+</mo><mfrac><mrow><mn>60273</mn><mo>−</mo><mn>63064</mn></mrow><mn>3</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62133</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>3</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>62133</mn><mo>+</mo><mfrac><mrow><mn>66617</mn><mo>−</mo><mn>62133</mn></mrow><mn>4</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>63254</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>4</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>63254</mn><mo>+</mo><mfrac><mrow><mn>61781</mn><mo>−</mo><mn>63254</mn></mrow><mn>5</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62959</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>5</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>62959</mn><mo>+</mo><mfrac><mrow><mn>60847</mn><mo>−</mo><mn>62959</mn></mrow><mn>6</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62607</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>6</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>62607</mn><mo>+</mo><mfrac><mrow><mn>63437</mn><mo>−</mo><mn>62607</mn></mrow><mn>7</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62725</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>7</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>62725</mn><mo>+</mo><mfrac><mrow><mn>61955</mn><mo>−</mo><mn>62725</mn></mrow><mn>8</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62628</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>8</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>62628</mn><mo>+</mo><mfrac><mrow><mn>62766</mn><mo>−</mo><mn>62628</mn></mrow><mn>9</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62643</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>avg</mi><mn>9</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>62643</mn><mo>+</mo><mfrac><mrow><mn>62580</mn><mo>−</mo><mn>62643</mn></mrow><mn>10</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>62636</mn></mrow></mtd></mtr></mtable></math>
    <p>62636μs sind 62,636ms.</p>
</div>

#### Standardabweichung

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>σ</mi></math>
    <span>gibt die Standardabweichung der RTT an.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>ssd</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mi>k</mi></msub><mo>=</mo><mo>⌊</mo><mrow><msub><mi>ssd</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><mrow><mo>(</mo><mrow><msub><mi>rtt</mi><mi>k</mi></msub><mo>−</mo><msub><mi>avg</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><msub><mi>rtt</mi><mi>k</mi></msub><mo>−</mo><msub><mi>avg</mi><mi>k</mi></msub></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo></mrow></mtd></mtr><mtr><mtd><mrow><mi>σ</mi><mo>=</mo><mo>⌊</mo><msqrt><mfrac><msub><mi>ssd</mi><mi>n</mi></msub><mi>n</mi></mfrac></msqrt><mo>⌋</mo></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <span>Beispiel:</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>ssd</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>1</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>0</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>64956</mn><mo>−</mo><mn>61172</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>64956</mn><mo>−</mo><mn>63064</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>7159328</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>2</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>7159328</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>−</mo><mn>63064</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>60273</mn><mo>−</mo><mn>62133</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>12350588</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>3</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>12350588</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>−</mo><mn>62133</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>66617</mn><mo>−</mo><mn>63254</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>27430280</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>4</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>27430280</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>61781</mn><mo>−</mo><mn>63254</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>61781</mn><mo>−</mo><mn>62959</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>29165474</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>5</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>29165474</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>60847</mn><mo>−</mo><mn>62959</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>60847</mn><mo>−</mo><mn>62607</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>32882594</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>6</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>32882594</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>63437</mn><mo>−</mo><mn>62607</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>63437</mn><mo>−</mo><mn>62725</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>33473554</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>7</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>33473554</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>61955</mn><mo>−</mo><mn>62725</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>61955</mn><mo>−</mo><mn>62628</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>33991764</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>8</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>33991764</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>62766</mn><mo>−</mo><mn>62628</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>62766</mn><mo>−</mo><mn>62643</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>34008738</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>ssd</mi><mn>9</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>34008738</mn><mo>+</mo><mrow><mo>(</mo><mrow><mn>62580</mn><mo>−</mo><mn>62643</mn></mrow><mo>)</mo></mrow><mo>⋅</mo><mrow><mo>(</mo><mrow><mn>62580</mn><mo>−</mo><mn>62636</mn></mrow><mo>)</mo></mrow></mrow><mo>⌋</mo><mo>=</mo><mn>34012266</mn></mrow></mtd></mtr><mtr><mtd><mrow><mi>σ</mi><mo>=</mo><mo>⌊</mo><msqrt><mfrac><mn>34012266</mn><mn>9</mn></mfrac></msqrt><mo>⌋</mo><mo>=</mo><mn>1944</mn></mrow></mtd></mtr></mtable></math>
    <p>1944μs sind 1,944ms.</p>
</div>

#### geometrisches Mittel

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>gmean</mi><mi>n</mi></msub></math>
    <span>gibt das geometrische Mittel der RTT an.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>gmean</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>0</mn></msub><mo>=</mo><msub><mi>rtt</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mi>k</mi></msub><mo>=</mo><mo>⌊</mo><mrow><msup><msub><mi>gmean</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mfrac><mi>k</mi><mrow><mi>k</mi><mo>+</mo><mn>1</mn></mrow></mfrac></msup><mo>⋅</mo><msup><msub><mi>rtt</mi><mi>k</mi></msub><mfrac><mn>1</mn><mrow><mi>k</mi><mo>+</mo><mn>1</mn></mrow></mfrac></msup></mrow><mo>⌋</mo></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Beispiel:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>gmean</mi><mn>0</mn></msub><mo>=</mo><mn>61172</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>1</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>61172</mn><mfrac><mn>1</mn><mn>2</mn></mfrac></msup><mo>⋅</mo><msup><mn>64956</mn><mfrac><mn>1</mn><mn>2</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>63035</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>2</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>63035</mn><mfrac><mn>2</mn><mn>3</mn></mfrac></msup><mo>⋅</mo><msup><mn>60273</mn><mfrac><mn>1</mn><mn>3</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62100</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>3</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>62100</mn><mfrac><mn>3</mn><mn>4</mn></mfrac></msup><mo>⋅</mo><msup><mn>66617</mn><mfrac><mn>1</mn><mn>4</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>63199</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>4</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>63199</mn><mfrac><mn>4</mn><mn>5</mn></mfrac></msup><mo>⋅</mo><msup><mn>61781</mn><mfrac><mn>1</mn><mn>5</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62912</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>5</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>62912</mn><mfrac><mn>5</mn><mn>6</mn></mfrac></msup><mo>⋅</mo><msup><mn>60847</mn><mfrac><mn>1</mn><mn>6</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62563</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>6</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>62563</mn><mfrac><mn>6</mn><mn>7</mn></mfrac></msup><mo>⋅</mo><msup><mn>63437</mn><mfrac><mn>1</mn><mn>7</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62687</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>7</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>62687</mn><mfrac><mn>7</mn><mn>8</mn></mfrac></msup><mo>⋅</mo><msup><mn>61955</mn><mfrac><mn>1</mn><mn>8</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62595</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>8</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>62595</mn><mfrac><mn>8</mn><mn>9</mn></mfrac></msup><mo>⋅</mo><msup><mn>62766</mn><mfrac><mn>1</mn><mn>9</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62613</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>gmean</mi><mn>9</mn></msub><mo>=</mo><mo>⌊</mo><mrow><msup><mn>62613</mn><mfrac><mn>9</mn><mn>10</mn></mfrac></msup><mo>⋅</mo><msup><mn>62580</mn><mfrac><mn>1</mn><mn>10</mn></mfrac></msup></mrow><mo>⌋</mo><mo>=</mo><mn>62609</mn></mrow></mtd></mtr></mtable></math>
    <p>62609μs sind 62,609ms.</p>
</div>

<div>
    <span><em>Anmerkung</em>:</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mi>jbest</mi></math>
    <span>- also eine Art kleinstes gemessenes Jitter - gibt es nicht. Da dies häufig Nahe</span>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mn>0</mn></math>
    <span>ausfallen würde.</span>
</div>

#### Schlechtestes Jitter

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>jworst</mi><mi>n</mi></msub></math>
    <span>ist das schlechteste gemessene Jitter.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>jworst</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mi>k</mi></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><msub><mi>jworst</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>,</mo><msub><mi>jitter</mi><mi>k</mi></msub></mrow><mo>)</mo></mrow></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Beispiel:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>jworst</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>1</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>0</mn><mo>,</mo><mn>3784</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>3784</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>2</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>3784</mn><mo>,</mo><mn>4683</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>4683</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>3</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>6344</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>4</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>4836</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>5</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>934</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>6</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>2590</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>7</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>1482</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>8</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>811</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jworst</mi><mn>9</mn></msub><mo>=</mo><mi>max</mi><mo>⁡</mo><mrow><mo>(</mo><mrow><mn>4683</mn><mo>,</mo><mn>186</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>6344</mn></mrow></mtd></mtr></mtable></math>
    <p>6344μs sind 6,344ms.</p>
</div>

#### Durchschnittlicher Jitter

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>javg</mi><mi>n</mi></msub></math>
    <span>ist das durchschnittliche gemessene Jitter.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>javg</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mi>k</mi></msub><mo>=</mo><mo>⌊</mo><mrow><msub><mi>javg</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><mfrac><mrow><msub><mi>jitter</mi><mi>k</mi></msub><mo>−</mo><msub><mi>javg</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub></mrow><mrow><mi>k</mi><mo>+</mo><mn>1</mn></mrow></mfrac></mrow><mo>⌋</mo></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Beispiel:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>javg</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>1</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>0</mn><mo>+</mo><mfrac><mrow><mn>3784</mn><mo>−</mo><mn>0</mn></mrow><mn>2</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>1892</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>2</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>1892</mn><mo>+</mo><mfrac><mrow><mn>4683</mn><mo>−</mo><mn>1892</mn></mrow><mn>3</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>2822</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>3</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>2822</mn><mo>+</mo><mfrac><mrow><mn>6344</mn><mo>−</mo><mn>2822</mn></mrow><mn>4</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>3702</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>4</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>3702</mn><mo>+</mo><mfrac><mrow><mn>4836</mn><mo>−</mo><mn>3702</mn></mrow><mn>5</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>3928</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>5</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>3928</mn><mo>+</mo><mfrac><mrow><mn>934</mn><mo>−</mo><mn>3928</mn></mrow><mn>6</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>3429</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>6</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>3429</mn><mo>+</mo><mfrac><mrow><mn>2590</mn><mo>−</mo><mn>3429</mn></mrow><mn>7</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>3309</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>7</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>3309</mn><mo>+</mo><mfrac><mrow><mn>1482</mn><mo>−</mo><mn>3309</mn></mrow><mn>8</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>3080</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>8</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>3080</mn><mo>+</mo><mfrac><mrow><mn>811</mn><mo>−</mo><mn>3080</mn></mrow><mn>9</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>2827</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>javg</mi><mn>9</mn></msub><mo>=</mo><mo>⌊</mo><mrow><mn>2827</mn><mo>+</mo><mfrac><mrow><mn>186</mn><mo>−</mo><mn>2827</mn></mrow><mn>10</mn></mfrac></mrow><mo>⌋</mo><mo>=</mo><mn>2562</mn></mrow></mtd></mtr></mtable></math>
    <p>2562μs sind 2,562ms.</p>
</div>

#### Interarrival Jitter

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><msub><mi>jinta</mi><mi>n</mi></msub></math>
    <span>ist der gemessene Interarrival Jitter. Die Formel stammt aus <a href="https://www.rfc-editor.org/rfc/rfc1889.html#appendix-A.8">RFC1889 Anhang A8</a>.</span>
</div>

<div>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><mi>jinta</mi><mo>:</mo><msub><mi>ℕ</mi><mn>0</mn></msub><mo>→</mo><msub><mi>ℕ</mi><mn>0</mn></msub></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mi>k</mi></msub><mo>=</mo><msub><mi>jinta</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><msub><mi>jitter</mi><mi>k</mi></msub><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><msub><mi>jinta</mi><mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow></mrow></mtd></mtr></mtable></math>
</div>

<div>
    <p>Beispiel:</p>
    <math xmlns="http://www.w3.org/1998/Math/MathML"><mtable columnalign="left"><mtr><mtd><mrow><msub><mi>jinta</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>1</mn></msub><mo>=</mo><mn>0</mn><mo>+</mo><mn>3784</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>0</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>3784</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>2</mn></msub><mo>=</mo><mn>3784</mn><mo>+</mo><mn>4683</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>3784</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>8230</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>3</mn></msub><mo>=</mo><mn>8230</mn><mo>+</mo><mn>6344</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>8230</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>14060</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>4</mn></msub><mo>=</mo><mn>14060</mn><mo>+</mo><mn>4836</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>14060</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>18017</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>5</mn></msub><mo>=</mo><mn>18017</mn><mo>+</mo><mn>934</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>18017</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>17825</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>6</mn></msub><mo>=</mo><mn>17825</mn><mo>+</mo><mn>2590</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>17825</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>19301</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>7</mn></msub><mo>=</mo><mn>19301</mn><mo>+</mo><mn>1482</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>19301</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>19577</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>8</mn></msub><mo>=</mo><mn>19577</mn><mo>+</mo><mn>811</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>19577</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>19164</mn></mrow></mtd></mtr><mtr><mtd><mrow><msub><mi>jinta</mi><mn>9</mn></msub><mo>=</mo><mn>19164</mn><mo>+</mo><mn>186</mn><mo>−</mo><mrow><mo>(</mo><mrow><mo>(</mo><mrow><mn>19164</mn><mo>+</mo><mn>8</mn></mrow><mo>)</mo><mo>>></mo><mn>4</mn></mrow><mo>)</mo></mrow><mo>=</mo><mn>18152</mn></mrow></mtd></mtr></mtable></math>
</div>]]>
        </content>
    </entry>
    
    <entry>
        <title>Wie groß ist das MTU in einem OpenVPN-Tunnel?</title>
        
        <link rel="alternate" href="https://mk16.de/blog/openvpn-mtu-de/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/openvpn-mtu-de/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/openvpn-mtu-de/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/openvpn-mtu-de/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/openvpn-mtu-de/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/openvpn-mtu-de/" hreflang="de" />

        <id>/blog/openvpn-mtu-de/</id>
        <published>2024-10-04T01:00:00+02:00</published>
        <updated>2024-10-04T01:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Welches MTU hat OpenVPN? Welches Overhead hat OpenVPN? Wie hängt dies mit den verwendeten Chiffren zusammen?</summary>
        <content type="html">
            <![CDATA[OpenVPN verwendet standardmäßig 1500 als MTU. Dies kann jedoch zu Fragmentierung der äußeren VPN-Pakete führen, was man im Allgemeinen vermeiden möchte. Daher ist es notwendig, das MTU im Tunnel so zu senken, dass keine Fragmentierung auftritt. Daher stellt sich die Frage, wie groß das optimale MTU sein darf. OpenVPN bietet dafür leider keine einfache Dokumentation an.

Die eigentlichen Daten, welche im Tunnel transportiert werden, werden auch als Nutzdaten oder Payload bezeichnet. Vor diesen Daten werden jedoch noch verschiedene Header vorgestellt. Diese dienen unter anderem dazu, dass die Pakete richtig zugestellt werden (IP-Routing) als auch dafür, verschiedene kryptografische Parameter zu übertragen.

Der Prozess, dass mehrere Header den eigentlichen Nutzdaten vorangestellt werden, bezeichnet man auch als Kapselung.

Folgende Kapseln existieren:

- IP Header
- UDP/TCP Header
- OpenVPN Header
- Nutzdaten

Um also das optimale MTU - also die maximale Größe der Nutzdaten, ohne dass es zu Fragmentierung kommt - zu kennen, muss die Größe des IP-Headers, des UDP/TCP-Header sowie des OpenVPN-Headers bekannt sein.

## IP-Header

Im Normalfall hat ein IP-Header eine Größe von 20-40 Bytes. Wenn IPv4 verwendet wird, sind es 20 Bytes, bei IPv6 sind es 40 Bytes. Wenn man als Endpunkt nicht eine feste IP-Adresse, sondern einen DNS-Namen angibt, entscheidet im Normalfall die Software oder das Betriebssystem, ob IPv4 oder IPv6 verwendet werden soll. Ich empfehle daher, immer mit 40 Bytes als IP-Header zurechnen.

TL;DR: 40 Bytes

## UDP/TCP-Header

In OpenVPN kann man zwischen UDP und TCP als Transportprotokoll wählen. Im Allgemeinen wird für VPN-Anwendungen UDP empfohlen. Dort beträgt die Headergröße 8 Byte. Bei TCP beträgt die Headergröße zwischen 20 und 60 Bytes. OpenVPN scheint die Verwendung von 20 Bytes nicht zu erzwingen. Ich empfehle daher, bei TCP mit 60 Bytes, der maximalen Größe eines TCP-Headers, zu rechnen.

TL;DR: 8 Bytes bei UDP, 60 Bytes bei TCP

## OpenVPN-Header

Der OpenVPN-Header unterscheidet sich je nach verwendeter Chiffre und verwendetem Betriebsmodus. Zusätzlich gibt es zwei unterschiedliche Headerformate `P_DATA_V1` und `P_DATA_V2`. Moderne OpenVPN-Clients sollten eigentlich ausschließlich `P_DATA_V2` verwenden. Um die kryptografischen Parameter auszuhandeln, unterstützt OpenVPN zwei Methoden: TLS und Preshared Key (PSK). Dabei ist PSK veraltet und sollte nicht mehr verwendet werden. Darüber hinaus unterstützt die PSK-Methode kein Perfect Forward Secrecy (PFS), was eine wünschenswerte kryptografische Eigenschaft ist. Ich gehe für folgende Angaben von einem "modernen" OpenVPN-Client aus, welcher `P_DATA_V2` verwendet.

### AEAD-Chiffren / GCM-Betriebsmodus (auch CHACHA20-POLY1305)

Der OpenVPN-Header besteht aus folgenden Komponenten:

- 1 Byte Opcode und Key-ID (5-bit Opcode und 3-bit Key-ID)
- 3 Bytes Peer-ID
- 4 Bytes Packet-ID
- 16 Bytes tag

TL;DR: Overhead = 24 Bytes

### CBC-Betriebsmodus

Der OpenVPN-Header besteht aus folgenden Komponenten:

- 1 Bytes Opcode und Key-ID (5-bit Opcode und 3-bit Key-ID)
- 3 Bytes Peer-ID
- HMAC
- IV
- 4 Bytes Packet-ID
- Nur bei PSK: 4 Bytes Zeitstempel
- Padding

Wenn das optimale MTU nicht genau berechnet werden soll, kann man für das Padding 16 Bytes verwenden.

Im CBC-Betriebsmodus wird die Packet-ID und der Zeitstempel (bei PSK) mit verschlüsselt. Wenn die Länge dieses verschlüsselten Text ein Vielfaches von 16 ist, wird ein Padding von 16 Bytes angehängt. Ansonsten wird ein Padding angehängt, sodass die Länge ein Vielfaches von 16 ist.
Um das optimale MTU zu berechnen, kann folgendes Algorithmus verwendet werden:
1. Berechne das temporäre MTU ohne Packet-ID, Zeitstempel oder Padding.
2. Ist das temporäre MTU ein Vielfaches von 16?
    - Wenn nein: Bestimme die größte Zahl, die kleiner als temporäres MTU + 4 (Packet-ID) + 4 (Zeitstempel bei PSK) ist und ein Vielfaches von 16 ist.
3. Subtrahiere 4 (Packet-ID), 4 (Zeitstempel bei PSK) und 1.

TL;DR: Overhead = 26 Bytes + IV-Größe + HMAC-Größe + 4 Byte Zeitstempel (bei PSK)

### CFB/OFB-Betriebsmodus

Der OpenVPN-Header besteht aus folgenden Komponenten:

- 1 Byte Opcode und Key-ID (5-bit Opcode und 3-bit Key-ID)
- 3 Bytes Peer-ID
- HMAC
- IV

TL;DR: Overhead = 4 Bytes + HMAC-Größe + IV-Größe

### Keine Verschlüsselung

OpenVPN bietet mit der `none` Chiffre einen Modus an, welcher die Daten nicht verschlüsselt, sondern nur authentifiziert.

Der OpenVPN-Header besteht dann aus folgenden Komponenten:

- 1 Byte Opcode und Key-ID (5-bit Opcode und 3-bit Key-ID)
- 3 Bytes Peer-ID
- HMAC
- 4 Bytes Packet-ID
- Nur bei PSK: 4 Byte Zeitstempel

TL;DR: Overhead = 8 Bytes + HMAC-Größe + 4 Byte Zeitstempel (bei PSK)

## IV-Größe

Das IV der meisten Chiffren ist 16 Byte groß. Die genaue Größe des IV lässt sich über die Blockgröße herausfinden. Diese kann man mit `openvpn --show-ciphers` anzeigen.

## HMAC-Größe

Manchmal wird die Größe des HMACs benötigt, um das MTU zu berechnen. Im Folgenden ist eine Tabelle mit der HMAC auf der rechten Seite und ihrer Größe auf der linken Seite.

| HMAC | Größe |
| --- | --- |
| SHA1 | 20 Bytes |
| SHA256 / SHA2-256 / SHA3-256 | 32 Bytes |
| SHA384 / SHA2-384 / SHA3-384 | 48 Bytes |
| SHA512 / SHA2-512 / SHA3-512 | 64 Bytes |

Alternativ kann mit `openvpn --show-digests` die Größen ermitteln.

## Empfohlene MTUs

In der folgenden Tabelle wird angenommen, dass das äußere MTU 1500 Bytes beträgt, das UDP als Transportprotokoll verwendet wird, dass der IV 16 Byte groß ist und dass IPv6 verwendet werden könnte.

| Chiffre | MTU |
| --- | --- |
| TLS + AEAD/GCM | `1428` |
| TLS + CBC + SHA1 | `1403` |
| TLS + CBC + SHA256 | `1387` |
| TLS + CBC + SHA384 | `1371` |
| TLS + CBC + SHA512 | `1355` |
| PSK + CBC + SHA1 | `1399` |
| PSK + CBC + SHA256 | `1383` |
| PSK + CBC + SHA384 | `1367` |
| PSK + CBC + SHA512 | `1351` |
| TLS + CFB/OFB + SHA1 | `1412` |
| TLS + CFB/OFB + SHA256 | `1400` |
| TLS + CFB/OFB + SHA384 | `1384` |
| TLS + CFB/OFB + SHA512 | `1368` |
| TLS + None + SHA1 | `1424` |
| TLS + None + SHA256 | `1412` |
| TLS + None + SHA384 | `1396` |
| TLS + None + SHA512 | `1380` |
| PSK + None + SHA1 | `1420` |
| PSK + None + SHA256 | `1408` |
| PSK + None + SHA384 | `1392` |
| PSK + None + SHA512 | `1376` |

## Quellen

- [OpenVPN's network protocol](https://build.openvpn.net/doxygen/network_protocol.html)
- [Data Channel Crypto module](https://build.openvpn.net/doxygen/group__data__crypto.html)
- [Does AES_cbc_encrypt add padding?](https://stackoverflow.com/questions/31226594/does-aes-cbc-encrypt-add-padding)
- [What size of initialization vector (IV) is needed for AES encryption?](https://crypto.stackexchange.com/questions/50782/what-size-of-initialization-vector-iv-is-needed-for-aes-encryption)]]>
        </content>
    </entry>
    
    <entry>
        <title>How large is the MTU in an OpenVPN tunnel?</title>
        
        <link rel="alternate" href="https://mk16.de/blog/openvpn-mtu-en/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/openvpn-mtu-en/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/openvpn-mtu-en/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/openvpn-mtu-en/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/openvpn-mtu-en/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/openvpn-mtu-en/" hreflang="en" />

        <id>/blog/openvpn-mtu-en/</id>
        <published>2024-10-04T01:00:00+02:00</published>
        <updated>2024-10-04T01:00:00+02:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>What MTU does OpenVPN have? What overhead does OpenVPN have? How does this relate to the ciphers used?</summary>
        <content type="html">
            <![CDATA[OpenVPN uses 1500 as MTU by default. However, this can lead to fragmentation of the outer VPN packets, which is generally to be avoided. It is therefore necessary to reduce the MTU in the tunnel so that no fragmentation occurs. This raises the question of how large the ideal MTU may be. Unfortunately, OpenVPN does not offer any simple documentation for this.

The actual data that is transported in the tunnel is also referred to as payload. However, various headers are presented before this data. These are used, among other things, to ensure that the packets are delivered correctly (IP routing) and to transmit various cryptographic parameters.

The process of placing several headers in front of the actual user data is also known as encapsulation.

The following encapsulations exist:

- IP header
- UDP/TCP header
- OpenVPN header
- Payload

In order to know the optimum MTU - i.e. the maximum size of the payload without fragmentation - the size of the IP header, the UDP/TCP header and the OpenVPN header must be known.

## IP header

Normally, an IP header has a size of 20-40 bytes. If IPv4 is used, it is 20 bytes, with IPv6 it is 40 bytes. If you specify a DNS name rather than a fixed IP address as the endpoint, the software or operating system normally decides whether IPv4 or IPv6 should be used. I therefore recommend always using 40 bytes as the IP header.

TL;DR: 40 bytes

## UDP/TCP header

In OpenVPN, you can choose between UDP and TCP as the transport protocol. UDP is generally recommended for VPN applications. The header size there is 8 bytes. With TCP, the header size is between 20 and 60 bytes. OpenVPN does not seem to enforce the use of 20 bytes. I therefore recommend using 60 bytes for TCP, the maximum size of a TCP header.

TL;DR: 8 bytes for UDP, 60 bytes for TCP

## OpenVPN header

The OpenVPN header differs depending on the cipher and operating mode used. In addition, there are two different header formats `P_DATA_V1` and `P_DATA_V2`. Modern OpenVPN clients should actually only use `P_DATA_V2`. To negotiate the cryptographic parameters, OpenVPN supports two methods: TLS and Preshared Key (PSK). PSK is outdated and should no longer be used. In addition, the PSK method does not support Perfect Forward Secrecy (PFS), which is a desirable cryptographic property. I assume a “modern” OpenVPN client that uses `P_DATA_V2` for the following information.

### AEAD ciphers / GCM mode (also CHACHA20-POLY1305)

The OpenVPN header consists of the following components:

- 1 byte opcode and key ID (5-bit opcode and 3-bit key ID)
- 3 bytes peer ID
- 4 bytes packet ID
- 16 bytes tag

TL;DR: Overhead = 24 bytes

### CBC mode

The OpenVPN header consists of the following components:

- 1 bytes opcode and key ID (5-bit opcode and 3-bit key ID)
- 3 bytes peer ID
- HMAC
- IV
- 4 bytes Packet ID
- Only for PSK: 4 bytes Timestamp
- Padding

If the optimum MTU is not to be calculated exactly, 16 bytes can be used for padding.

In CBC mode, the packet ID and the timestamp (for PSK) are also encrypted. If the length of this encrypted text is a multiple of 16, a padding of 16 bytes is appended. Otherwise, a padding is appended so that the length is a multiple of 16.
The following algorithm can be used to calculate the optimum MTU:
1. Calculate the temporary MTU without packet ID, timestamp or padding.
2. Is the temporary MTU a multiple of 16?
    - If no: Determine the largest number that is less than temporary MTU + 4 (packet ID) + 4 (timestamp for PSK) and is a multiple of 16.
3. Subtract 4 (packet ID), 4 (timestamp for PSK) and 1.

TL;DR: Overhead = 26 bytes + IV size + HMAC size + 4 bytes timestamp (for PSK)

### CFB/OFB mode

The OpenVPN header consists of the following components:

- 1 byte Opcode and Key-ID (5-bit Opcode and 3-bit Key-ID)
- 3 bytes Peer-ID
- HMAC
- IV

TL;DR: Overhead = 4 bytes + HMAC size + IV size

### No encryption

With the `none` cipher, OpenVPN offers a mode which does not encrypt the data, but only authenticates it.

The OpenVPN header then consists of the following components:

- 1 byte opcode and key ID (5-bit opcode and 3-bit key ID)
- 3 bytes peer ID
- HMAC
- 4 bytes Packet-ID
- Only for PSK: 4 byte timestamp

TL;DR: Overhead = 8 bytes + HMAC size + 4 bytes timestamp (for PSK)

## IV size

The IV of most ciphers is 16 bytes in size. The size of the IV can be found out via the block size. This can be displayed with `openvpn --show-ciphers`.

## HMAC size

Sometimes the size of the HMAC is needed to calculate the MTU. Below is a table with the HMAC on the right and its size on the left.

| HMAC | size |
| --- | --- |
| SHA1 | 20 bytes |
| SHA256 / SHA2-256 / SHA3-256 | 32 bytes |
| SHA384 / SHA2-384 / SHA3-384 | 48 bytes |
| SHA512 / SHA2-512 / SHA3-512 | 64 bytes |

Alternatively, you can use `openvpn --show-digests` to determine the sizes.

## Recommended MTUs

The following table assumes that the outer MTU is 1500 bytes, that UDP is used as the transport protocol, that the IV is 16 bytes in size and that IPv6 could be used.

| Encryption + Mode | MTU |
| --- | --- |
| TLS + AEAD/GCM | `1428` |
| TLS + CBC + SHA1 | `1403` |
| TLS + CBC + SHA256 | `1387` |
| TLS + CBC + SHA384 | `1371` |
| TLS + CBC + SHA512 | `1355` |
| PSK + CBC + SHA1 | `1399` |
| PSK + CBC + SHA256 | `1383` |
| PSK + CBC + SHA384 | `1367` |
| PSK + CBC + SHA512 | `1351` |
| TLS + CFB/OFB + SHA1 | `1412` |
| TLS + CFB/OFB + SHA256 | `1400` |
| TLS + CFB/OFB + SHA384 | `1384` |
| TLS + CFB/OFB + SHA512 | `1368` |
| TLS + None + SHA1 | `1424` |
| TLS + None + SHA256 | `1412` |
| TLS + None + SHA384 | `1396` |
| TLS + None + SHA512 | `1380` |
| PSK + None + SHA1 | `1420` |
| PSK + None + SHA256 | `1408` |
| PSK + None + SHA384 | `1392` |
| PSK + None + SHA512 | `1376` |

## Sources

- [OpenVPN's network protocol](https://build.openvpn.net/doxygen/network_protocol.html)
- [Data Channel Crypto module](https://build.openvpn.net/doxygen/group__data__crypto.html)
- [Does AES_cbc_encrypt add padding?](https://stackoverflow.com/questions/31226594/does-aes-cbc-encrypt-add-padding)
- [What size of initialization vector (IV) is needed for AES encryption?](https://crypto.stackexchange.com/questions/50782/what-size-of-initialization-vector-iv-is-needed-for-aes-encryption)]]>
        </content>
    </entry>
    
    <entry>
        <title>Falsche Uhrzeit auf Android / Chrony in Termux</title>
        
        <link rel="alternate" href="https://mk16.de/blog/chrony-in-termux-de/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/chrony-in-termux-de/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/chrony-in-termux-de/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/chrony-in-termux-de/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/chrony-in-termux-de/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/chrony-in-termux-de/" hreflang="de" />

        <id>/blog/chrony-in-termux-de/</id>
        <published>2023-11-14T00:00:00+01:00</published>
        <updated>2023-11-14T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Ein ping kommt an bevor er abgesendet wurde? Wie kann das sein? Und was hat die Uhrzeit damit zu tun? In diesem Blogeintrag schaue ich, wie ich auf Android schauen kann, um wie viele Sekunden die Uhr falsch läuft.</summary>
        <content type="html">
            <![CDATA[Ich habe neulich [sping](https://codeberg.org/mark22k/sping) in [Termux](https://termux.dev/) ausprobiert. sping ist ein Protokoll, welches asymmetrische Latenzen und asymmetrischen Paketverlust messen kann. Dies bedeutet, dass sping mir die Zeit mitteilen kann, wie lange ein Paket zum anderen Peer braucht und wie lange es vom Peer zu mir zurück braucht. Außerdem kann mir sping wenn ein Paket verloren geht mitteilen, auf welcher dieser zwei Strecken - Hin- oder Rückweg - es verloren gegangen ist. Dies ist besonders hilfreich, wenn die Wege zum Peer Hin- und Zurück unterschiedlich sind. Das Phänomen, dass die Hin- und Rückwege unterschiedlich sind, wird auch als "asymmetrisches Routing" bezeichnet.

Nun wollte ich das ganze auf meinem Smartphone in Termux ausprobieren. Also habe ich eine sping-Implementation heruntergeladen und an meinem Server getestet. Das Resultat war Folgendes:
```
[fd00:8e13:ce5d::1] RX: -594.034314ms TX: 650.83909ms [Loss RX: 1/32 | Loss TX: 0/32]
```
Wenn man das Resultat interpretiert, bedeutet dies, dass mein Smartphone das Antwortpaket etwa eine halbe Sekunde bekommen hat, bevor der Server dieses überhaupt abgesendet hat. Dies ist natürlich nicht möglich. Im Allgemeinen deutet eine solche Ausgabe darauf hin, dass die Uhren der beiden Peers nicht synchron sind. Da nur die Zeitdifferenz gemessen wird, ist es dabei irrelevant, ob die Uhren richtig gehen - solange beide synchron sind. Das Phänomen entsteht, da das Antwortpaket die Uhrzeit enthält, wann der Server das Paket verarbeitet und gesendet hat. Beim anderen Peer angekommen, wird dann die Differenz der Uhrzeit der Ankunft des Paketes und der Uhrzeit, wann dieses gesendet wurde (die Information in im Paket selber vorhanden) berechnet. Eine solche falsche Uhr hat in diesem Fall auch dazu geführt, dass ein Paket als "verloren" markiert wurden ist.

## Welche Uhr geht falsch?

Auf meinem Server, auf welchem ich meinen sping-Server installiert hatte, habe ich chrony - einen bekannten NTP-Daemon - laufen. Nachdem ich überprüft habe, dass dieser wie gewünscht läuft, habe ich mir die Uhrzeit auf meinem Smartphone näher angeschaut. Auf diesem läuft LineageOS (Android). Ich wollte nun herausfinden, ob die Uhr auf meinem Smartphone falsch geht und wenn ja, um wie viele Sekunden.

Um dies zu tun, habe ich chrony installiert. Ich habe später dazu auch ein [Skript](#skript) für die Installtion geschrieben und eine [Anfrage an Termux](https://github.com/termux/termux-packages/pull/18504) gesendet, ob chrony in das Termux Paket Repository aufgenommen werden kann.

Folgendes war das Ergebnis:
```
$ chronyd -Q -t 3 'server ptbtime1.ptb.de iburst maxsamples 1'
2023-11-14T17:34:36Z chronyd version 4.4 starting (+CMDMON +NTP -REFCLOCK +RTC -PRIVDROP -SCFILTER -SIGND +ASYNCDNS +NTS +SECHASH +IPV6 -DEBUG)
2023-11-14T17:34:36Z Disabled control of system clock
2023-11-14T17:34:40Z System clock wrong by -1.282094 seconds (ignored)
2023-11-14T17:34:40Z chronyd exiting
$ chronyd -Q -t 3 'server ptbtime4.ptb.de iburst maxsamples 1'
2023-11-14T17:34:53Z chronyd version 4.4 starting (+CMDMON +NTP -REFCLOCK +RTC -PRIVDROP -SCFILTER -SIGND +ASYNCDNS +NTS +SECHASH +IPV6 -DEBUG)
2023-11-14T17:34:53Z Disabled control of system clock
2023-11-14T17:34:57Z System clock wrong by -1.275957 seconds (ignored)
2023-11-14T17:34:57Z chronyd exiting
$ chronyd -Q -t 3 'server time.cloudflare.com iburst maxsamples 1'
2023-11-14T17:35:02Z chronyd version 4.4 starting (+CMDMON +NTP -REFCLOCK +RTC -PRIVDROP -SCFILTER -SIGND +ASYNCDNS +NTS +SECHASH +IPV6 -DEBUG)
2023-11-14T17:35:02Z Disabled control of system clock
2023-11-14T17:35:06Z System clock wrong by -1.281283 seconds (ignored)
2023-11-14T17:35:06Z chronyd exiting
```
Ich habe chrony die Zeit mit verschiedenen Servern als Referenz überprüfen lassen. Man sieht, dass die Uhr auf meinem Smartphone um etwa 1.2 Sekunden vorgeht, was die Ausgabe von sping erklärt.

## Skript

`CHRONY_VERSION` wird durch die aktuelle Version von chrony ersetzt.
```sh
#!/data/data/com.termux/files/usr/bin/bash

CHRONY_VERSION=4.4

die () {
    >&2 echo "$1"
    exit 1
}

pkg install -y build-essential clang pkg-config readline libtomcrypt libnettle libnss libgnutls libcap gnupg || die "Could not install all required packages."

curl "https://chrony-project.org/releases/chrony-${CHRONY_VERSION}.tar.gz" --output "chrony-${CHRONY_VERSION}.tar.gz" || die "Could not download chrony."
curl "https://chrony-project.org/releases/chrony-${CHRONY_VERSION}-tar-gz-asc.txt" --output "chrony-${CHRONY_VERSION}-tar-gz-asc.txt" || die "Could not download the GPG signature from chrony."

gpg --keyserver "keyserver.ubuntu.com" --recv-keys "8F375C7E8D0EE125A3D3BD51537E2B76F7680DAC" || die "Could not download the GPG key to verify the signature."

gpg --verify "chrony-${CHRONY_VERSION}-tar-gz-asc.txt" "chrony-${CHRONY_VERSION}.tar.gz" || die "Could not verify the signature."

tar xzf "chrony-${CHRONY_VERSION}.tar.gz" || die "Could not extract the chrony download."
# x = extract
# z = gzip
# f = file

(
    cd "chrony-${CHRONY_VERSION}/" || die "Could not change to the downloaded directory of chrony."

    # chrony can currently (not yet) adjust the time on Android, see https://gitlab.com/chrony/chrony/-/issues/1
    # For the build to be successful, this functionality must be deactivated with `--disable-refclock`.
    ./configure \
        --disable-refclock \
        --prefix="/data/data/com.termux/files/usr/" \
        --sysconfdir="/data/data/com.termux/files/usr/etc" \
        --localstatedir="/data/data/com.termux/files/usr/var" \
            || die "Could not successfully perform the preparation of the compilation of chrony."
    make -j "$(nproc)" || die "Could not compile chrony successfully."
    make install || die "Could not install chrony successfully."
)
```

Update zu chrony in Termux Package Repository: @sylirre hat es hinbekommen, ein Build-Script für Termux zu bauen. Es ist nun in den [Paketquellen vorhanden](https://github.com/termux/termux-packages/commit/211588e6031e37e2cb7600362224a2da35856bae).]]>
        </content>
    </entry>
    
    <entry>
        <title>Wrong time on Android / Chrony in Termux</title>
        
        <link rel="alternate" href="https://mk16.de/blog/chrony-in-termux-en/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/chrony-in-termux-en/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/chrony-in-termux-en/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/chrony-in-termux-en/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/chrony-in-termux-en/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/chrony-in-termux-en/" hreflang="en" />

        <id>/blog/chrony-in-termux-en/</id>
        <published>2023-11-14T00:00:00+01:00</published>
        <updated>2023-11-14T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>A ping arrives before it has been sent? How can that be? And what does the time have to do with it? In this blog entry, I look at how I can see how many seconds the clock is wrong on Android.</summary>
        <content type="html">
            <![CDATA[I recently tried [sping](https://codeberg.org/mark22k/sping) in [Termux](https://termux.dev/). sping is a protocol that can measure asymmetric latency and asymmetric packet loss. This means that sping can tell me how long a packet takes to get to the other peer and how long it takes to get back from the peer to me. In addition, if a packet is lost, sping can tell me on which of these two routes - outbound or return - it was lost. This is particularly helpful if the outward and return routes to the peer are different. The phenomenon that the outbound and return routes are different is also known as "asymmetric routing".

Now I wanted to try this out on my smartphone in Termux. So I downloaded a sping implementation and tested it with my server. The result was as follows:
```
[fd00:8e13:ce5d::1] RX: -594.034314ms TX: 650.83909ms [Loss RX: 1/32 | Loss TX: 0/32]
```
If you interpret the result, this means that my smartphone received the response packet about half a second before the server even sent it. This is of course not possible. In general, such an output indicates that the clocks of the two peers are not synchronized. As only the time difference is measured, it is irrelevant whether the clocks are correct - as long as both are synchronized. The phenomenon arises because the response packet contains the time at which the server processed and sent the packet. When it arrives at the other peer, the difference between the time of arrival of the packet and the time at which it was sent (the information in the packet itself) is calculated. In this case, such an incorrect clock has also led to a packet being marked as "lost".

## Which clock is wrong?

On my server, on which I had installed my sping server, I have chrony - a well-known NTP daemon - running. After checking that it is running as desired, I took a closer look at the time on my smartphone. This is running LineageOS (Android). I now wanted to find out whether the clock on my smartphone was wrong and if so, by how many seconds.

To do this, I installed chrony. I later wrote a [script](#script) for the installation and sent a [request to Termux](https://github.com/termux/termux-packages/pull/18504) asking if chrony could be included in the Termux package repository.

The result was as follows:
```
$ chronyd -Q -t 3 'server ptbtime1.ptb.de iburst maxsamples 1'
2023-11-14T17:34:36Z chronyd version 4.4 starting (+CMDMON +NTP -REFCLOCK +RTC -PRIVDROP -SCFILTER -SIGND +ASYNCDNS +NTS +SECHASH +IPV6 -DEBUG)
2023-11-14T17:34:36Z Disabled control of system clock
2023-11-14T17:34:40Z System clock wrong by -1.282094 seconds (ignored)
2023-11-14T17:34:40Z chronyd exiting
$ chronyd -Q -t 3 'server ptbtime4.ptb.de iburst maxsamples 1'
2023-11-14T17:34:53Z chronyd version 4.4 starting (+CMDMON +NTP -REFCLOCK +RTC -PRIVDROP -SCFILTER -SIGND +ASYNCDNS +NTS +SECHASH +IPV6 -DEBUG)
2023-11-14T17:34:53Z Disabled control of system clock
2023-11-14T17:34:57Z System clock wrong by -1.275957 seconds (ignored)
2023-11-14T17:34:57Z chronyd exiting
$ chronyd -Q -t 3 'server time.cloudflare.com iburst maxsamples 1'
2023-11-14T17:35:02Z chronyd version 4.4 starting (+CMDMON +NTP -REFCLOCK +RTC -PRIVDROP -SCFILTER -SIGND +ASYNCDNS +NTS +SECHASH +IPV6 -DEBUG)
2023-11-14T17:35:02Z Disabled control of system clock
2023-11-14T17:35:06Z System clock wrong by -1.281283 seconds (ignored)
2023-11-14T17:35:06Z chronyd exiting
```
I had chrony check the time with different servers as a reference. You can see that the clock on my smartphone is about 1.2 seconds ahead, which explains the output of sping.

## Script

`CHRONY_VERSION` is replaced by the current version of chrony.
```sh
#!/data/data/com.termux/files/usr/bin/bash

CHRONY_VERSION=4.4

die () {
    >&2 echo "$1"
    exit 1
}

pkg install -y build-essential clang pkg-config readline libtomcrypt libnettle libnss libgnutls libcap gnupg || die "Could not install all required packages."

curl "https://chrony-project.org/releases/chrony-${CHRONY_VERSION}.tar.gz" --output "chrony-${CHRONY_VERSION}.tar.gz" || die "Could not download chrony."
curl "https://chrony-project.org/releases/chrony-${CHRONY_VERSION}-tar-gz-asc.txt" --output "chrony-${CHRONY_VERSION}-tar-gz-asc.txt" || die "Could not download the GPG signature from chrony."

gpg --keyserver "keyserver.ubuntu.com" --recv-keys "8F375C7E8D0EE125A3D3BD51537E2B76F7680DAC" || die "Could not download the GPG key to verify the signature."

gpg --verify "chrony-${CHRONY_VERSION}-tar-gz-asc.txt" "chrony-${CHRONY_VERSION}.tar.gz" || die "Could not verify the signature."

tar xzf "chrony-${CHRONY_VERSION}.tar.gz" || die "Could not extract the chrony download."
# x = extract
# z = gzip
# f = file

(
    cd "chrony-${CHRONY_VERSION}/" || die "Could not change to the downloaded directory of chrony."

    # chrony can currently (not yet) adjust the time on Android, see https://gitlab.com/chrony/chrony/-/issues/1
    # For the build to be successful, this functionality must be deactivated with `--disable-refclock`.
    ./configure \
        --disable-refclock \
        --prefix="/data/data/com.termux/files/usr/" \
        --sysconfdir="/data/data/com.termux/files/usr/etc" \
        --localstatedir="/data/data/com.termux/files/usr/var" \
            || die "Could not successfully perform the preparation of the compilation of chrony."
    make -j "$(nproc)" || die "Could not compile chrony successfully."
    make install || die "Could not install chrony successfully."
)
```

Update to chrony in Termux Package Repository: @sylirre has managed to create a build script for Termux. It is [now available](https://github.com/termux/termux-packages/commit/211588e6031e37e2cb7600362224a2da35856bae) in the package repositories.]]>
        </content>
    </entry>
    
    <entry>
        <title>traceroute and tracepath</title>
        
        <link rel="alternate" href="https://mk16.de/blog/traceroute-and-tracepath/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/traceroute-and-tracepath/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/traceroute-and-tracepath/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/traceroute-and-tracepath/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/traceroute-and-tracepath/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/traceroute-and-tracepath/" hreflang="en" />

        <id>/blog/traceroute-and-tracepath/</id>
        <published>2023-05-08T01:12:00+02:00</published>
        <updated>2025-02-21T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>How do traceroute and tracepath work? Where are the differences? What are the obstacles? Is it possible to perform tracing manually?</summary>
        <content type="html">
            <![CDATA[## What is it?

traceroute and tracepath are command line tools that allow to track which paths packets take to the destination. When you send a packet, it takes a very specific route on its way to the destination. In home networks, you usually send the requests to your router, which acts as a gateway to the Internet. The router then forwards the packet to an Internet Service Provider (ISP), which forwards the packet to the next network so that it gets closer and closer to its destination. For various reasons, it can be helpful to know this path.

![Scenario described below shown graphically as an image](/assets/img/traceroute-tracepath/DNS_request_-_symetric_routing.png)

In this example, a DNS request is sent to a DNS server. This is generated in the user's computer and next sent to the home router (for example, OpenWrt), which in turn forwards the request to the ISP. The ISP now looks up which route leads to the destination and sends the packet on accordingly. At some point it arrives at the router from the DNS server. The router forwards the request to the DNS server. The DNS server responds and the packet takes the same route back.

## What can go wrong?

A number of things can go wrong along the way:
1. The packet gets lost: DNS uses the connectionless UDP protocol by default. So it can happen that the packet gets lost without anyone noticing anything. In the case of DNS, the request is repeated a few times over UDP. However, if that also fails, the request is made over the TCP protocol. ![Described scenario shown graphically as an image](/assets/img/traceroute-tracepath/Packet_gets_lost.png)
2. The packet is too large: It is unlikely that this will happen in the case of DNS, but it can happen with other protocols. Each interface has a Maximum Transmission Unit (MTU). This describes the maximum size of the IP packet. If the packet is larger than the MTU, "something" happens. This "something" is different for IPv4 and IPv6. With IPv6 the packet is discarded and (if configured correctly) an ICMP error message is returned to the sender that the packet is too large. It must then usually be split into several smaller ones. The split packets are then reassembled at the receiver. With IPv4 the same happens if the "Don't fragment" flag (a parameter in the IP header) is set. If it is not set, even the routers, which would otherwise return the ICMP error message, can split the packet and retransmit the split packets. ![Described scenario shown graphically as an image](/assets/img/traceroute-tracepath/Packet_too_big.png)
3. If a request is sent with UDP or TCP, a port is set. However, if the port is not used by any program on the server, an ICMP error message is returned (if configured correctly) that no program is running on the port. However, it may also happen that the server's firewall blocks the request and no ICMP error message is returned. ![Described scenario shown graphically as an image](/assets/img/traceroute-tracepath/Destination_Unreachable_Port_unreachable.png)
4. The packet is on the way too long: Furthermore it can happen that the packet is on the way too long. Normally this is an indication that there is an error somewhere in the network. Every IP packet (so also UDP datagrams and TCP packets) get a Time to Live (TTL) value. After each hop (for example, each router) that the packet traverses, the TTL value is decreased by one. If the value reaches zero, an ICMP error message is returned. This value exists, among other things, to prevent loops from occurring and packets from circulating for ages. The maximum TTL value is 255, but often, but not always, a TTL value of 64 is used. ![Described scenario shown graphically as an image](/assets/img/traceroute-tracepath/Time_Exceeded_hop_limit_exceeded_in_transit.png)
5) The ICMP error message is blocked: There may be firewalls at both the server and routers that block the ICMP error message. This can be a nuisance for the sender as it does not know what happened to his packet.

## Asymmetric routing

It may happen that packets do not take the same outward and return route. For example, a package may pass through eight hops on the outbound trip and only five hops on the return trip, or vice versa. There can also be the same number of hops on the outward journey as on the return journey, but these do not have to be the same as on the outward journey (same number of hops, different route). If something like this happens, it is called "asymmetric routing", otherwise it is called "symmetric" routing. Asymmetric routing doesn't have to be bad - but it can lead to problems with misconfigurations.

![Above Described scenario shown graphically as an image](/assets/img/traceroute-tracepath/DNS_request_-_asymetric_routing.png)

## How does traceroute work?

Traceroute and tracepath in turn determine the hops (so computers that do IP routing) that lie between the sender and the recipient. The functionality of traceroute and tracepath is explained in the manual pages and is actually quite simple:

Traceroute sends out "probes" to the destination. These probes have a TTL of one, then two, and so on. When the TTL expires, an ICMP error message is returned. Since this error message also has a sender, you now know the router that sent the error message and thus you now know one of the routers that is on the path to the destination. In the next probe you increase the TTL by one and send the packet again. Another router should reply with an ICMP error message. This continues until you reach the destination. Traceroute tries to reach a probably unassigned UDP port. When the packet arrives at the server, it responds with an ICMP error message that no program is running on the port. This way you know that you have reached the destination.

![Described scenario shown graphically as an image](/assets/img/traceroute-tracepath/UDP_packet_with_TTL_1.png)
![Described scenario shown graphically as an image](/assets/img/traceroute-tracepath/UDP_packet_with_TTL_2.png)

> traceroute  tracks  the route packets taken from an IP network on their way to a given host. It utilizes the IP protocol's time to  live  (TTL) field  and  attempts to elicit an ICMP TIME_EXCEEDED response from each gateway along the path to the host.

>  This program attempts to trace the route an IP packet would  follow  to some internet host by launching probe packets with a small ttl (time to live) then listening for an ICMP "time exceeded" reply from a  gateway. We  start our probes with a ttl of one and increase by one until we get an ICMP "port unreachable" (or TCP reset), which means we  got  to  the "host",  or hit a max (which defaults to 30 hops).

If the host is not reached after 30 hops, the attempt is aborted and only the previous hops are output.

By default, the ICMP error messages coming from the hops on the path do not contain any information about where they are on the path - i.e. whether they are the first, the second, the third, ... Hop. Therefore Traceroute uses a little trick. The packets that Traceroute sends by default are UDP packets. With the TTL of one the port 33434 is used, with a TTL of two the port 33435, with a TTL of three the port 33436. Since the original packet comes back with the ICMP error message, traceroute can in turn determine the port from it, and so traceroute knows at which TTL (and therefore where on the path) the hop is located.

Traceroute can also be used with an ICMP and a TCP mode. So that instead of UDP packets ICMP or TCP packets are used.

Traceroute sends three packet probes to each hop. This can be adjusted with `-q` if needed.

## How to interpret the output of traceroute?

```
$ traceroute -6 git.dn42
traceroute to git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7), 30 hops max, 80 byte packets
 1  de.hujk.dn42 (fd94:dba8:42b0:e::1)  4.102 ms  4.070 ms  4.052 ms
 2  de-fra1.burble.dn42 (fd42:4242:2601:31::1)  5.643 ms  5.621 ms  5.583 ms
 3  ca-bhs2.burble.dn42 (fd42:4242:2601:2d::1)  105.657 ms  105.936 ms  105.928 ms
 4  git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)  105.900 ms  105.887 ms  105.852 ms
```

The first thing to do is to enter the traceroute command. You can use different parameters there. For example `-6` for IPv6 or `-4` for IPv4. Furthermore you can use `-m` or `--max-hops=` to set the maximum number of hops (30 by default).

With `--mtu` you can get the maximum MTU. This automatically implies that the packets must not be fragmented (manually adjustable with the `-F` flag) and that at most one probe may be sent at a time (adjustable with `-N`). 

The reverse DNS (rDNS) name and the corresponding IP address are then displayed in the first line. Furthermore the maximum number of hops as well as the size of the packets is displayed. After that comes a listing. First the position of the hop. Then the rDNS name and the IP address. Furthermore the time, which the first, the second and the third probe took.

### Determine MTU

```
$ traceroute -6 --mtu git.dn42
traceroute to git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7), 30 hops max, 65000 byte packets
 1  de.hujk.dn42 (fd94:dba8:42b0:e::1)  4.126 ms F=1420  4.162 ms  4.112 ms
 2  de-fra1.burble.dn42 (fd42:4242:2601:31::1)  5.114 ms F=1280  5.279 ms  5.382 ms
 3  ca-bhs2.burble.dn42 (fd42:4242:2601:2d::1)  103.731 ms  104.233 ms  104.718 ms
 4  git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)  104.492 ms  104.539 ms  103.605 ms
```

Here, in addition to the previous output, an `F=` is added. After that the MTU is displayed. In this case you can see that the sender to the first host has an MTU of 1420. To the second host, however, there is an MTU of only 1280. So it is likely that the connection between `en.hujk.dn42` and `en-fra1.burble.dn42` has an MTU of only 1280.

### A hop does not answer

```
$ traceroute vpnhub1.hack
traceroute to vpnhub1.hack (172.31.2.1), 30 hops max, 60 byte packets
 1  * * *
 2  * * *
 3  * * *
 4  * * *
 5  * * *
 6  * * *
 7  * * *
 8  * * *
 9  * * *
10  * * *
11  * * *
12  * * *
13  * * *
14  * * *
15  * * *
16  * * *
17  * * *
18  * * *
19  * * *
20  * * *
21  * * *
22  * * *
23  * * *
24  * * *
25  * * *
26  * * *
27  * * *
28  * * *
29  * * *
30  * * *
```

If a hop does not respond, this is shown with `* * *`. The `*` means that there was no response to this probe. This can be the case, for example, due to a too restrictive firewall. An rDNS name or IP address will then also not be shown, because the hop and its IP address are not known.

To work around this problem you can try the ICMP mode. In this mode ICMP messages are sent instead of UDP datagrams. Some firewalls only block UDP packets, but not ICMP packets.

```
$ traceroute -6 bbs.dn42
traceroute to bbs.dn42 (fd42:1919:810::3), 30 hops max, 80 byte packets
 1  de.hujk.dn42 (fd94:dba8:42b0:e::1)  4.143 ms  4.137 ms  4.108 ms
 2  * usw.hujk.dn42 (fd94:dba8:42b0:18::1)  157.087 ms  157.062 ms
 3  lax2.6.nico.dn42 (fd42:1919:810::3)  158.998 ms  158.981 ms  158.954 ms
```

Furthermore, it can also happen that only one hop has a firewall and the others do not. Furthermore, it is also possible that a hop only responds intermittently or partially to a probe.

#### Workaround

```
$ sudo traceroute -I vpnhub1.hack
traceroute to vpnhub1.hack (172.31.2.1), 30 hops max, 60 byte packets
 1  vpnhub1.hack (172.31.2.1)  3.526 ms  3.682 ms  3.674 ms
```

However, ICMP mode requires elevated privileges because raw ICMP messages are sent. Therefore, one can execute this with `sudo`. Another possibility is to give traceroute the `cap_net_raw` privilege:
```
sudo setcap cap_net_raw+ep $(realpath $(which traceroute))
```
There are also [alternative ways to do this](https://github.com/ValentinBELYN/icmplib/blob/main/docs/6-use-icmplib-without-privileges.md).

### Error messages

traceroute can also display some, at first sight "cryptic", hints.

```
$ traceroute -6 fd94:dba8:42b0:e::90
traceroute to fd94:dba8:42b0:e::90 (fd94:dba8:42b0:e::90), 30 hops max, 80 byte packets
 1  de.hujk.dn42 (fd94:dba8:42b0:e::1)  4.502 ms !N  4.450 ms !N  4.461 ms !N
```

In this case, an attempt is made to reach a host which cannot be reached. At the hop where this is detected, a `!N` is then displayed.
`!N` corresponds to the error message `Destination unreachable: No route` and means that the destination network or address is unreachable. For example, it could be temporarily disconnected from the main network or simply does not exist.

> After the trip time, some additional annotation can be printed: !H, !N, or !P (host,  network or protocol  unreachable), !S (source route failed), !F (fragmentation needed), !X (communication administratively prohibited), !V (host precedence violation), !C (precedence cutoff in effect), or !num (ICMP unreachable code num). If almost all the probes result in some kind of unreachable, traceroute will give up and exit.

## How does Tracepath work?

Tracepath works almost the same way as traceroute. In contrast to traceroute, however, it does not always use port 33434+, but a random one. Furthermore, tracepath always determines the MTU without additional parameters and therefore always sets the "Don't fragment" flag in the IP header for IPv4. Another difference is that it sends only one probe instead of three.

## How to interpret the output of tracepath?

```
$ tracepath -p 33434 herzstein.bandura.dn42
 1?: [LOCALHOST]                        0.008ms pmtu 1420
 1:  p2prouter.bandura.dn42                                0.504ms 
 1:  p2prouter.bandura.dn42                                0.611ms 
 2:  laplace.bandura.dn42                                 19.435ms 
 3:  herzstein.bandura.dn42                              203.043ms reached
     Resume: pmtu 1420 hops 3 back 3
```

As always, the first thing to do is to call tracepath. With the option `-p` you can set the start port. In this case it was set to `33434` as in traceroute. This argument is optional. However, since my network, which I use in this example, only responds to UDP traceroutes, I had to set the port manually.
The first thing that is output is the position on the path, as with traceroute. If a `?` is printed after the number, it means that the position is guessed. The first point in this case is the own computer, so `localhost`. This has an MTU of 1420 to the next hop. `pmtu` stands for `path mtu`. Furthermore, as with traceroute, the Round Tripe Time (RTT) is displayed - i.e. how long our packet took to get there and back. When the destination is reached, `reached` is displayed. Finally, we summarize again: A packet to the destination may be a maximum of 1420 bytes in size and takes three hops there and three back.

## MTU and asymmetric routing

```
$ tracepath -p 33434 git.dn42
 1?: [LOCALHOST]                        0.008ms pmtu 1420
 1:  de.hujk.dn42                                          4.221ms 
 1:  de.hujk.dn42                                          4.137ms 
 2:  de.hujk.dn42                                          4.136ms pmtu 1280
 2:  tier1.de-fra1.burble.dn42                             5.133ms 
 3:  tier1.ca-bhs2.burble.dn42                           105.222ms asymm  4 
 4:  git.dn42                                            105.094ms reached
     Resume: pmtu 1280 hops 4 back 5
```

In this output, you can see that the MTU has changed on the way from 1420 to 1280. We could already see this with traceroute. With tracepath you can also clearly see that the MTU has changed between the 1 and 2 hops - i.e. between `en.hujk.dn42` and `en-fra1.burble.dn42`. The second hop shows up twice in the output here because the sample had to be resent with the correct MTU, i.e. a smaller packet. So on the first pass, the packet was stopped at `en.hujk.dn42`, which thus appeared as the second hop. However, the adjusted packet was able to continue and was stopped at (due to the expired TTL) `tier1.de-fra1.burble.dn42`.
Furthermore, tracepath shows us that this is asymmetric routing. At the third hop there is the annotation `asymm 4`. This means that the packet probably needed four hops for the way back. However, this indication is not reliable (see below).
Finally, the summary is output again. The packet has traversed four hops to the destination and five hops back.

It should be noted that tracepath partially guesses the number of hops on the return path. This is calculated by reducing the presumably used maximum TTL by the TTL the return packet has when it arrives. However, since there is no standard for this, tracepath must guess the maximum TTL when it sends, so this specification is unreliable.

### Output of IP addresses

If you prefer to output the IP addresses of the hops instead of the rDNS name, you can use the `-n` flag. If you want to output both the IP address and the rDNS name, you can use the `-b` option.

```
$ tracepath -p 33434 -n git.dn42
 1?: [LOCALHOST]                        0.008ms pmtu 1420
 1:  fd94:dba8:42b0:e::1                                   4.234ms 
 1:  fd94:dba8:42b0:e::1                                   4.263ms 
 2:  fd94:dba8:42b0:e::1                                   4.175ms pmtu 1280
 2:  fd42:4242:2601:31::1                                  5.338ms 
 3:  fd42:4242:2601:2d::1                                104.775ms asymm  4 
 4:  fd42:180:3de0:100:fc5f:3a14:838e:a7a7               104.840ms reached
     Resume: pmtu 1280 hops 4 back 5 
```

### Error Messages

```
$ tracepath -p 33434 fd94:dba8:42b0:e::120
 1?: [LOCALHOST]                        0.026ms pmtu 1420
 1:  de.hujk.dn42                                          4.484ms !N
 1:  de.hujk.dn42                                          4.156ms !N
     Resume: pmtu 1420 
```

tracepath uses the same abbreviations for error messages as traceroute. If an error message is returned, the corresponding abbreviation is displayed after the RTT.

## Manual traceroute using ping

What traceroute and tracepath do automatically can also be done using ping. The ping parameters `-t` to set the TTL, `-s` to determine the size of the payload (and thus the packet), `-Mdo` to prevent automatic fragmentation and, for IPv4, to set the "don't fragment" flag, and `-c` to determine the number of pings help.

If we also want to determine the MTU, it is important to set the payload to the maximum value and disable automatic fragmentation with `-Mdo`.
The maximum payload is calculated by reducing the MTU of the outgoing interface (here 1420, visible with `ip link`) with the IP header (20 bytes for IPv4 and 40 bytes for IPv6) and the ICMP header (here: 4 bytes ICMP header + 4 bytes echo request header): `1420 bytes (MTU) - 40 bytes (IPv6) - 8 bytes (ICMP) = 1372 bytes` Accordingly set the payload with `-s`.

This manual method would correspond to a traceroute in ICMP mode with MTU determination.

Request with maximum payload and a TTL of one:
```
$ ping -t1 -s 1372 -c1 -Mdo -6 git.dn42
PING git.dn42(git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)) 1372 data bytes
From de.hujk.dn42 (fd94:dba8:42b0:e::1) icmp_seq=1 Time exceeded: Hop limit

--- git.dn42 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% Paket loss, time 0ms
```

Request with maximum payload and a TTL of two:
```
$ ping -t2 -s 1372 -c1 -Mdo -6 git.dn42
PING git.dn42(git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)) 1372 data bytes
From de.hujk.dn42 (fd94:dba8:42b0:e::1) icmp_seq=1 Paket too big: mtu=1280

--- git.dn42 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% Paket loss, time 0ms
```

Here the MTU changes, so we recalculate the maximum payload with the new MTU: `1280 bytes (MTU) - 40 bytes (IPv6) - 8 bytes (ICMP) = 1232 bytes`

Request with maximum payload and a TTL of two:
```
$ ping -t2 -s 1232 -c1 -Mdo -6 git.dn42
PING git.dn42(git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)) 1232 data bytes
From tier1.de-fra1.burble.dn42 (fd42:4242:2601:31::1) icmp_seq=1 Time exceeded: Hop limit

--- git.dn42 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% Paket loss, time 0ms
```

Request with maximum payload and a TTL of three:
```
$ ping -t3 -s 1232 -c1 -Mdo -6 git.dn42
PING git.dn42(git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)) 1232 data bytes
From tier1.ca-bhs2.burble.dn42 (fd42:4242:2601:2d::1) icmp_seq=1 Time exceeded: Hop limit

--- git.dn42 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% Paket loss, time 0ms
```

Request with maximum payload and a TTL of four:
```
$ ping -t4 -s 1232 -c1 -Mdo -6 git.dn42
PING git.dn42(git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)) 1232 data bytes
1240 bytes from git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7): icmp_seq=1 ttl=60 time=105 ms

--- git.dn42 ping statistics ---
1 packets transmitted, 1 received, 0% Paket loss, time 0ms
rtt min/avg/max/mdev = 104.894/104.894/104.894/0.000 ms
```

Based on the successful response, we can see that we have reached the destination here. Thus, we have determined all hops as well as the maximum MTU and where it changes.

## Notes

- TTL stands for time to live and is only used in IPv4. In IPv6, TTL is replaced by "Hop Limit" which serves the same function. For simplicity, I have only referred to TTL in this blog post.
- Packet loss can occur not only with UDP. It can also occur with ICMP or TCP. TCP, however, has mechanisms which ensure that a packet sent will arrive reliably. However, UDP is considered (minimally) faster, which is why it is used by default for DNS requests.
- ICMP is only used in IPv4. In IPv6 ICMPv6 is used. However, for simplicity, I will only talk about ICMP.
- Asymmetric routing cannot always be detected by Tracepath - for example, if the number of hops on the outbound and return paths are the same, but a different path was taken anyway.
- Doing traceroutes manually via ping is laborious and inefficient, but can give you a good understanding of how traceroute and tracepath work.

I have made an effort to be correct in content when writing this, but errors can still occur. If you find an error, I would be grateful if you email me :-)

## Addendum

11.05.2023: Error message codes such as `!X` are not the same for traceroute and tracepath. For example, traceroute uses `!X` and tracepath uses `!A` if the message has been administratively filtered.

I could not find any documentation for the codes of Tracepath. Therefore, I opened an [Issue](https://github.com/iputils/iputils/issues/463) on GitHub. But the meaning can be derived quite well from the [source code](https://github.com/iputils/iputils/blob/master/tracepath.c#LL299-L347):

| Ausgabe | Code | Bedeutung |
| `!A` | EACCES | Communication administratively prohibited  |
| `!N` | ENETUNREACH | Destination network unreachable |
| `!H` | EHOSTUNREACH | Destination host unreachable |
| `!P` | EPROTO | Destination protocol unreachable |

This is what I could read from the source code. However, I am not sure if the table is correct.

## Addendum 2

When a packet comes back, traceroute looks at the original packet in the receiving ICMP message. The section above stated that for UDP packets the destination port is used to identify the packet. This is incorrect. Nowadays, in case of UDP the source port and in case of ICMP the ICMP sequence is used for identification. **In the past** traceroute used the method described above. Since the program has grown historically, traceroute still sends packets with destination port 33434 + TTL by default - but this is no longer necessary.

Thanks to Dmitry Butskoy, who answered my question to the [traceroute mailling list](https://sourceforge.net/p/traceroute/mailman/traceroute-devel/?viewmonth=202307) some time ago and pointed out this mistake to me.

## Links

- [5 Ways to Traceroute - wikiHow](https://www.wikihow.com/Traceroute)
- [What ports does trraceroute use?](https://networkengineering.stackexchange.com/questions/45408/traceroute-udp-port-question)
- [What does `!H1` mean?](https://networkengineering.stackexchange.com/questions/16454/difference-between-unresponsive-and-unreachable-h-hosts-in-traceroute-out)
- [TracePath - eduPERT KB](https://wiki.geant.org/display/public/EK/TracePath)
- [what does asymm mean in the results for a traceroute?](https://answers.launchpad.net/ubuntu/+source/gnome-nettool/+question/7682)
- [Use icmplib without root privileges](https://github.com/ValentinBELYN/icmplib/blob/main/docs/6-use-icmplib-without-privileges.md)
- [Why ping works without capability and setuid](https://unix.stackexchange.com/questions/617927/why-ping-works-without-capability-and-setuid)
- [Please add cap_net_raw+ep capabilities to /bin/traceroute](https://bugzilla.redhat.com/show_bug.cgi?id=706554)
- [Security implications of using SETCAP CAP_NET_RAW](https://security.stackexchange.com/questions/128958/security-implications-of-using-setcap-cap-net-raw)]]>
        </content>
    </entry>
    
    <entry>
        <title>traceroute und tracepath</title>
        
        <link rel="alternate" href="https://mk16.de/blog/traceroute-und-tracepath/" hreflang="de" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/traceroute-und-tracepath/" hreflang="de" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/traceroute-und-tracepath/" hreflang="de" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/traceroute-und-tracepath/" hreflang="de" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/traceroute-und-tracepath/" hreflang="de" />
        <link rel="alternate" href="http://bandura.ygg/blog/traceroute-und-tracepath/" hreflang="de" />

        <id>/blog/traceroute-und-tracepath/</id>
        <published>2023-05-08T01:12:00+02:00</published>
        <updated>2025-02-21T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Wie funktionieren traceroute und tracepath? Wo sind die Unterschiede? Welche Hindernisse gibt es? Kann man ein Tracing auch manuell durchführen?</summary>
        <content type="html">
            <![CDATA[## Was ist das?

traceroute und tracepath sind Kommandozeilenwerkzeuge, welche es ermöglichen nachzuvollziehen, welche Wege Pakete zum Ziel nehmen. Wenn man ein Paket versendet, nimmt dieses eine ganz bestimmte Route auf dem Weg zum Ziel. In Heimnetzwerken sendet man die Anfragen im Normalfall zu seinem Router, welcher als Gateway zum Internet fungiert. Dieser leitet das Paket dann an einen Internet Service Provider (ISP), welcher das Paket in das nächste Netzwerk weiterleitet, sodass dieses dem Ziel immer näherkommt. Aus unterschiedlichen Gründen kann es hilfreich sein, diesen Weg zu kennen.

![Unten beschriebenes Szenario als Bild grafisch dargestellt](/assets/img/traceroute-tracepath/DNS_request_-_symetric_routing.png)

In diesem Beispiel wird eine DNS-Anfrage an einen DNS-Server gesendet. Diese wird im Computer des Anwenders generiert und als Nächstes an den Heimrouter (zum Beispiel OpenWrt) gesendet, dieser leitet die Anfrage wiederum an den ISP weiter. Der ISP schaut nun nach, welche Route zum Ziel führt und sendet das Paket entsprechend weiter. Irgendwann kommt es dann am Router vom DNS-Server an. Dieser leitet die Anfrage wiederum an den DNS-Server. Der DNS-Server antwortet und das Paket nimmt den gleichen Weg zurück.

## Was kann schiefgehen?

Auf dem Weg kann einiges schiefgehen:
1. Das Paket geht verloren: DNS verwendet standardmäßig das verbindungslose UDP-Protokoll. Es kann also passieren, dass das Paket verloren geht, ohne dass jemand etwas mitbekommt. Im Fall von DNS wird die Anfrage einige Male über UDP wiederholt. Schlägt das jedoch auch fehl, wird die Anfrage über das TCP-Protokoll gestellt. ![Beschriebenes Szenario als Bild grafisch dargestellt](/assets/img/traceroute-tracepath/Packet_gets_lost.png)
2. Das Paket ist zu groß: Es ist unwahrscheinlich, dass dies im Fall von DNS passiert, jedoch kann dies mit anderen Protokollen passieren. Jedes Interface hat eine Maximum Transmission Unit (MTU). Diese beschreibt, wie groß das IP-Paket maximal sein darf. Wenn das Paket großer als die MTU ist, passiert "etwas". Dieses "etwas" unterscheidet sich bei IPv4 und IPv6. Bei IPv6 wird das Paket verworfen und (wenn richtig konfiguriert) eine ICMP Fehlermeldung an den Absender zurückgegeben, dass das Paket zu groß ist. Dieses muss es dann im Regelfall in mehrere kleinere aufgeteilt werden. Die aufgeteilten Pakete werden dann beim Empfänger wieder zusammengesetzt. Bei IPv4 geschieht das gleiche, wenn das "Don't fragment" Flag (ein Parameter im IP-Header) gesetzt ist. Ist es nicht gesetzt, so können auch die Router, welche sonst die ICMP Fehlermeldung zurückgeben würden, das Paket aufteilen und die geteilten Pakete weitersenden. ![Beschriebenes Szenario als Bild grafisch dargestellt](/assets/img/traceroute-tracepath/Packet_too_big.png)
3. Wenn eine Anfrage mit UDP oder TCP gesendet, wird ein Port gesetzt. Sollte auf dem Server der Port jedoch von keinem Programm benutzt sein, wird (wenn richtig konfiguriert) eine ICMP Fehlermeldung zurückgegeben, dass auf dem Port kein Programm läuft. Es kann jedoch auch vorkommen, dass die Firewall des Servers die Anfrage blockiert und keine ICMP Fehlermeldung zurückgesendet wird. ![Beschriebenes Szenario als Bild grafisch dargestellt](/assets/img/traceroute-tracepath/Destination_Unreachable_Port_unreachable.png)
4. Das Paket ist zu lange unterwegs: Des Weiteren kann es vorkommen, dass das Paket zu lange unterwegs ist. Normalerweise ist dies ein Anzeichen dafür, dass irgendwo im Netzwerk ein Fehler vorliegt. Jedes IP-Paket (also auch UDP-Datagramme und TCP-Pakete) bekommen einen Time to Live (TTL)-Wert. Nach jedem Hop (beispielsweise jedem Router), welches das Paket durchquert, wird der TTL-Wert um eins verringert. Erreicht der Wert null, wird eine ICMP Fehlermeldung zurückgegeben. Diesen Wert gibt es unter anderem, um zu verhindern, dass Schleifen entstehen und Pakete Ewigkeiten im Umlauf sind. Der maximale TTL-Wert ist 255. Häufig, jedoch nicht immer, wird ein TTL-Wert von 64 verwendet. ![Beschriebenes Szenario als Bild grafisch dargestellt](/assets/img/traceroute-tracepath/Time_Exceeded_hop_limit_exceeded_in_transit.png)
5. Die ICMP Fehlermeldung wird blockiert: Es kann sowohl am Server als auch an den Routern Firewalls geben, welche die ICMP Fehlermeldung blockieren. Dies kann ein Ärgernis für den Absender darstellen, da er nicht weiß, was mit seinem Paket passiert ist.

## Asymmetrisches Routing

Es kann vorkommen, dass Pakete nicht den gleichen Hin- und Rückweg nehmen. So kann ein Paket auf dem Hinweg zum Beispiel Acht und auf dem Rückweg nur Fünf Hops passieren oder andersherum. Es können auch gleich viele Hops auf dem Hinweg wie auf dem Rückweg sein, welche aber nicht dieselben wie auf dem Hinweg sein müssen (gleiche Anzahl Hops, anderer Weg). Passiert so etwas, spricht man von "asymmetrischem Routing", sonst von "symmetrischen" Routing. Asymmetrisches Routing muss nicht zwingend schlecht sein - kann aber bei Fehlkonfigurationen zu Problem führen.

![Oben beschriebenes Szenario als Bild grafisch dargestellt](/assets/img/traceroute-tracepath/DNS_request_-_asymetric_routing.png)

## Wie funktioniert Traceroute?

Traceroute und Tracepath wiederum ermitteln die Hops (also Computer, welche IP-Routing machen), welche zwischen dem Absender und dem Empfänger liegen. Die Funktionsweise von traceroute und tracepath ist wiederum in den Manual Pages erklärt und eigentlich recht simpel:

Traceroute sendet "Proben" zum Ziel aus. Diese Proben haben ein TTL von eins, dann von zwei und so weiter. Wenn die TTL abläuft, wird eine ICMP Fehlermeldung zurückgegeben. Da diese Fehlermeldung auch einen Absender hat, weiß man nun den Router, welcher die Fehlermeldung gesendet hat und damit kennt man nun einen der Router, der auf dem Weg zum Ziel liegt. In der nächsten Probe erhöht man das TTL um Eins und sendet das Paket wieder. Es sollte ein anderer Router mit einer ICMP Fehlermeldung antworten. Dies wird so lange fortgesetzt, bis man das Ziel erreicht hat. Traceroute versucht einen vermutlich nicht vergebenen UDP-Port zu erreichen. Wenn das Paket dann beim Server ankommt, antwortet dieser mit einer ICMP Fehlermeldung, dass auf dem Port kein Programm läuft. Dadurch weiß man, dass man das Ziel erreicht hat.

![Beschriebenes Szenario als Bild grafisch dargestellt](/assets/img/traceroute-tracepath/UDP_packet_with_TTL_1.png)
![Beschriebenes Szenario als Bild grafisch dargestellt](/assets/img/traceroute-tracepath/UDP_packet_with_TTL_2.png)

> traceroute verfolgt die Route, die Pakete von einem IP-Netzwerk auf ihrem Weg zu einem Weg zu einem bestimmten Host. Es nutzt das TTL-Feld (Time to Live) des IP-Protokolls Feld des IP-Protokolls und versucht, eine ICMP TIME_EXCEEDED-Antwort von jedem Gateway entlang des Pfades zum Host zu erhalten.

>  Dieses Programm versucht, den Weg eines IP-Pakets zu einem zu einem Internet-Host zu verfolgen, indem es Testpakete mit einer kleinen ttl (time to live) aussendet und dann auf eine ICMP-Antwort "time exceeded" von einem Gateway wartet. Wir beginnen unsere Probes mit einer ttl von eins und erhöhen sie um eins, bis wir eine ein ICMP "port unreachable" (oder TCP reset) erhalten, was bedeutet, dass wir den  "Host" erreicht haben oder auf ein Maximum stoßen (das standardmäßig bei 30 Hops liegt).

Sollte der Host nach 30 Hops nicht erreicht wurden sein, wird der Versuch abgebrochen und nur die bisherigen Hops ausgegeben.

Die ICMP Fehlermeldung, welche von den Hops auf dem Weg kommen, beinhalten standardmäßig keine Angabe darüber, wo diese auf dem Weg liegen - also ob diese der erste, der zweite, der dritte, ... Hop sind. Deswegen greift Traceroute zu einem kleinen Trick. Die Pakete, welche Traceroute, standardmäßig sendet, sind UDP-Paket. Bei dem TTL von eins wird der Port 33434, bei einem TTL von zwei der Port 33435, bei einem TTL von drei der Port 33436 verwendet. Da das Originalpaket mit der ICMP Fehlermeldung zurückkommt, kann traceroute daraus wiederum den Port ermitteln und so weiß traceroute, bei welchem TTL (und daher wo auf dem Weg) der Hop liegt.

Traceroute lässt sich auch mit einem ICMP und einem TCP-Modus benutzen. Sodass, statt UDP-Pakete ICMP- oder TCP-Paket verwendet werden.

Traceroute sendet zu jedem Hop drei Paketproben. Dies kann bei Bedarf mit `-q` angepasst werden.

## Wie ist die Ausgabe von traceroute zu deuten?

```
$ traceroute -6 git.dn42
traceroute to git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7), 30 hops max, 80 byte packets
 1  de.hujk.dn42 (fd94:dba8:42b0:e::1)  4.102 ms  4.070 ms  4.052 ms
 2  de-fra1.burble.dn42 (fd42:4242:2601:31::1)  5.643 ms  5.621 ms  5.583 ms
 3  ca-bhs2.burble.dn42 (fd42:4242:2601:2d::1)  105.657 ms  105.936 ms  105.928 ms
 4  git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)  105.900 ms  105.887 ms  105.852 ms
```

Als Erstes wird der traceroute Befehl eingegeben. Man kann dort verschiedene Parameter benutzen. Zum Beispiel `-6` für IPv6 oder `-4` für IPv4. Des Weiteren kann mit man `-m` oder `--max-hops=` die Anzahl der maximalen Hops festlegen (standardmäßig 30).

Mit `--mtu` kann man die maximale MTU ermitteln lassen. Dies impliziert automatisch, dass die Pakete nicht fragmentiert werden dürfen (manuell mit dem Flag `-F` einstellbar) und dass maximal eine Probe auf einmal gesendet werden darf (einstellbar mit `-N`). 

In der ersten Zeile wird dann der reverse DNS (rDNS) Name sowie die dazugehörige IP-Adresse angezeigt. Des Weiteren wird die maximale Anzahl an Hops sowie die Größe der Pakete ausgegeben. Danach kommt eine Auflistung. Als Erstes steht die Position des Hops. Dann der rDNS Name und die IP-Adresse. Des Weiteren die Zeit, welche die Erste, die zweite und die dritte Probe gebraucht hat.

### MTU ermitteln

```
$ traceroute -6 --mtu git.dn42
traceroute to git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7), 30 hops max, 65000 byte packets
 1  de.hujk.dn42 (fd94:dba8:42b0:e::1)  4.126 ms F=1420  4.162 ms  4.112 ms
 2  de-fra1.burble.dn42 (fd42:4242:2601:31::1)  5.114 ms F=1280  5.279 ms  5.382 ms
 3  ca-bhs2.burble.dn42 (fd42:4242:2601:2d::1)  103.731 ms  104.233 ms  104.718 ms
 4  git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)  104.492 ms  104.539 ms  103.605 ms
```

Hier wird zusätzlich zur vorherigen Ausgabe noch ein `F=` angefügt. Danach steht die MTU. In diesem Fall kann man sehen, dass der Absender zum ersten Host ein MTU von 1420 hat. Zum zweiten Host gibt es allerdings ein MTU von nur 1280. Es ist also wahrscheinlich, dass die Verbindung zwischen `de.hujk.dn42` und `de-fra1.burble.dn42` nur ein MTU von 1280 hat.

### Ein Hop antwortet nicht

```
$ traceroute vpnhub1.hack
traceroute to vpnhub1.hack (172.31.2.1), 30 hops max, 60 byte packets
 1  * * *
 2  * * *
 3  * * *
 4  * * *
 5  * * *
 6  * * *
 7  * * *
 8  * * *
 9  * * *
10  * * *
11  * * *
12  * * *
13  * * *
14  * * *
15  * * *
16  * * *
17  * * *
18  * * *
19  * * *
20  * * *
21  * * *
22  * * *
23  * * *
24  * * *
25  * * *
26  * * *
27  * * *
28  * * *
29  * * *
30  * * *
```

Sollte ein Hop nicht antworten, wird dies mit `* * *` gezeigt. Das `*` bedeutet, dass auf diese Probe keine Antwort kam. Dies kann zum Beispiel aufgrund einer zu restriktiven Firewall der Fall sein. Ein rDNS Name bzw. eine IP-Adresse wird dann auch nicht angezeigt, da der Hop und seine IP-Adresse nicht bekannt sind.

Um dieses Problem zu umgehen, kann man den ICMP Modus probieren. In diesem Modus werden statt UDP-Datagramme ICMP-Nachrichten versendet. Einige Firewalls blockieren lediglich UDP Pakete, jedoch keine ICMP Pakete.

```
$ traceroute -6 bbs.dn42
traceroute to bbs.dn42 (fd42:1919:810::3), 30 hops max, 80 byte packets
 1  de.hujk.dn42 (fd94:dba8:42b0:e::1)  4.143 ms  4.137 ms  4.108 ms
 2  * usw.hujk.dn42 (fd94:dba8:42b0:18::1)  157.087 ms  157.062 ms
 3  lax2.6.nico.dn42 (fd42:1919:810::3)  158.998 ms  158.981 ms  158.954 ms
```

Des Weiteren kann es auch vorkommen, dass nur ein Hop eine Firewall hat und die anderen nicht. Darüber hinaus kann es auch sein, dass ein Hop nur zeitweise bzw. teilweise nicht auf eine Probe antwortet.

#### Workaround

```
$ sudo traceroute -I vpnhub1.hack
traceroute to vpnhub1.hack (172.31.2.1), 30 hops max, 60 byte packets
 1  vpnhub1.hack (172.31.2.1)  3.526 ms  3.682 ms  3.674 ms
```

Der ICMP Modus erfordert jedoch erhöhte Rechte, da rohe ICMP-Nachrichten gesendet werden. Daher kann man dies mit `sudo` ausführen. Eine andere Möglichkeit besteht darin, dass man traceroute das `cap_net_raw`-Privileg gibt:
```
sudo setcap cap_net_raw+ep $(realpath $(which traceroute))
```
Dazu gibt es auch [alternative Möglichkeiten](https://github.com/ValentinBELYN/icmplib/blob/main/docs/6-use-icmplib-without-privileges.md).

### Fehlermeldungen

traceroute kann des Weiteren, einige auf den ersten Blick "kryptische", Hinweise anzeigen.

```
$ traceroute -6 fd94:dba8:42b0:e::90
traceroute to fd94:dba8:42b0:e::90 (fd94:dba8:42b0:e::90), 30 hops max, 80 byte packets
 1  de.hujk.dn42 (fd94:dba8:42b0:e::1)  4.502 ms !N  4.450 ms !N  4.461 ms !N
```

In diesem Fall wird versucht ein Host zu erreichen, welcher nicht erreichbar ist. An dem Hop, wo dies festgestellt wird, wird dann ein `!N` angezeigt.
`!N` entspricht der Fehlermeldung `Destination unreachable: No route` und bedeutet, dass das Zielnetzwerk bzw. die Zieladresse nicht erreichbar ist. Beispielsweise könnte sie temporär vom Hauptnetzwerk getrennt sein oder existiert einfach nicht.

> Nach der Round Trip Time können einige zusätzliche Anmerkungen ausgegeben werden: !H, !N, oder !P (Host, Netzwerk oder Protokoll unerreichbar), !S (Quellroute fehlgeschlagen), !F (Fragmentierung erforderlich), !X (Kommunikation administrativ verboten), !V (Host-Präferenzverletzung), !C (Prioritätsabschaltung in Kraft), oder !num (ICMP Unerreichbarkeitscode num). Wenn fast alle Proben eine Art von Unerreichbarkeit ergeben, gibt Traceroute auf und wird beendet.

## Wie funktioniert Tracepath?

Tracepath funktioniert fast so wie traceroute. In Gegenzug zu traceroute, verwendet es aber nicht immer den Port 33434+, sondern einen Zufälligen. Des Weiteren ermittelt tracepath ohne zusätzlichen Parameter immer das MTU und setzt daher bei IPv4 auch immer das "Don't fragment" Flag im IP-Header. Ein weiterer Unterschied besteht darin, dass es statt drei nur eine Probe aussendet.

## Wie ist die Ausgabe von tracepath zu deuten?

```
$ tracepath -p 33434 herzstein.bandura.dn42
 1?: [LOCALHOST]                        0.008ms pmtu 1420
 1:  p2prouter.bandura.dn42                                0.504ms 
 1:  p2prouter.bandura.dn42                                0.611ms 
 2:  laplace.bandura.dn42                                 19.435ms 
 3:  herzstein.bandura.dn42                              203.043ms reached
     Resume: pmtu 1420 hops 3 back 3
```

Wie immer erfolgt als Erstes der Aufruf von tracepath. Mit der Option `-p` kann man den Startport festlegen. In diesem Fall wurde er auf `33434` wie bei traceroute gesetzt. Die Angabe ist optional. Da mein Netzwerk, welches ich in diesem Beispiel verwende, jedoch nur auf UDP Traceroutes antwortet, musste ich den Port manuell festlegen.
Als Erstes wird wie bei traceroute die Position auf dem Weg ausgegeben. Wird ein `?` nach der Zahl ausgegeben, bedeutet dies, dass die Position geraten ist. Der erste Punkt ist in diesem Fall der eigene Computer, also `localhost`. Dieser hat zum nächsten Hop ein MTU von 1420. `pmtu` steht dabei für `path mtu`. Des Weiteren wird wie bei Traceroute die Round Tripe Time (RTT) angezeigt - also wie lange unser Paket hin und zurück gebraucht hat. Wenn das Ziel erreicht ist, wird `reached` angezeigt. Zum Schluss wird noch einmal zusammengefasst: Ein Paket zum Ziel darf maximal 1420 Bytes groß sein und braucht drei Hops hin und drei zurück.

## MTU und asymetrisches Routing

```
$ tracepath -p 33434 git.dn42
 1?: [LOCALHOST]                        0.008ms pmtu 1420
 1:  de.hujk.dn42                                          4.221ms 
 1:  de.hujk.dn42                                          4.137ms 
 2:  de.hujk.dn42                                          4.136ms pmtu 1280
 2:  tier1.de-fra1.burble.dn42                             5.133ms 
 3:  tier1.ca-bhs2.burble.dn42                           105.222ms asymm  4 
 4:  git.dn42                                            105.094ms reached
     Resume: pmtu 1280 hops 4 back 5
```

In dieser Ausgabe sieht man, dass sich das MTU auf dem Weg von 1420 zu 1280 verändert hat. Dies konnten wir auch schon bei traceroute feststellen. Bei tracepath kann man auch klar erkennen, dass sich das MTU zwischen dem 1 und 2 Hops verändert hat - also zwischen `de.hujk.dn42` und `de-fra1.burble.dn42`. Der zweite Hop taucht hier zweimal in der Ausgabe aus, da die Probe mit der richtigen MTU, also einem kleineren Paket, erneut versendet werden musste. Beim ersten Durchgang wurde das Paket also beim `de.hujk.dn42` angehalten, welche damit als zweiten Hop auftrat. Das angepasste Paket konnte jedoch weiter und wurde bei (aufgrund der ausgelaufenen TTL) `tier1.de-fra1.burble.dn42` aufgehalten.
Des Weiteren zeigt uns tracepath, dass es hier um asymmetrisches Routing handelt. Beim dritten Hop gibt es die Anmerkung `asymm 4`. Dies bedeutet, dass das Paket für den Rückweg vermutlich vier Hops brauchte. Diese Angabe ist jedoch nicht zuverlässig (siehe unten).
Zum Schluss wird wieder die Zusammenfassung ausgegeben. Das Paket hat zum Ziel vier und zurück fünf Hops durchquert.

Dabei sollte man anmerken, dass tracepath die Anzahl der Hops auf dem Rückweg zum Teil errät. Berechnet wird diese, in dem die vermutlich verwendete maximale TTL um die TTL, welche das Rückpaket hat, wenn es ankommt, verringert. Da es jedoch keinen Standard dafür gibt, muss tracepath die maximale TTL beim Absenden erraten, weshalb diese Angabe unzuverlässig ist.

### Ausgabe von IP-Adressen

Möchte man statt dem rDNS Namen lieber die IP-Adressen der Hops ausgegeben haben, so kann man das Flag `-n` benutzen. Wenn Sie sowohl die IP-Adresse als auch den rDNS-Namen ausgeben möchten, kann man das Flag `-b` verwenden.

```
$ tracepath -p 33434 -n git.dn42
 1?: [LOCALHOST]                        0.008ms pmtu 1420
 1:  fd94:dba8:42b0:e::1                                   4.234ms 
 1:  fd94:dba8:42b0:e::1                                   4.263ms 
 2:  fd94:dba8:42b0:e::1                                   4.175ms pmtu 1280
 2:  fd42:4242:2601:31::1                                  5.338ms 
 3:  fd42:4242:2601:2d::1                                104.775ms asymm  4 
 4:  fd42:180:3de0:100:fc5f:3a14:838e:a7a7               104.840ms reached
     Resume: pmtu 1280 hops 4 back 5 
```

### Fehlermeldungen

```
$ tracepath -p 33434 fd94:dba8:42b0:e::120
 1?: [LOCALHOST]                        0.026ms pmtu 1420
 1:  de.hujk.dn42                                          4.484ms !N
 1:  de.hujk.dn42                                          4.156ms !N
     Resume: pmtu 1420 
```

tracepath benutzt die gleichen Abkürzungen für Fehlermeldungen wie traceroute. Sollte eine Fehlermeldung zurückkommen, wird die entsprechende Abkürzung nach der RTT angezeigt.

## Manuelle Traceroute mithilfe von Ping

Das, was traceroute und tracepath automatisiert machen, kann man auch mithilfe von Ping erreichen. Dabei helfen die Ping-Paramater `-t`, um die TTL zu setzen, `-s` um die Größe des Payload (und dadurch des Paketes) zu bestimmten sowie `-Mdo`, um eine automatische Fragmentierung zu verhindern und bei IPv4 um das "Don't fragment" Flag zu setzen sowie `-c` um die Anzahl der Pings zu festzulegen.

Wenn wir auch die MTU ermitteln wollen, ist es wichtig, dass die Payload auf den maximalen Wert festzulegen und die automatische Fragmentierung mit `-Mdo` zu deaktivieren.
Die maximale Payload berechnet man, indem man die MTU des ausgehenden Interfaces (hier 1420, einsehbar mit `ip link`) mit dem IP-Header (20 Bytes bei IPv4 und 40 Bytes bei IPv6) sowie den ICMP-Header (hier: 4 Bytes ICMP-Header + 4 Bytes Echo-Request Header) verringert: `1420 bytes (MTU) - 40 bytes (IPv6) - 8 bytes (ICMP) = 1372 bytes` Entsprechend setzt man das Payload mit `-s`.

Diese manuelle Methode würde einer traceroute im ICMP-Modus mit MTU Ermittlung entsprechen.

Anfrage mit maximalen Payload und einer TTL von Eins:
```
$ ping -t1 -s 1372 -c1 -Mdo -6 git.dn42
PING git.dn42(git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)) 1372 data bytes
From de.hujk.dn42 (fd94:dba8:42b0:e::1) icmp_seq=1 Time exceeded: Hop limit

--- git.dn42 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% Paket loss, time 0ms
```

Anfrage mit maximalen Payload und einer TTL von Zwei:
```
$ ping -t2 -s 1372 -c1 -Mdo -6 git.dn42
PING git.dn42(git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)) 1372 data bytes
From de.hujk.dn42 (fd94:dba8:42b0:e::1) icmp_seq=1 Paket too big: mtu=1280

--- git.dn42 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% Paket loss, time 0ms
```

Hier verändert sich die MTU, also berechnen wir die maximale Payload mit der neuen MTU neu: `1280 bytes (MTU) - 40 bytes (IPv6) - 8 bytes (ICMP) = 1232 bytes`

Anfrage mit maximalen Payload und einer TTL von Zwei:
```
$ ping -t2 -s 1232 -c1 -Mdo -6 git.dn42
PING git.dn42(git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)) 1232 data bytes
From tier1.de-fra1.burble.dn42 (fd42:4242:2601:31::1) icmp_seq=1 Time exceeded: Hop limit

--- git.dn42 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% Paket loss, time 0ms
```

Anfrage mit maximalen Payload und einer TTL von Drei:
```
$ ping -t3 -s 1232 -c1 -Mdo -6 git.dn42
PING git.dn42(git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)) 1232 data bytes
From tier1.ca-bhs2.burble.dn42 (fd42:4242:2601:2d::1) icmp_seq=1 Time exceeded: Hop limit

--- git.dn42 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% Paket loss, time 0ms
```

Anfrage mit maximalen Payload und einer TTL von Vier:
```
$ ping -t4 -s 1232 -c1 -Mdo -6 git.dn42
PING git.dn42(git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7)) 1232 data bytes
1240 bytes from git.dn42 (fd42:180:3de0:100:fc5f:3a14:838e:a7a7): icmp_seq=1 ttl=60 time=105 ms

--- git.dn42 ping statistics ---
1 packets transmitted, 1 received, 0% Paket loss, time 0ms
rtt min/avg/max/mdev = 104.894/104.894/104.894/0.000 ms
```

Aufgrund der erfolgreichen Antwort, kann man feststellen, dass wir hier das Ziel erreicht haben. Damit haben wir alle Hops ermittelt sowie das maximale MTU und wo sich dieses verändert.

## Anmerkungen

- TTL steht für Time to live und wird nur in IPv4 verwendet. In IPv6 wird TTL durch das "Hop Limit" ersetzt, welches die gleiche Funktion erfüllt. Aus einfachheitsgründen habe ich in diesem Blogbeitrag nur von TTL gesprochen.
- Paketverlust kann nicht nur bei UDP auftreten. Er kann genauso bei ICMP oder TCP auftreten. TCP verfügt jedoch über Mechanismen, welche sicherzustellen, dass ein gesendetes Paket auch zuverlässig ankommt. Jedoch gilt UDP als (minimal) schneller, weshalb es standardmäßig für DNS Anfragen verwendet wird.
- ICMP wird nur in IPv4 verwendet. In IPv6 wird ICMPv6 verwendet. Aus Einfachheitsgründen spreche ich jedoch nur von ICMP.
- Asymmetrisches Routing kann nicht immer von Tracepath erkannt werden - beispielsweise, wenn die Anzahl der Hops auf Hin- und Rückweg gleich sind, aber trotzdem ein anderer Weg genommen wurde.
- Manuelles Durchführen von Traceroutes via Ping ist aufwendig und ineffizient, kann einem aber ein gutes Verständnis für die Funktionsweise von Traceroute und Tracepath geben.

Ich habe mir beim Schreiben Mühe gegeben auf inhaltliche Richtigkeit zu achten, jedoch kann es trotzdem zu Fehlern kommen. Wenn du einen Fehler entdeckt, wäre ich dir dankbar, wenn du mir eine E-Mail schreibst :-)

## Nachtrag

11.05.2023: Die Fehlermeldungscodes wie `!X` sind bei traceroute und tracepath nicht gleich. Beispielsweise verwendet traceroute `!X` und tracepath `!A`, wenn die Nachricht administrativ gefiltert worden ist.

Ich konnte keine Dokumentation für die Codes von Tracepath finden. Daher habe ich auf GitHub ein [Issue](https://github.com/iputils/iputils/issues/463) geöffnet. Die Bedeutung lässt sich aber recht gut vom [Quellcode](https://github.com/iputils/iputils/blob/master/tracepath.c#LL299-L347) her ableiten:

| Ausgabe | Code | Bedeutung |
| `!A` | EACCES | Communication administratively prohibited  |
| `!N` | ENETUNREACH | Destination network unreachable |
| `!H` | EHOSTUNREACH | Destination host unreachable |
| `!P` | EPROTO | Destination protocol unreachable |

Dies ist das, was ich aus dem Quellcode lesen konnte. Ich bin mir allerdings nicht sicher, ob die Tabelle richtig ist.

## Nachtrag 2

Wenn ein Paket zurückkommt, schaut sich traceroute das Originalpaket in der empfangenden ICMP-Nachricht an. Im obigen Abschnitt stand, dass bei UDP-Paketen der Zielport für die Identifizierung des Pakets verwendet wird. Das ist falsch. Heutzutage wird bei UDP für die Identifizierung der Quellport verwendet und bei ICMP die ICMP-Sequenz verwendet. **Früher** hat traceroute die oben beschriebene Methode verwendet. Da das Programm historisch gewachsen ist, sendet traceroute standardmäßig immer noch Pakete mit Zielport 33434 + TTL - dies ist jedoch nicht mehr zwingend notwendig.

Danke an Dmitry Butskoy, welche mir bereits vor einiger Zeit auf meine Frage an die [traceroute-Mailling-Liste](https://sourceforge.net/p/traceroute/mailman/traceroute-devel/?viewmonth=202307) geantwortet hat und mich damit auf diesen Fehler hingewiesen hat.

## Links

- [5 Ways to Traceroute - wikiHow](https://www.wikihow.com/Traceroute)
- [What ports does trraceroute use?](https://networkengineering.stackexchange.com/questions/45408/traceroute-udp-port-question)
- [What does `!H1` mean?](https://networkengineering.stackexchange.com/questions/16454/difference-between-unresponsive-and-unreachable-h-hosts-in-traceroute-out)
- [TracePath - eduPERT KB](https://wiki.geant.org/display/public/EK/TracePath)
- [what does asymm mean in the results for a traceroute?](https://answers.launchpad.net/ubuntu/+source/gnome-nettool/+question/7682)
- [Use icmplib without root privileges](https://github.com/ValentinBELYN/icmplib/blob/main/docs/6-use-icmplib-without-privileges.md)
- [Why ping works without capability and setuid](https://unix.stackexchange.com/questions/617927/why-ping-works-without-capability-and-setuid)
- [Please add cap_net_raw+ep capabilities to /bin/traceroute](https://bugzilla.redhat.com/show_bug.cgi?id=706554)
- [Security implications of using SETCAP CAP_NET_RAW](https://security.stackexchange.com/questions/128958/security-implications-of-using-setcap-cap-net-raw)]]>
        </content>
    </entry>
    
    <entry>
        <title>Small collection of commands to filter public Yggdrasil peers</title>
        
        <link rel="alternate" href="https://mk16.de/blog/yggdrasil-get-peers/" hreflang="en" />
        <link rel="alternate" href="https://homepage.bandura.dn42/blog/yggdrasil-get-peers/" hreflang="en" />
        <link rel="alternate" href="http://n6eqyu6glmtgt544ys43ggrozhpiw7biyrelrvmkmg7llfcjwm4a.b32.i2p/blog/yggdrasil-get-peers/" hreflang="en" />
        <link rel="alternate" href="http://homepage.bandura.i2p/blog/yggdrasil-get-peers/" hreflang="en" />
        <link rel="alternate" href="http://uqmsowm6bw6zogeouduw3dgogakzf73f2w2tcel2fxa3pxa55nvilhad.onion/blog/yggdrasil-get-peers/" hreflang="en" />
        <link rel="alternate" href="http://bandura.ygg/blog/yggdrasil-get-peers/" hreflang="en" />

        <id>/blog/yggdrasil-get-peers/</id>
        <published>2024-01-17T00:00:00+01:00</published>
        <updated>2024-01-17T00:00:00+01:00</updated>
        
        <author>
            <name></name>
            
            
        </author>
        
        <summary>Small collection of commands to filter public Yggdrasil peers.</summary>
        <content type="html">
            <![CDATA[In the [Yggdrasil Public Peers Repository](https://github.com/yggdrasil-network/public-peers/) you will find a public list of Yggdrasil peers sorted by region.

Some of these peers are specified using a domain name. However, it is possible that these domain names are incorrectly listed on DNS blocklists, which are intended to keep out advertising and trackers. This has [happened in the past](https://github.com/no-cmyk/Search-Engine-Spam-Blocklist/issues/7) with the aaoth.xyz peer, for example.
To check something like this, you need a command that extracts the domain names from the public peers repository.
Since I recently dealt with this topic again, I thought I would publish the commands:
```sh
grep --recursive --extended-regexp "  \* \`(tcp|tls|quic)://[a-z][a-z0-9\.]*:[0-9]{1,5}\`" public-peers/ | sed --regexp-extended 's/.*:\/\/([^:]+).*/\1/' | sort | uniq
```

However, you can also modify these commands slightly to extract other peers. For example, the following command extracts all peers (DNS, IPv4, IPv6):
```sh
grep --recursive --extended-regexp "  \* \`(tcp|tls|quic)://.+:[0-9]{1,5}\`" public-peers/ | sed --regexp-extended 's/.*`(.*)`.*/\1/' | sort
```

However, you can also restrict the search to a folder or a file. To extract all peers from Germany, for example, the following command can be used:
```sh
grep --recursive --extended-regexp "  \* \`(tcp|tls|quic)://.+:[0-9]{1,5}\`" public-peers/europe/germany.md | sed --regexp-extended 's/.*`(.*)`.*/\1/' | sort
```

This command counts all QUIC peers:
```sh
grep --recursive --extended-regexp "  \* \`(quic)://.+:[0-9]{1,5}\`" public-peers/ | sed --regexp-extended 's/.*`(.*)`.*/\1/' | wc -l
```
At the time of writing this blog entry, for example, there are 36.]]>
        </content>
    </entry>
    
</feed>
