Add Forward Error Correction (FEC) to Librecast.
IP Multicast is based on UDP, which is inherently unreliable. Packets may arrive out of order, or not at all. TCP provides unicast with a reliable messaging layer on top of this unreliable, connectionless medium.
Unicast, however, is one-to-one only. Multicast could, in theory, use all of the same reliability options (ACKs etc.) as TCP, at the cost of not being scalable any more.
Fortunately there are other ways to achieve similar reliability. RFC3208 describes Pragmatic General Multicast (PGM) based on NAKs (negative acknowledgements). This, too, has scaling issues.
Forward Error Correction (FEC) offers us another approach.
Thanks to parity checking in the network stack, we don't generally need to worry about errors within packets. Every packet has a checksum, and if that doesn't match, the packet is dropped before it reaches us. Our encryption provides further checking of data received. We need only concern ourselves with erasures. ie. dropped packets.
FEC encodes the data in such a way that we can recover any lost packets, provided a certain minimum number of packets have been received.
Error correction has applications outside of multicast (and networking in general). Reed-Solomon erasure codes are what enable CDs to play even when scratched. ECC/FEC are used in bar codes to allow scanning of damaged codes. It is used in RAID modes like RAID6 to recover from the loss of a disk in an array. Erasure codes have been used with USENET posts to allow files to be recovered when posted in parts into a newsgroup and some of those parts get lost.
RaptorQ (RFC6330) is a method of implementing a class of systematic erasure codes called fountain codes.
The data we want to send is split into blocks, and then pre-encoded into a set of intermediate symbols. From these intermediate symbols we can generate both our original source symbols, and also additional repair symbols.
Provided the recipient receives at least a minimum value K' of these symbols (any unique combination of source and repair) the intermediate symbols can be reconstituted, and the original data recovered.
RaptorQ is what is called a systematic encoding, because the set of symbols we send includes our original data as plain text. Provided all source symbols are received, the original data has been transmitted with no decoding overhead. It is only in the case where we need to supplement the source symbols with repair symbols that we must perform the decoding process.
lcrq is the Librecast RaptorQ FEC library. lcrq provides a C library implementation of RFC6330.
It was coded from the ground up and has zero external dependencies.
It has been tested and runs on Linux (Gentoo, Ubuntu, Debian), FreeBSD and NetBSD. We compiled and ran it on one of the original Raspberry PI Zeros from 2011 (Running Rasbian 7) and all the lcrq and librecast tests pass. We even compiled it on a VAX, and an (emulated) IBM s390.
Support for LCRQ is integrated into the Librecast library. Enabling FEC with RaptorQ is a matter of turning on this encoding with lc_channel_coding_set(3)
.
"Sounds great! Where do I get me some FEC?"
You have two options:
First, download lcrq from Codeberg.
Then install it as per the instructions in the README.md.
`make test`
and `make speedtest`
provide a simple way to verify the code is working as expected.
There are man pages for all of the API, and example programs can be found in examples/
. `man 7 lcrq`
is a good place to start.
Alternatively, on Debian you can install the library from Debian Experimental with `apt install liblcrq`
. To get the development package use `apt install liblcrq-dev`
. lcrq is available for all supported Debian architectures (and quite a few others).
To use FEC with Librecast, download and install the latest liblibrecast release. FEC support was added in the 0.5.0 release.
To enable FEC for a Librecast Channel, turn on the RaptorQ encoding with:
lc_channel_coding_set(3)
Tests 0039 and 0040 exercise the RaptorQ encoding. The first test (0039) sends some data, simulating a dropped packet, and recovers this with FEC. Test 0040 does the same, but with symmetric encryption also enabled. Encodings can be combined in the Librecast API, and we will be adding support for several more, including compression and other encryption modes
There are a handful of existing libraries that provide RaptorQ support. We could have used one of those, so why develop our own?
FEC is fundamental to multicast in the same way SYN/ACK is to TCP. The two technologies combine beautifully. It is important that we have a strong understanding of and full control over this part of the stack, so we can ensure that we do things in a multicast way. FEC isn't just something you turn on and forget (although Librecast tries to make it that simple). In the same way that the more you know about data, the better you can compress it, the more you know about your data flows the more effective the use of FEC will be. Things like block sizes make a huge difference to encoding speed, and multicast's ability to split data flows over multiple channels let us do things that simply aren't possible in unicast.
This will be an ongoing area of research for the project. We are looking with interest at BATS codes (BATched Sparse codes - like RaptorQ, but batched encoding).