aboutsummaryrefslogtreecommitdiff
path: root/content/en
diff options
context:
space:
mode:
Diffstat (limited to 'content/en')
-rw-r--r--content/en/blog/news/20200502-frcp.md236
1 files changed, 236 insertions, 0 deletions
diff --git a/content/en/blog/news/20200502-frcp.md b/content/en/blog/news/20200502-frcp.md
new file mode 100644
index 0000000..f677730
--- /dev/null
+++ b/content/en/blog/news/20200502-frcp.md
@@ -0,0 +1,236 @@
+---
+date: 2020-02-16
+title: "Flow and Retransmission Control Protocol (FRCP) implementation"
+linkTitle: "Flow and Retransmission Control Protocol (FRCP)"
+description: "A quick demo of FRCP"
+author: Dimitri Staessens
+---
+
+With the longer weekend I had some fun implementing (parts of) the
+[Flow and Retransmission Control Protocol (FRCP)](/docs/concepts/protocols/#flow-and-retransmission-control-protocol-frcp)
+to the point that it's stable enough to bring you a very quick demo of it.
+
+FRCP is the Ouroboros alternative to TCP / QUIC / LLC. It assures
+delivery of packets when the network itself isn't very reliable.
+
+The setup is simple: we run Ouroboros over the Ethernet loopback
+adapter _lo_,
+```
+systemctl restart ouroboros
+irm i b t eth-dix l dix n dix dev lo
+```
+to which we add some impairment
+[_qdisc_](http://man7.org/linux/man-pages/man8/tc-netem.8.html):
+
+```
+$ sudo tc qdisc add dev lo root netem loss 8% duplicate 3% reorder 10% delay 1
+```
+
+This causes the link to lose, duplicate and reorder packets.
+
+We can use the oping tool to uses different [QoS
+specs](https://ouroboros.rocks/cgit/ouroboros/tree/include/ouroboros/qos.h)
+and watch the behaviour. Quality-of-Service (QoS) specs are a
+technology-agnostic way to request a network service (current
+status - not finalized yet). I'll also capture tcpdump output.
+
+We start an oping server and tell Ouroboros for it to listen to the _name_ "oping":
+```
+#bind the program oping to the name oping
+irm b prog oping n oping
+#register the name oping in the Ethernet layer that is attached to the loopback
+irm n r oping l dix
+#run the oping server
+oping -l
+```
+
+We'll now send 20 pings. If you try this, it can be that the flow
+allocation fails, due to the loss of a flow allocation packet (a bit
+similar to TCP losing the first SYN). The oping client currently
+doesn't retry flow allocation. The default payload for oping is 64
+bytes (of zeros); oping waits 2 seconds for all packets it has
+sent. It doesn't detect duplicates.
+
+Let's first look at the _raw_ QoS cube. That's like best-effort
+UDP/IP. In Ouroboros, however, it doesn't require a packet header at
+all.
+
+First, the output of the client using a _raw_ QoS cube:
+```
+$ oping -n oping -c 20 -i 200ms -q raw
+Pinging oping with 64 bytes of data (20 packets):
+
+64 bytes from oping: seq=0 time=0.880 ms
+64 bytes from oping: seq=1 time=0.742 ms
+64 bytes from oping: seq=4 time=1.303 ms
+64 bytes from oping: seq=6 time=0.739 ms
+64 bytes from oping: seq=6 time=0.771 ms [out-of-order]
+64 bytes from oping: seq=6 time=0.789 ms [out-of-order]
+64 bytes from oping: seq=7 time=0.717 ms
+64 bytes from oping: seq=8 time=0.759 ms
+64 bytes from oping: seq=9 time=0.716 ms
+64 bytes from oping: seq=10 time=0.729 ms
+64 bytes from oping: seq=11 time=0.720 ms
+64 bytes from oping: seq=12 time=0.718 ms
+64 bytes from oping: seq=13 time=0.722 ms
+64 bytes from oping: seq=14 time=0.700 ms
+64 bytes from oping: seq=16 time=0.670 ms
+64 bytes from oping: seq=17 time=0.712 ms
+64 bytes from oping: seq=18 time=0.716 ms
+64 bytes from oping: seq=19 time=0.674 ms
+Server timed out.
+
+--- oping ping statistics ---
+20 packets transmitted, 18 received, 2 out-of-order, 10% packet loss, time: 6004.273 ms
+rtt min/avg/max/mdev = 0.670/0.765/1.303/0.142 ms
+```
+
+The _netem_ did a good job of jumbling up the traffic! There were a
+couple out-of-order, duplicates, and quite some packets lost.
+
+Let's dig into an Ethernet frame captured from the "wire". The most
+interesting thing its small total size: 82 bytes.
+
+```
+13:37:25.875092 00:00:00:00:00:00 (oui Ethernet) > 00:00:00:00:00:00 (oui Ethernet), ethertype Unknown (0xa000), length 82:
+ 0x0000: 0042 0040 0000 0001 0000 0011 e90c 0000 .B.@............
+ 0x0010: 0000 0000 203f 350f 0000 0000 0000 0000 .....?5.........
+ 0x0020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+ 0x0030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+ 0x0040: 0000 0000
+```
+
+The first 12 bytes are the two MAC addresses (all zeros), then 2 bytes
+for the "Ethertype" (the default for an Ouroboros layer is 0xa000, so
+you can create more layers and seperate them by Ethertype[^1]. The
+Ethernet Payload is thus 68 bytes. The Ouroboros _ipcpd-eth-dix_ adds
+and extra header of 4 bytes with 2 extra "fields". The first field we
+needed to take over from our [Data
+Transfer](/docs/concepts/protocols/) protocol: the Endpoint Identifier
+that identifies the flow. The _ipcpd-eth-dix_ has two endpoints, one
+for the client and one for the server. 0x0042 (66) is the destination
+EID of the server, 0x0043 (67) is the destination EID of the client.
+The second field is the _length_ of the payload in octets, 0x0040 =
+64. This is needed because Ethernet II has a minimum frame size of 64
+bytes and pads smaller frames (called _runt frames_)[^2]. The
+remaining 64 bytes are the oping payload, giving us an 82 byte packet.
+
+That's it for the raw QoS. The next one is _voice_. A voice service
+usually requires packets to be delivered with little delay and jitter
+(i.e. ASAP). Out-of-order packets are rejected since they cause
+artifacts in the audio output. The voice QoS will enable FRCP, because
+it needs to track sequence numbers.
+
+```
+$ oping -n oping -c 20 -i 200ms -q voice
+Pinging oping with 64 bytes of data (20 packets):
+
+64 bytes from oping: seq=0 time=0.860 ms
+64 bytes from oping: seq=2 time=0.704 ms
+64 bytes from oping: seq=3 time=0.721 ms
+64 bytes from oping: seq=4 time=0.706 ms
+64 bytes from oping: seq=5 time=0.721 ms
+64 bytes from oping: seq=6 time=0.710 ms
+64 bytes from oping: seq=7 time=0.721 ms
+64 bytes from oping: seq=8 time=0.691 ms
+64 bytes from oping: seq=10 time=0.691 ms
+64 bytes from oping: seq=12 time=0.702 ms
+64 bytes from oping: seq=13 time=0.730 ms
+64 bytes from oping: seq=14 time=0.716 ms
+64 bytes from oping: seq=15 time=0.725 ms
+64 bytes from oping: seq=16 time=0.709 ms
+64 bytes from oping: seq=17 time=0.703 ms
+64 bytes from oping: seq=18 time=0.693 ms
+64 bytes from oping: seq=19 time=0.666 ms
+Server timed out.
+
+--- oping ping statistics ---
+20 packets transmitted, 17 received, 0 out-of-order, 15% packet loss, time: 6004.243 ms
+rtt min/avg/max/mdev = 0.666/0.716/0.860/0.040 ms
+```
+
+As you can see, packets are delivered in-order, and some packets are
+missing. Nothing fancy. Let's look at a data packet:
+
+```
+14:06:05.607699 00:00:00:00:00:00 (oui Ethernet) > 00:00:00:00:00:00 (oui Ethernet), ethertype Unknown (0xa000), length 94:
+ 0x0000: 0045 004c 0100 0000 eb1e 73ad 0000 0000 .E.L......s.....
+ 0x0010: 0000 0000 0000 0012 a013 0000 0000 0000 ................
+ 0x0020: 705c e53a 0000 0000 0000 0000 0000 0000 p\.:............
+ 0x0030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+ 0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+
+```
+
+The same 18-byte header is present. The flow endpoint ID is a
+different one, and the length is also different. The packet is 94
+bytes, the payload length for the _ipcp-eth_dix_ is 0x004c = 76
+octets. So the FRCP header adds 12 bytes, the total overhead is 30
+bytes. Maybe a bit more detail on the FRCP header contents (more depth
+is available the protocol documentation). The first 2 bytes are the
+FLAGS (0x0100). There are only 7 flags, it's 16 bits for memory
+alignment. This packet only has the DATA bit set. Then follows the
+flow control window, which is 0 (not implemented yet). Then we have a
+4 byte sequence number (eb1e 73ae = 3944641454)[^3] and a 4 byte ACK
+number, which is 0. The remaining 64 bytes are the oping payload.
+
+Next, the data QoS:
+
+```
+$ oping -n oping -c 20 -i 200ms -q data
+Pinging oping with 64 bytes of data (20 packets):
+
+64 bytes from oping: seq=0 time=0.932 ms
+64 bytes from oping: seq=1 time=0.701 ms
+64 bytes from oping: seq=2 time=200.949 ms
+64 bytes from oping: seq=3 time=0.817 ms
+64 bytes from oping: seq=4 time=0.753 ms
+64 bytes from oping: seq=5 time=0.730 ms
+64 bytes from oping: seq=6 time=0.726 ms
+64 bytes from oping: seq=7 time=0.887 ms
+64 bytes from oping: seq=8 time=0.878 ms
+64 bytes from oping: seq=9 time=0.883 ms
+64 bytes from oping: seq=10 time=0.865 ms
+64 bytes from oping: seq=11 time=401.192 ms
+64 bytes from oping: seq=12 time=201.047 ms
+64 bytes from oping: seq=13 time=0.872 ms
+64 bytes from oping: seq=14 time=0.966 ms
+64 bytes from oping: seq=15 time=0.856 ms
+64 bytes from oping: seq=16 time=0.849 ms
+64 bytes from oping: seq=17 time=0.843 ms
+64 bytes from oping: seq=18 time=0.797 ms
+64 bytes from oping: seq=19 time=0.728 ms
+
+--- oping ping statistics ---
+20 packets transmitted, 20 received, 0 out-of-order, 0% packet loss, time: 4004.491 ms
+rtt min/avg/max/mdev = 0.701/40.864/401.192/104.723 ms
+```
+
+With the data spec, we have no packet loss, but some packets have been
+retransmitted (hence the higher latency). The reason for the very high
+latency is that the current implementation only ACKs on data packets,
+this will be fixed soon.
+
+Looking at an Ethernet frame, it's again 94 bytes:
+
+```
+14:35:42.612066 00:00:00:00:00:00 (oui Ethernet) > 00:00:00:00:00:00 (oui Ethernet), ethertype Unknown (0xa000), length 94:
+ 0x0000: 0044 004c 0700 0000 81b8 0259 e2f3 eb59 .D.L.......Y...Y
+ 0x0010: 0000 0000 0000 0012 911a 0000 0000 0000 ................
+ 0x0020: 86b3 273b 0000 0000 0000 0000 0000 0000 ..';............
+ 0x0030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+ 0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+
+```
+
+The main difference is that it has 2 flags set (DATA + ACK), and it
+thus contains both a sequence number (81b8 0259) and an
+acknowledgement (e2f3 eb59).
+
+That's about it for now. More to come soon.
+
+Dimitri
+
+[^1]: Don't you love standards? One of the key design objectives for Ouroboros is exactly to avoid such shenanigans. Modify/abuse a header and Ouroboros should reject it because it _cannot work_, not because some standard says one shouldn't do it.
+[^2]: Lesser known fact: Gigabit Ethernet has a 512 byte minimum frame size; but _carrier extension_ handles this transparently.
+[^3]: As you have figured out, the loopback is not in _network byte order_. \ No newline at end of file