diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 366a043a5a..23b35abdd6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -7,6 +7,12 @@ variables:
     NETMAP_VERSION: "11.1"
     CONFIG: "--enable-all-elements --disable-verbose-batch --enable-simtime"
 
+default:
+    tags:
+        - sudo
+        - docker
+    image: ubuntu:20.04
+
 batch:
     script:
         - ./configure CXXFLAGS="-std=gnu++11" --enable-batch $CONFIG --disable-verbose-batch && make && make check
@@ -83,5 +89,5 @@ before_script:
     - gcc -v
     - if [ ! -e "netmap-$NETMAP_VERSION/sys/net/netmap.h" ] ; then wget https://github.com/luigirizzo/netmap/archive/v$NETMAP_VERSION.tar.gz && tar -xvf v$NETMAP_VERSION.tar.gz && ( cd netmap-$NETMAP_VERSION && cd LINUX && ./configure --no-drivers ; cd .. && cd .. ) ; fi
     - ls -al
-    - if [ `sudo -n whoami` = "root" ] ; then sudo insmod netmap-$NETMAP_VERSION/LINUX/netmap.ko && sudo chmod 666 /dev/netmap ; fi
+    - if [ `sudo -n whoami` = "root" ] && command -v insmod ; then sudo insmod netmap-$NETMAP_VERSION/LINUX/netmap.ko && sudo chmod 666 /dev/netmap ; fi
     - export CONFIG_NETMAP="--with-netmap=`pwd`/netmap-$NETMAP_VERSION/sys/"
diff --git a/README.md b/README.md
index a50de757d9..e273cb0eb2 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ Quick start (using DPDK for I/O)
  * Build FastClick, with support for DPDK using the following command:
 
 ```
-./configure --enable-dpdk --enable-intel-cpu --verbose --enable-select=poll CFLAGS="-O3" CXXFLAGS="-std=c++11 -O3"  --disable-dynamic-linking --enable-poll --enable-bound-port-transfer --enable-local --enable-flow --disable-task-stats --disable-cpu-load
+./configure CFLAGS="-O3" CXXFLAGS="-std=c++11 -O3" --enable-dpdk --enable-intel-cpu --disable-dynamic-linking --enable-bound-port-transfer --enable-flow --disable-task-stats --disable-cpu-load
 make
 ```
 
@@ -32,7 +32,7 @@ FastClick "Light"
 -----------------
 FastClick, like Click comes with a lot of features that you may not use. The following options will improve performance further :
 ```
-./configure --enable-dpdk --enable-intel-cpu --verbose --enable-select=poll CFLAGS="-O3" CXXFLAGS="-std=c++11 -O3"  --disable-dynamic-linking --enable-poll --enable-bound-port-transfer --enable-local --enable-flow --disable-task-stats --disable-cpu-load --enable-dpdk-packet --disable-clone --disable-dpdk-softqueue
+./configure  CFLAGS="-O3" CXXFLAGS="-std=c++11 -O3" --enable-dpdk --enable-intel-cpu --disable-dynamic-linking --enable-bound-port-transfer --enable-flow --disable-task-stats --disable-cpu-load --enable-dpdk-packet --disable-clone --disable-dpdk-softqueue
 make
 ```
  * Disable task stats suppress statistics tracking for advanced task scheduling with e.g. BalancedThreadSched. With DPDK, it's polling anyway... And as far as scheduling is concerned, [RSS++](#rss) has a better solution.
diff --git a/doc/click-elem2man b/doc/click-elem2man
index f62565b80f..9ad7366bc3 100755
--- a/doc/click-elem2man
+++ b/doc/click-elem2man
@@ -91,7 +91,7 @@ my(@section_headers) =
       'nat'                     => 'Network Address Translation',
       'tcp'                     => 'TCP',
       'udp'                     => 'UDP',
-      'gtp'                     => 'GTP',
+      'tunnel'                  => 'Tunnel (GRE, ...)',
       'app'                     => 'Applications',
       'traces'                  => 'Trace Manipulation',
       'ipmeasure'               => 'TCP/IP Measurement',
diff --git a/doc/clicktest.1 b/doc/clicktest.1
index 4d343b5c7b..3e7a576f7a 100644
--- a/doc/clicktest.1
+++ b/doc/clicktest.1
@@ -46,7 +46,7 @@
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .\"
-.\" If the F register is >0, we'll generate index entries on stderr for
+.\" If the F register is turned on, we'll generate index entries on stderr for
 .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
 .\" entries marked with X<> in POD.  Of course, you'll have to process the
 .\" output yourself in some meaningful fashion.
@@ -219,7 +219,8 @@ ClickTest test files consist of several sections, each introduced by a line
 starting with \fB%\fR. There must be, at least, a \fB\f(CB%script\fB\fR section.
 The \fB\f(CB%file\fB\fR and \fB\f(CB%expect\fB\fR sections define input and output files by
 name.
-.IP "\fB\f(CB%script\fB\fR" 8
+.ie n .IP "\fB\fB%script\fB\fR" 8
+.el .IP "\fB\f(CB%script\fB\fR" 8
 .IX Item "%script"
 The \fBsh\fR shell script that controls the test. ClickTest will run each
 command in sequence. Every command in the script must succeed, with
@@ -236,7 +237,8 @@ The script's environment is populated with any \fI\s-1VARIABLE\s0\fRs set on the
 clicktest command line with \fB\f(BI\s-1VARIABLE\s0\fB=\f(BI\s-1VALUE\s0\fB\fR syntax. Also, the
 \&\fB\f(CB$rundir\fB\fR environment variable is set to the directory in which
 clicktest was originally run.
-.IP "\fB\f(CB%require\fB [\-q]\fR" 8
+.ie n .IP "\fB\fB%require\fB [\-q]\fR" 8
+.el .IP "\fB\f(CB%require\fB [\-q]\fR" 8
 .IX Item "%require [-q]"
 An \fBsh\fR shell script defining prerequisites that must be satisfied
 before the test can run. Every command in the script must succeed, with
@@ -246,15 +248,18 @@ if a requirement fails.
 .Sp
 ClickTest runs the requirement script before creating any other test files.
 For example, contents of \fB\f(CB%file\fB\fR sections are not available.
-.IP "\fB\f(CB%info\fB\fR" 8
+.ie n .IP "\fB\fB%info\fB\fR" 8
+.el .IP "\fB\f(CB%info\fB\fR" 8
 .IX Item "%info"
 A short description of the test. In \fB\-\-superverbose\fR mode, the first
 paragraph of its contents is printed before the test results.
-.IP "\fB\f(CB%cut\fB\fR" 8
+.ie n .IP "\fB\fB%cut\fB\fR" 8
+.el .IP "\fB\f(CB%cut\fB\fR" 8
 .IX Item "%cut"
 This section is ignored. It is intended to comment out obsolete parts of
 the test.
-.IP "\fB\f(CB%file\fB [\-de] [+\f(BI\s-1LENGTH\s0\fB] \f(BI\s-1FILENAME\s0\fB...\fR" 8
+.ie n .IP "\fB\fB%file\fB [\-de] [+\f(BI\s-1LENGTH\s0\fB] \f(BI\s-1FILENAME\s0\fB...\fR" 8
+.el .IP "\fB\f(CB%file\fB [\-de] [+\f(BI\s-1LENGTH\s0\fB] \f(BI\s-1FILENAME\s0\fB...\fR" 8
 .IX Item "%file [-de] [+LENGTH] FILENAME..."
 Create an input file for the script. \fI\s-1FILENAME\s0\fR can be \fBstdin\fR,
 which sets the script's standard input. If \fB+\fR\fI\s-1LENGTH\s0\fR is provided,
@@ -266,7 +271,8 @@ Base64\-encoded (see \fBbase64\fR\|(1)); it is decoded before use. To
 include a file with lines that start with \fB%\fR (which would normally
 start a new section), use \fB\-d\fR and preface each line of the file with
 a space, or use \fB\-e\fR.
-.IP "\fB\f(CB%expect\fB [\-adeiw] [+\f(BI\s-1LENGTH\s0\fB] \f(BI\s-1FILENAME\s0\fB...\fR" 8
+.ie n .IP "\fB\fB%expect\fB [\-adeiw] [+\f(BI\s-1LENGTH\s0\fB] \f(BI\s-1FILENAME\s0\fB...\fR" 8
+.el .IP "\fB\f(CB%expect\fB [\-adeiw] [+\f(BI\s-1LENGTH\s0\fB] \f(BI\s-1FILENAME\s0\fB...\fR" 8
 .IX Item "%expect [-adeiw] [+LENGTH] FILENAME..."
 Define an expected output file. Differences between the script's
 output \fI\s-1FILENAME\s0\fR and the contents of the \fB\f(CB%expect\fB\fR section will
@@ -308,29 +314,35 @@ The \fB\-a\fR flag marks this expected output as an alternate. ClickTest will
 compare the script's output file with each provided alternate; the
 test succeeds if any of the alternates match. The \fB\-d\fR flag behaves
 as in \fB\f(CB%file\fB\fR.
-.IP "\fB\f(CB%expectv\fB [\-ade] [+\f(BI\s-1LENGTH\s0\fB] \f(BI\s-1FILENAME\s0\fB...\fR" 8
+.ie n .IP "\fB\fB%expectv\fB [\-ade] [+\f(BI\s-1LENGTH\s0\fB] \f(BI\s-1FILENAME\s0\fB...\fR" 8
+.el .IP "\fB\f(CB%expectv\fB [\-ade] [+\f(BI\s-1LENGTH\s0\fB] \f(BI\s-1FILENAME\s0\fB...\fR" 8
 .IX Item "%expectv [-ade] [+LENGTH] FILENAME..."
 Define a literal expected output file. This behaves like \fB\f(CB%expect\fB\fR,
 except that the script's output file must match the provided data
 \&\fIexactly\fR: \fB\f(CB%expectv\fB\fR never ignores whitespace differences, does not
 treat \f(CW\*(C`{{}}\*(C'\fR blocks as regular expressions, and does not parse
 \&\fB\f(CB%ignore\fB\fR patterns.
-.IP "\fB\f(CB%expectx\fB [\-adiw] [+\f(BI\s-1LENGTH\s0\fB] \f(BI\s-1FILENAME\s0\fB...\fR" 8
+.ie n .IP "\fB\fB%expectx\fB [\-adiw] [+\f(BI\s-1LENGTH\s0\fB] \f(BI\s-1FILENAME\s0\fB...\fR" 8
+.el .IP "\fB\f(CB%expectx\fB [\-adiw] [+\f(BI\s-1LENGTH\s0\fB] \f(BI\s-1FILENAME\s0\fB...\fR" 8
 .IX Item "%expectx [-adiw] [+LENGTH] FILENAME..."
 Define a regular-expression expected output file. This behaves like
 \&\fB\f(CB%expect\fB\fR, except that every line is treated as a regular expression.
 \&\f(CW\*(C`{{?comment}}\*(C'\fR blocks are ignored, but other brace pairs are treated
 according to the normal regular expression rules.
-.IP "\fB\f(CB%stdin\fB [\-de] [+\f(BI\s-1LENGTH\s0\fB]\fR" 8
+.ie n .IP "\fB\fB%stdin\fB [\-de] [+\f(BI\s-1LENGTH\s0\fB]\fR" 8
+.el .IP "\fB\f(CB%stdin\fB [\-de] [+\f(BI\s-1LENGTH\s0\fB]\fR" 8
 .IX Item "%stdin [-de] [+LENGTH]"
 Same as \fB\f(CB%file\fB stdin\fR.
-.IP "\fB\f(CB%stdout\fB [\-adeiw] [+\f(BI\s-1LENGTH\s0\fB]\fR" 8
+.ie n .IP "\fB\fB%stdout\fB [\-adeiw] [+\f(BI\s-1LENGTH\s0\fB]\fR" 8
+.el .IP "\fB\f(CB%stdout\fB [\-adeiw] [+\f(BI\s-1LENGTH\s0\fB]\fR" 8
 .IX Item "%stdout [-adeiw] [+LENGTH]"
 Same as \fB\f(CB%expect\fB stdout\fR.
-.IP "\fB\f(CB%stderr\fB [\-adeiw] [+\f(BI\s-1LENGTH\s0\fB]\fR" 8
+.ie n .IP "\fB\fB%stderr\fB [\-adeiw] [+\f(BI\s-1LENGTH\s0\fB]\fR" 8
+.el .IP "\fB\f(CB%stderr\fB [\-adeiw] [+\f(BI\s-1LENGTH\s0\fB]\fR" 8
 .IX Item "%stderr [-adeiw] [+LENGTH]"
 Same as \fB\f(CB%expect\fB stderr\fR.
-.IP "\fB\f(CB%ignorex\fB [\-di] [+\f(BI\s-1LENGTH\s0\fB] [\f(BI\s-1FILENAME\s0\fB]\fR" 8
+.ie n .IP "\fB\fB%ignorex\fB [\-di] [+\f(BI\s-1LENGTH\s0\fB] [\f(BI\s-1FILENAME\s0\fB]\fR" 8
+.el .IP "\fB\f(CB%ignorex\fB [\-di] [+\f(BI\s-1LENGTH\s0\fB] [\f(BI\s-1FILENAME\s0\fB]\fR" 8
 .IX Item "%ignorex [-di] [+LENGTH] [FILENAME]"
 Each line in the \fB\f(CB%ignorex\fB\fR section is a Perl regular expression. Lines in
 the supplied \fI\s-1FILENAME\s0\fR that match any of those regular expressions will not
@@ -338,18 +350,22 @@ be considered when comparing files with \fB\f(CB%expect\fB\fR data. The regular
 expression must match the whole line. \fI\s-1FILENAME\s0\fR may be \fBall\fR, in which case
 the regular expressions will apply to all \fB\f(CB%expect\fB\fR files. \f(CW\*(C`{{?comment}}\*(C'\fR
 blocks are ignored.
-.IP "\fB\f(CB%ignore\fB\fR, \fB\f(CB%ignorev\fB [\-adeiw] [+\f(BI\s-1LENGTH\s0\fB] [\f(BI\s-1FILENAME\s0\fB]\fR" 8
+.ie n .IP "\fB\fB%ignore\fB\fR, \fB\fB%ignorev\fB [\-adeiw] [+\f(BI\s-1LENGTH\s0\fB] [\f(BI\s-1FILENAME\s0\fB]\fR" 8
+.el .IP "\fB\f(CB%ignore\fB\fR, \fB\f(CB%ignorev\fB [\-adeiw] [+\f(BI\s-1LENGTH\s0\fB] [\f(BI\s-1FILENAME\s0\fB]\fR" 8
 .IX Item "%ignore, %ignorev [-adeiw] [+LENGTH] [FILENAME]"
 Like \fB\f(CB%ignorex\fB\fR, but \fB\f(CB%ignore\fB\fR parses regular expressions only inside
 double braces (\f(CW\*(C`{{ }}\*(C'\fR), and \fB\f(CB%ignorev\fB\fR lines must match exactly.
-.IP "\fB\f(CB%include\fB \f(BI\s-1FILENAME\s0\fB\fR" 8
+.ie n .IP "\fB\fB%include\fB \f(BI\s-1FILENAME\s0\fB\fR" 8
+.el .IP "\fB\f(CB%include\fB \f(BI\s-1FILENAME\s0\fB\fR" 8
 .IX Item "%include FILENAME"
 Interpolate the contents of another clicktest file.
-.IP "\fB\f(CB%eot\fB\fR" 8
+.ie n .IP "\fB\fB%eot\fB\fR" 8
+.el .IP "\fB\f(CB%eot\fB\fR" 8
 .IX Item "%eot"
 Marks the end of the current test. The rest of the file will be parsed for
 additional tests.
-.IP "\fB\f(CB%eof\fB\fR" 8
+.ie n .IP "\fB\fB%eof\fB\fR" 8
+.el .IP "\fB\f(CB%eof\fB\fR" 8
 .IX Item "%eof"
 The rest of the file is ignored.
 .SH "EXAMPLE"
diff --git a/elements/ip/checkipheader.cc b/elements/ip/checkipheader.cc
index 07048acad5..8ad3e80f55 100644
--- a/elements/ip/checkipheader.cc
+++ b/elements/ip/checkipheader.cc
@@ -113,7 +113,7 @@ CheckIPHeader::configure(Vector<String> &conf, ErrorHandler *errh)
 
     if (Args(conf, this, errh)
         .read("BADSRC", OldBadSrcArg(), _bad_src)
-        .read("OFFSET", _offset)
+        .read_or_set("OFFSET", _offset, 0)
         .complete() < 0)
         return -1;
 
diff --git a/elements/ip6/checkip6header.cc b/elements/ip6/checkip6header.cc
index 80cc9356fb..f30fa09dd3 100644
--- a/elements/ip6/checkip6header.cc
+++ b/elements/ip6/checkip6header.cc
@@ -178,7 +178,7 @@ CheckIP6Header::read_handler(Element *e, void *thunk)
     switch (reinterpret_cast<uintptr_t>(thunk)) {
         case h_count: {
             PER_THREAD_SUM(uint64_t, count, c->_count);
-	    return String(count);
+            return String(count);
         }
         case h_drops: {
             return String(c->_drops);
diff --git a/elements/ip6/ip6drop.cc b/elements/ip6/ip6drop.cc
new file mode 100644
index 0000000000..cc24dc4871
--- /dev/null
+++ b/elements/ip6/ip6drop.cc
@@ -0,0 +1,173 @@
+/*
+ * ip6drop.{cc,hh} -- element drops packets following a Gilbert Eliott model
+ * Louis Navarre
+ * 
+ * Copyright (c) 2024 UCLouvain
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Click LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Click LICENSE file; the license in that file is
+ * legally binding.
+ */
+
+#include <click/config.h>
+#include "ip6drop.hh"
+#include <click/nameinfo.hh>
+#include <click/confparse.hh>
+#include <click/error.hh>
+#include <click/glue.hh>
+
+CLICK_DECLS
+
+IP6Drop::IP6Drop()
+{
+    _use_dst_anno = false;
+}
+
+IP6Drop::~IP6Drop()
+{
+}
+
+int
+IP6Drop::configure(Vector<String> &conf, ErrorHandler *errh)
+{
+    if (Args(conf, this, errh)
+	.read_all("ADDR", addrs)
+    .read_or_set("P", p, 0)
+    .read_or_set("R", r, 0)
+    .read_or_set("H", h, 0)
+    .read_or_set("K", k, 1)
+    .read_or_set("SEED", seed, 51)
+	.complete() < 0)
+        return -1;
+
+    total_seen = 0;
+    srand(seed);
+    state = good;
+
+    return 0;
+}
+
+void
+IP6Drop::push(int input, Packet *p_in)
+{
+    drop_model(p_in, [this](Packet*p){output(0).push(p);});
+}
+
+#if HAVE_BATCH
+void
+IP6Drop::push_batch(int input, PacketBatch *batch) {
+    EXECUTE_FOR_EACH_PACKET_DROPPABLE(drop_model, batch, [](Packet *){});
+    if (batch) {
+        output_push_batch(0, batch);
+    }
+}
+#endif
+
+#if HAVE_BATCH
+Packet *
+#else
+void
+#endif
+IP6Drop::drop_model(Packet *p_in)
+{
+    return drop_model(p_in, [this](Packet*p){output(0).push(p);});
+}
+
+#if HAVE_BATCH
+Packet *
+#else
+void
+#endif
+IP6Drop::drop_model(Packet *p_in, std::function<void(Packet*)>push)
+{
+    const click_ip6 *ip6 = reinterpret_cast<const click_ip6 *>(p_in->data());
+    uint32_t *dst_32 = (uint32_t *)&ip6->ip6_dst;
+    bool found = false;
+    for (int i = 0; i < addrs.size(); ++i) {
+        IP6Address addr = addrs.at(i);
+        uint32_t *addr_32 = (uint32_t *)addr.data32();
+        if (addr_eq(addr_32, dst_32)) {
+            found = true;
+            break;
+        }
+    }
+    if (!found) {
+#if HAVE_BATCH
+        return p_in;
+#else
+        push(p_in);
+#endif
+    }
+    
+    total_seen++;
+    // Do not drop the first packets to ensure a connection between the client and the broker
+    if (total_seen < 20) {
+        return p_in;
+    }
+
+    if (!gemodel()) {
+        click_chatter("Drop packet #%u", total_seen);
+#if HAVE_BATCH
+        return 0;
+    }
+    return p_in;
+#else
+    }
+    push(p_in);
+#endif
+}
+
+bool
+IP6Drop::gemodel()
+{
+    bool keep_packet = true;
+    bool change_state = false;
+    // click_chatter("State is %u", state);
+    if (state == good) {
+        double rand_val1 = rand() / (RAND_MAX + 1.);
+        // click_chatter("Generated value: %f", rand_val1);
+        keep_packet = rand_val1 < k;
+        rand_val1 = rand() / (RAND_MAX + 1.);
+        change_state = rand_val1 < p;
+        // click_chatter("K and keep packet: %f, %u change state=%u (p=%u)", k * 100, keep_packet, change_state, p * 100);
+        if (change_state) {
+            state = bad;
+        }
+    } else {
+        double rand_val1 = rand() / (RAND_MAX + 1.);
+        keep_packet = rand_val1 < h;
+        rand_val1 = rand() / (RAND_MAX + 1.);
+        change_state = rand_val1 < r;
+        if (change_state) {
+            state = good;
+        }
+    }
+    return keep_packet;
+}
+
+bool
+IP6Drop::addr_eq(uint32_t *a1, uint32_t *a2)
+{
+    return (a1[0] == a2[0] && a1[1] == a2[1] && a1[2] == a2[2] && a1[3] == a2[3]);
+}
+
+String
+IP6Drop::read_handler(Element *e, void *thunk)
+{
+    return "<error>";
+}
+
+void
+IP6Drop::add_handlers()
+{
+}
+
+CLICK_ENDDECLS
+EXPORT_ELEMENT(IP6Drop)
+ELEMENT_MT_SAFE(IP6Drop)
diff --git a/elements/ip6/ip6drop.hh b/elements/ip6/ip6drop.hh
new file mode 100644
index 0000000000..4a43d22e62
--- /dev/null
+++ b/elements/ip6/ip6drop.hh
@@ -0,0 +1,77 @@
+#ifndef CLICK_IP6_DROP_HH
+#define CLICK_IP6_DROP_HH
+#include <click/batchelement.hh>
+#include <click/element.hh>
+#include <click/glue.hh>
+#include <click/atomic.hh>
+#include <clicknet/ip6.h>
+#include <click/ip6address.hh>
+
+CLICK_DECLS
+
+/*
+=c
+
+IP6Drop(ADDR[, ADDR, ...], P, R, K, H)
+
+=s ip
+
+Gilbert-Elliott drop model
+
+=d
+
+=e
+
+
+  IP6SRv6FECDecode(fc00::a, fc00::9)
+
+=a IP6Encap */
+
+enum state_e {
+  good,
+  bad
+};
+
+class IP6Drop : public BatchElement { 
+ 
+ public:
+
+  IP6Drop();
+  ~IP6Drop();
+
+  const char *class_name() const override        { return "IP6Drop"; }
+  const char *port_count() const override        { return PORTS_1_1; }
+
+  int  configure(Vector<String> &, ErrorHandler *) CLICK_COLD;
+  bool can_live_reconfigure() const     { return true; }
+  void add_handlers() CLICK_COLD;
+  String read_handler(Element *e, void *thunk) CLICK_COLD;
+
+  void push(int, Packet *p_in) override;
+#if HAVE_BATCH
+  void push_batch(int, PacketBatch * batch_in) override;
+  Packet *drop_model(Packet *p_in);
+  Packet *drop_model(Packet *p_in, std::function<void(Packet*)>push);
+#else
+  void drop_model(Packet *p_in, std::function<void(Packet*)>push);
+#endif
+  bool gemodel() CLICK_COLD;
+  bool addr_eq(uint32_t *a1, uint32_t *a2) CLICK_COLD;
+
+ private:
+  Vector<IP6Address> addrs;
+  uint16_t total_seen; // Number of analyzed packets
+  double p; // Good -> bad
+  double r; // Bad -> good
+  double h; // Don't drop in bad
+  double k; // Don't drop in good
+  state_e state; // State of the machine
+  uint16_t seed;
+  bool is_in_good;
+  bool _use_dst_anno;
+};
+
+CLICK_ENDDECLS
+#endif
+
+
diff --git a/elements/ip6/ip6srdecap.cc b/elements/ip6/ip6srdecap.cc
new file mode 100644
index 0000000000..afd7354e27
--- /dev/null
+++ b/elements/ip6/ip6srdecap.cc
@@ -0,0 +1,100 @@
+/*
+ * ip6srdecap.{cc,hh} -- element encapsulates packet in IP6 SRv6 header
+ * Tom Barbette, Louis Navarre
+ *
+ * Copyright (c) 2024 UCLouvain
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Click LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Click LICENSE file; the license in that file is
+ * legally binding.
+ */
+
+#include <click/config.h>
+#include "ip6srdecap.hh"
+#include <click/nameinfo.hh>
+#include <click/confparse.hh>
+#include <click/error.hh>
+#include <click/glue.hh>
+CLICK_DECLS
+
+IP6SRDecap::IP6SRDecap() : _force(false)
+{
+
+}
+
+IP6SRDecap::~IP6SRDecap()
+{
+}
+
+int
+IP6SRDecap::configure(Vector<String> &conf, ErrorHandler *errh)
+{
+ 
+    if (Args(conf, this, errh)
+        .read("FORCE_DECAP", _force)
+	    .complete() < 0)
+        return -1;
+
+    
+    return 0;
+}
+
+
+Packet *
+IP6SRDecap::simple_action(Packet *p_in)
+{
+    
+    WritablePacket *p = p_in->uniqueify();
+    if (!p)
+        return 0;
+
+    click_ip6 *ip6 = reinterpret_cast<click_ip6 *>(p->data());
+    click_ip6_sr *sr = (click_ip6_sr*)ip6_find_header(ip6, IP6_EH_ROUTING, p->end_data());
+
+    if (sr == 0)
+        return p;
+    
+    if (_force || sr->segment_left == 1) {
+
+            IP6Address last = IP6Address(sr->segments[0]);
+
+            unsigned char* old_data = p->data();
+            unsigned char nxt = sr->ip6_sr_next;
+            unsigned char *next_ptr = (unsigned char*)ip6_find_header(ip6, nxt, p->end_data());
+            unsigned offset = p->transport_header_offset();
+            if (next_ptr == 0) {
+                p->kill();
+                click_chatter("Cannot find next header %d. Buggy packet?", nxt);
+                return 0;
+            }
+            unsigned srlen = (unsigned char*)next_ptr - (unsigned char*)sr;
+            p->pull(srlen);
+
+            memmove(p->data(), old_data, (unsigned char*)sr-old_data);
+            ip6 = (click_ip6 *)(p->data());
+
+            ip6->ip6_dst = last;
+            ip6->ip6_nxt = nxt;
+            ip6->ip6_plen = htons(ntohs(ip6->ip6_plen));
+            p->set_network_header(p->data(), offset);
+
+
+    } else if (unlikely(sr->segment_left == 0)) {
+        click_chatter("Invalid packet with 0 segments left?");
+        return p;
+    } else {
+        ip6->ip6_dst = sr->segments[--sr->segment_left];
+    }
+
+    return p;
+}
+
+CLICK_ENDDECLS
+EXPORT_ELEMENT(IP6SRDecap)
+ELEMENT_MT_SAFE(IP6SRDecap)
diff --git a/elements/ip6/ip6srdecap.hh b/elements/ip6/ip6srdecap.hh
new file mode 100644
index 0000000000..5feb64f40e
--- /dev/null
+++ b/elements/ip6/ip6srdecap.hh
@@ -0,0 +1,50 @@
+#ifndef CLICK_IP6SRDECAP_HH
+#define CLICK_IP6SRDECAP_HH
+#include <click/batchelement.hh>
+#include <click/glue.hh>
+#include <click/atomic.hh>
+#include <clicknet/ip6.h>
+#include <click/ip6address.hh>
+
+CLICK_DECLS
+
+/*
+=c
+
+IP6SRDecap(ADDR[, ADDR, ...])
+
+=s ip
+
+adds a SR Header to the IP6 packet
+
+=d
+
+Takes a list of adresses
+
+=e
+
+
+  IP6SRDecap(2000:10:1::2, 2000:20:1::3, ...)
+
+=a IP6Encap */
+
+class IP6SRDecap : public SimpleElement<IP6SRDecap> { public:
+
+  IP6SRDecap();
+  ~IP6SRDecap();
+
+  const char *class_name() const override        { return "IP6SRDecap"; }
+  const char *port_count() const override        { return PORTS_1_1; }
+
+  int  configure(Vector<String> &, ErrorHandler *) CLICK_COLD;
+  bool can_live_reconfigure() const     { return true; }
+
+  Packet *simple_action(Packet *);
+
+private:
+  bool _force;
+
+};
+
+CLICK_ENDDECLS
+#endif
diff --git a/elements/ip6/ip6srencap.cc b/elements/ip6/ip6srencap.cc
new file mode 100644
index 0000000000..41a87dd12d
--- /dev/null
+++ b/elements/ip6/ip6srencap.cc
@@ -0,0 +1,95 @@
+/*
+ * ip6srencap.{cc,hh} -- element encapsulates packet in IP6 SRv6 header
+ * Tom Barbette, Louis Navarre
+ *
+ * Copyright (c) 2024 UCLouvain
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Click LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Click LICENSE file; the license in that file is
+ * legally binding.
+ */
+
+#include <click/config.h>
+#include "ip6srencap.hh"
+#include <click/nameinfo.hh>
+#include <click/confparse.hh>
+#include <click/error.hh>
+#include <click/glue.hh
+
+CLICK_DECLS
+
+IP6SREncap::IP6SREncap() : _do_encap_dst(true)
+{
+
+}
+
+IP6SREncap::~IP6SREncap()
+{
+}
+
+int
+IP6SREncap::configure(Vector<String> &conf, ErrorHandler *errh)
+{
+    Vector<IP6Address> addr;
+
+    if (Args(conf, this, errh)
+	.read_all("ADDR", addr)
+    .read("ENCAP_DST", _do_encap_dst)
+	.complete() < 0)
+        return -1;
+    if (_do_encap_dst) {
+        addr.push_front(IP6Address());
+    }
+
+    static_assert(sizeof(click_ip6_sr) == 8);
+
+    _sr_len = sizeof(click_ip6_sr) + addr.size() * sizeof(IP6Address);
+    _sr = (click_ip6_sr*)CLICK_LALLOC(_sr_len);
+    _sr->ip6_hdrlen = sizeof(IP6Address) * addr.size() / 8;
+    _sr->type = 4;
+	_sr->segment_left = addr.size() - 1;
+	_sr->last_entry = addr.size() - 1;
+	_sr->flags = 0;
+    _sr->tag = 0;
+    memcpy(_sr->segments, addr.data(), sizeof(IP6Address) * addr.size());
+
+    return 0;
+}
+
+
+Packet *
+IP6SREncap::simple_action(Packet *p_in)
+{
+    
+    WritablePacket *p = p_in->push(_sr_len);
+    if (!p)
+        return 0;
+
+    click_ip6 *ip6 = reinterpret_cast<click_ip6 *>(p->data());
+    click_ip6_sr *sr = reinterpret_cast<click_ip6_sr *>(p->data() + sizeof(click_ip6));
+    memcpy(ip6, p->data() + _sr_len, sizeof(click_ip6));
+    memcpy(sr, _sr, _sr_len);
+    sr->ip6_sr_next =  ip6->ip6_nxt;
+    ip6->ip6_nxt = IP6_EH_ROUTING;
+
+    if (_do_encap_dst) {
+        sr->segments[0] = ip6->ip6_dst;
+    }
+
+    ip6->ip6_dst = sr->segments[sr->segment_left];
+    // Also update the IPv6 Header to add the SRH length in the payload
+    ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) + _sr_len);
+    p->set_network_header(p->data(), p->network_header_length() + _sr_len);
+
+    return p;
+}
+
+CLICK_ENDDECLS
+EXPORT_ELEMENT(IP6SREncap)
+ELEMENT_MT_SAFE(IP6SREncap)
diff --git a/elements/ip6/ip6srencap.hh b/elements/ip6/ip6srencap.hh
new file mode 100644
index 0000000000..359e2e666c
--- /dev/null
+++ b/elements/ip6/ip6srencap.hh
@@ -0,0 +1,50 @@
+#ifndef CLICK_IP6SRENCAP_HH
+#define CLICK_IP6SRENCAP_HH
+#include <click/batchelement.hh>
+#include <click/glue.hh>
+#include <click/atomic.hh>
+#include <clicknet/ip6.h>
+#include <click/ip6address.hh>
+
+CLICK_DECLS
+
+/*
+=c
+
+IP6SREncap(ADDR[, ADDR, ...])
+
+=s ip
+
+adds a SR Header to the IP6 packet
+
+=d
+
+Takes a list of adresses
+
+=e
+
+
+  IP6SREncap(2000:10:1::2, 2000:20:1::3, ...)
+
+=a IP6Encap */
+
+class IP6SREncap : public SimpleElement<IP6SREncap> { public:
+
+  IP6SREncap();
+  ~IP6SREncap();
+
+  const char *class_name() const override        { return "IP6SREncap"; }
+  const char *port_count() const override        { return PORTS_1_1; }
+
+  int  configure(Vector<String> &, ErrorHandler *) CLICK_COLD;
+  bool can_live_reconfigure() const     { return true; }
+
+  Packet *simple_action(Packet *);
+
+  int _sr_len;
+  click_ip6_sr* _sr;
+  bool _do_encap_dst;
+};
+
+CLICK_ENDDECLS
+#endif
diff --git a/elements/ip6/ip6srprocess.cc b/elements/ip6/ip6srprocess.cc
new file mode 100644
index 0000000000..6cb2fe4f14
--- /dev/null
+++ b/elements/ip6/ip6srprocess.cc
@@ -0,0 +1,67 @@
+/*
+ * IP6SRProcess.{cc,hh} -- element encapsulates packet in IP6 SRv6 header
+ * Tom Barbette, Louis Navarre
+ *
+ * Copyright (c) 2024 UCLouvain
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Click LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Click LICENSE file; the license in that file is
+ * legally binding.
+ */
+
+#include <click/config.h>
+#include "ip6srprocess.hh"
+#include <click/nameinfo.hh>
+#include <click/confparse.hh>
+#include <click/error.hh>
+#include <click/glue.hh>
+
+CLICK_DECLS
+
+IP6SRProcess::IP6SRProcess()
+{
+
+}
+
+IP6SRProcess::~IP6SRProcess()
+{
+}
+
+int
+IP6SRProcess::configure(Vector<String> &conf, ErrorHandler *errh)
+{
+    return 0;
+}
+
+
+Packet *
+IP6SRProcess::simple_action(Packet *p_in)
+{
+    
+    WritablePacket *p = (WritablePacket *)p_in;
+
+    click_ip6 *ip6 = reinterpret_cast<click_ip6 *>(p->data());
+    click_ip6_sr *sr = reinterpret_cast<click_ip6_sr *>(p->data() + sizeof(click_ip6));
+    
+    // Update segment left of the SRH
+    --sr->segment_left;
+
+    // Update IPv6 address according to the Segment Routing Header
+    ip6->ip6_dst = sr->segments[sr->segment_left];
+
+    // TODO: recompute the checksum with the new pseudo-header (the destination address has changed)
+
+    // TODO: if segment left is 0, pass it to the upper layer or drop it (do it in the .click files)
+
+    return p;
+}
+
+CLICK_ENDDECLS
+EXPORT_ELEMENT(IP6SRProcess)
+ELEMENT_MT_SAFE(IP6SRProcess)
diff --git a/elements/ip6/ip6srprocess.hh b/elements/ip6/ip6srprocess.hh
new file mode 100644
index 0000000000..1960780caa
--- /dev/null
+++ b/elements/ip6/ip6srprocess.hh
@@ -0,0 +1,47 @@
+#ifndef CLICK_IP6SRPROCESS_HH
+#define CLICK_IP6SRPROCESS_HH
+#include <click/batchelement.hh>
+#include <click/glue.hh>
+#include <click/atomic.hh>
+#include <clicknet/ip6.h>
+#include <click/ip6address.hh>
+
+CLICK_DECLS
+
+/*
+=c
+
+IP6SRProcess(ADDR[, ADDR, ...])
+
+=s ip
+
+Processes the Segment Routing Header of the IPv6 packet. For now it simply set the IP address
+to the next one in the list.
+
+=d
+
+Takes a list of adresses: SIDs triggering SRv6 functions. 
+
+=e
+
+
+  IP6SRProcess(2000:10:1::2, 2000:20:1::3, ...)
+
+=a IP6SRProcess */
+
+class IP6SRProcess : public SimpleElement<IP6SRProcess> { public:
+
+  IP6SRProcess();
+  ~IP6SRProcess();
+
+  const char *class_name() const override        { return "IP6SRProcess"; }
+  const char *port_count() const override        { return PORTS_1_1; }
+
+  int  configure(Vector<String> &, ErrorHandler *) CLICK_COLD;
+  bool can_live_reconfigure() const     { return true; }
+
+  Packet *simple_action(Packet *);
+};
+
+CLICK_ENDDECLS
+#endif
diff --git a/elements/ip6/ip6srv6fec.cc b/elements/ip6/ip6srv6fec.cc
new file mode 100644
index 0000000000..fcf0a029a1
--- /dev/null
+++ b/elements/ip6/ip6srv6fec.cc
@@ -0,0 +1,514 @@
+/*
+ * ip6encap.{cc,hh} -- element encapsulates packet in IP6 header
+ * Louis Navarre
+ * Tom Barbette
+ *
+ * Copyright (c) 2021 IP Networking Lab, UCLouvain
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Click LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Click LICENSE file; the license in that file is
+ * legally binding.
+ */
+
+#include <click/config.h>
+#include "ip6srv6fec.hh"
+#include <click/nameinfo.hh>
+#include <click/confparse.hh>
+#include <click/error.hh>
+#include <click/glue.hh>
+#define MAX(a, b) ((a > b) ? a : b)
+#define MIN(a, b) ((a < b) ? a : b)
+CLICK_DECLS
+
+uint16_t inline my_min(uint16_t a, uint16_t b) {
+    return ((a < b) ? a : b);
+}
+
+uint16_t inline my_max(uint16_t a, uint16_t b) {
+    return ((a > b) ? a : b);
+}
+
+IP6SRv6FECEncode::IP6SRv6FECEncode()
+{
+    _use_dst_anno = false;
+    memset(&_rlc_info, 0, sizeof(rlc_info_t));
+    rlc_fill_muls(_rlc_info.muls);
+    _rlc_info.prng = rlc_reset_coefs();
+}
+
+IP6SRv6FECEncode::~IP6SRv6FECEncode()
+{
+    static_assert(sizeof(source_tlv_t) == 8, "source_tlv_t should be 8 bytes");
+
+    for (int i = 0; i < SRV6_FEC_BUFFER_SIZE; ++i) {
+        Packet *packet = _rlc_info.source_buffer[i];
+        if (packet) {
+            packet->kill();
+        }  
+    }
+}
+
+int
+IP6SRv6FECEncode::configure(Vector<String> &conf, ErrorHandler *errh)
+{
+    if (Args(conf, this, errh)
+	.read_mp("ENC", enc)
+	.read_mp("DEC", dec)
+        .read_or_set("WINDOW", _rlc_info.window_size, 4)
+        .read_or_set("STEP", _rlc_info.window_step, 2)
+        .read_or_set("SCHEME", _fec_scheme, SRV6_FEC_RLC)
+        .read_or_set("REPAIR", _send_repair, true)
+	.complete() < 0)
+        return -1;
+    fed = IP6Address("fc00::b");
+
+    // Preset the IPv6 Header
+    memset(&_repair_ip6, 0, sizeof(click_ip6));
+    memcpy(&_repair_ip6.ip6_src, enc.data(), sizeof(IP6Address));
+    memcpy(&_repair_ip6.ip6_dst, dec.data(), sizeof(IP6Address));
+    _repair_ip6.ip6_flow = htonl(6 << IP6_V_SHIFT);
+    _repair_ip6.ip6_plen = 0; // Will be completed by the repair FEC Scheme
+    _repair_ip6.ip6_nxt = IPPROTO_ROUTING;
+    _repair_ip6.ip6_hlim = 53;
+
+    // Preset the SRv6 Header
+    memset(&_repair_srv6, 0, sizeof(click_ip6_sr));
+    _repair_srv6.type = IP6PROTO_SEGMENT_ROUTING;
+    _repair_srv6.segment_left = 1;
+    _repair_srv6.last_entry = 1;
+    _repair_srv6.flags = 0;
+    _repair_srv6.tag = 0;
+    _repair_srv6.ip6_sr_next = 253;
+    _repair_srv6.ip6_hdrlen = (sizeof(repair_tlv_t) + 2 * sizeof(IP6Address)) / 8;
+
+    return 0;
+}
+
+void
+IP6SRv6FECEncode::push(int input, Packet *p_in)
+{
+    if (input == SRV6_FEC_FEEDBACK_INPUT) {
+	feedback_message(p_in, [this](Packet*p){output(0).push(p);});
+    } else {
+        fec_framework(p_in, [this](Packet*p){output(0).push(p);});
+    }
+    //fec_framework(p_in, [this](Packet*p){output(0).push(p);});
+}
+
+#if HAVE_BATCH
+void 
+IP6SRv6FECEncode::push_batch(int input, PacketBatch *batch) {
+    if (input == SRV6_FEC_FEEDBACK_INPUT) {
+        EXECUTE_FOR_EACH_PACKET_ADD(feedback_message, batch);
+    } else {
+        EXECUTE_FOR_EACH_PACKET_ADD( fec_framework, batch );
+        if (batch)
+            output_push_batch(0, batch);
+    }
+    // EXECUTE_FOR_EACH_PACKET_ADD( fec_framework, batch );
+    // if (batch)
+    //     output_push_batch(0, batch);
+}
+#endif
+
+String
+IP6SRv6FECEncode::read_handler(Element *e, void *thunk)
+{
+    return "<error>";
+}
+
+void
+IP6SRv6FECEncode::add_handlers()
+{
+    add_read_handler("src", read_handler, 0, Handler::CALM);
+    add_write_handler("src", reconfigure_keyword_handler, "1 SRC");
+    add_read_handler("dst", read_handler, 1, Handler::CALM);
+    add_write_handler("dst", reconfigure_keyword_handler, "2 DST");
+}
+
+inline void
+IP6SRv6FECEncode::fec_framework(Packet *p_in, std::function<void(Packet*)>push)
+{
+    // Timestamp t_framework_s = Timestamp::now();
+    int err;
+
+    // Check the SID to determine if it is a source symbol or a feedback packet
+    // const click_ip6 *ip6 = reinterpret_cast<const click_ip6 *>(p_in->data());
+    // uint32_t *ip_32 = (uint32_t *)&ip6->ip6_dst.s6_addr;
+    // uint32_t *fb_32 = (uint32_t *)fed.data32();
+    // // We know that the packet belongs either to the FEC Framework or the Feedback process
+    // // The difference between the two SIDs must be located in the last 32 bits of the IPv6 Address
+    // // to make the following work
+    // if (ip_32[3] == fb_32[3]) {
+    //     // Feedback message
+        
+        
+    //     // Do not forward a feedback message
+    //     // click_chatter("Feedback message");
+    //     return;
+    // }
+    // This is a source symbol
+
+    // Call FEC Scheme
+    err = IP6SRv6FECEncode::fec_scheme(p_in, push);
+    if (err < 0) { 
+        return; 
+    }
+
+    if (err == 1 && _send_repair) { // Repair
+        if (!_repair_packet) {
+            return;
+        }
+
+
+	if (!_send_repair) {
+		_repair_packet->kill();
+		return;
+	}
+	
+        encapsulate_repair_payload(_repair_packet, &_repair_tlv, _rlc_info.max_length);
+
+        // Send repair packet
+        push(_repair_packet);
+        _repair_packet = 0;
+
+        // Reset parameters of the RLC information
+        _rlc_info.max_length = 0;
+        memset(&_repair_tlv, 0, sizeof(repair_tlv_t));
+        _rlc_info.prng = rlc_reset_coefs();
+    }
+}
+
+int
+IP6SRv6FECEncode::fec_scheme(Packet *p_in, std::function<void(Packet*)>push)
+{
+    // Complete the source TLV
+    _source_tlv.type = TLV_TYPE_FEC_SOURCE;
+    _source_tlv.len = sizeof(source_tlv_t) - 2; // Ignore type and len
+    _source_tlv.padding = 0;
+    _source_tlv.sfpid = _rlc_info.encoding_symbol_id;
+
+    // According to RFC8681, we should add the TLV in the FEC Framework and not the FEC Scheme
+    // but we do it here to improve the performance (we avoid to make a different copy of the same packet)
+    WritablePacket *p = srv6_fec_add_source_tlv(p_in, &_source_tlv);
+    if (!p) {
+        return -1; //Memory problem, packet is already destroyed
+    }
+
+    // Store packet as source symbol
+    store_source_symbol(p, _rlc_info.encoding_symbol_id);
+
+    // Update RLC information
+    ++_rlc_info.buffer_size;
+    ++_rlc_info.encoding_symbol_id;
+    
+    push(p);
+
+    // Same as the srv6_fec_add_source_tlv
+    push(p);
+
+    // Generate a repair symbol if full window
+    if (_rlc_info.buffer_size >= _rlc_info.window_size) {
+        // Compute maximum payload length (TODO: improve)
+        uint32_t start_esid = _source_tlv.sfpid - _rlc_info.window_size + 1;
+        for (int i = 0; i < _rlc_info.window_size; ++i) {
+            uint8_t idx = (start_esid + i) % SRV6_FEC_BUFFER_SIZE;
+            _rlc_info.max_length = MAX(_rlc_info.max_length, _rlc_info.source_buffer[idx]->length());
+        }
+        // Create new repair packet with correct size
+        _repair_packet = Packet::make(_rlc_info.max_length + sizeof(click_ip6) + sizeof(click_ip6_sr) + sizeof(repair_tlv_t) + 2 * sizeof(IP6Address));
+        if (!_repair_packet) {
+            // click_chatter("Cannot get repair packet TODO");
+            return -1;
+        }
+
+        memset(_repair_packet->data(), 0, _repair_packet->length());
+
+        // Encode the source symbols in repair
+        //t_s = Timestamp::now();
+        if (_fec_scheme == SRV6_FEC_RLC) {
+            rlc_encode_symbols(_source_tlv.sfpid);
+        } else {
+            xor_encode_symbols(_source_tlv.sfpid);
+        }
+        //t_e = Timestamp::now();
+        // click_chatter("Encode symbols: %u", t_e.usec() - t_s.usec());
+
+        // Complete the Repair FEC Payload ID
+        _repair_tlv.type = TLV_TYPE_FEC_REPAIR;
+        _repair_tlv.len = sizeof(repair_tlv_t) - 2;
+        _repair_tlv.padding = _fec_scheme;
+        _repair_tlv.rfpid = _source_tlv.sfpid;
+        _repair_tlv.rfi = ((_rlc_info.window_step << 24) + (_rlc_info.window_step << 16)) + _rlc_info.repair_key;
+        _repair_tlv.nss = _rlc_info.window_size;
+        _repair_tlv.nrs = 1;
+
+        // Update RLC informations after repair
+        _rlc_info.buffer_size -= _rlc_info.window_step;
+        ++_rlc_info.repair_key;
+
+        _rlc_info.previous_window_step = _rlc_info.window_step;
+        // Update coding rate
+        // TODO
+
+        
+        return 1;
+    }
+
+
+    return 0;
+}
+
+void
+IP6SRv6FECEncode::store_source_symbol(Packet *p_in, uint32_t encoding_symbol_id)
+{
+    // Free previous packet at the same place in the buffer
+    Packet *previous_packet = _rlc_info.source_buffer[encoding_symbol_id % SRV6_FEC_BUFFER_SIZE];
+    if (previous_packet) {
+        previous_packet->kill();
+    }
+
+    _rlc_info.source_buffer[encoding_symbol_id % SRV6_FEC_BUFFER_SIZE] = p_in->clone(true);
+}
+
+void
+IP6SRv6FECEncode::rlc_encode_symbols(uint32_t encoding_symbol_id)
+{
+    tinymt32_t prng = _rlc_info.prng;
+    // tinymt32_init(&prng, _rlc_info.repair_key);
+    tinymt32_init(&prng, 1);
+    // encoding_symbol_id: of the last source symbol (i.e. of the repair symbol)
+    uint32_t start_esid = encoding_symbol_id - _rlc_info.window_size + 1;
+    for (int i = 0; i < _rlc_info.window_size; ++i) {
+        uint8_t idx = (start_esid + i) % SRV6_FEC_BUFFER_SIZE;
+        Packet *source_symbol = _rlc_info.source_buffer[idx];
+
+        rlc_encode_one_symbol(source_symbol, _repair_packet, &prng, _rlc_info.muls, &_repair_tlv);
+    }
+}
+
+void
+IP6SRv6FECEncode::xor_encode_symbols(uint32_t encoding_symbol_id)
+{
+    uint32_t start_esid = encoding_symbol_id - _rlc_info.window_size + 1;
+    for (int i = 0; i < _rlc_info.window_size; ++i) {
+        uint8_t idx = (start_esid + i) % SRV6_FEC_BUFFER_SIZE;
+        Packet *source_symbol = _rlc_info.source_buffer[idx];
+
+        xor_encode_one_symbol(source_symbol, _repair_packet, &_repair_tlv);
+    }
+}
+
+void
+IP6SRv6FECEncode::xor_encode_one_symbol(Packet *s, WritablePacket *r, repair_tlv_t *repair_tlv)
+{
+    // Leave room for the IPv6 Header, SRv6 Header (3 segments) and repair TLV
+    uint8_t repair_offset = 40 + 8 + 16 * 2 + sizeof(repair_tlv_t);
+    uint8_t *s_64 = (uint8_t *)s->data();
+    uint8_t *r_64 = (uint8_t *)(r->data() + repair_offset);
+
+    for (uint16_t i = 0; i < s->length() / sizeof(uint8_t); ++i) {
+        r_64[i] ^= s_64[i];
+    }
+
+    // Also code the potential remaining data
+    uint8_t *s_8 = (uint8_t *)s->data();
+    uint8_t *r_8 = (uint8_t *)(r->data() + repair_offset);
+    for (uint16_t i = (s->length() / sizeof(uint8_t)) * sizeof(uint8_t); i < s->length(); ++i) {
+        r_8[i] ^= s_8[i];
+    }
+
+    // Encode the packet length
+    uint16_t coded_length = repair_tlv->coded_length;
+    repair_tlv->coded_length ^= s->length();
+}
+
+tinymt32_t
+IP6SRv6FECEncode::rlc_reset_coefs() {
+    tinymt32_t prng;
+    memset(&prng, 0, sizeof(tinymt32_t));
+    prng.mat1 = 0x8f7011ee;
+    prng.mat2 = 0xfc78ff1f;
+    prng.tmat = 0x3793fdff;
+    return prng;
+}
+
+void IP6SRv6FECEncode::rlc_fill_muls(uint8_t muls[256 * 256]) {
+    for (int i = 0; i < 256; ++i) {
+        for (int j = 0; j < 256; ++j) {
+            muls[i * 256 + j] = gf256_mul_formula(i, j);
+        }
+    }
+}
+
+uint8_t IP6SRv6FECEncode::rlc_get_coef(tinymt32_t *prng) {
+    uint8_t coef = tinymt32_generate_uint32(prng);
+    if (coef == 0) return 1;
+    return coef;
+}
+
+void IP6SRv6FECEncode::rlc_encode_one_symbol(Packet *s, WritablePacket *r, tinymt32_t *prng, uint8_t muls[256 * 256 * sizeof(uint8_t)], repair_tlv_t *repair_tlv) {
+    // Leave room for the IPv6 Header, SRv6 Header (3 segments) and repair TLV
+    uint8_t repair_offset = 40 + 8 + 16 * 2 + sizeof(repair_tlv_t);
+
+    // TODO: cancel varying fields?
+
+    // Get coefficient for this source symbol
+    uint8_t coef = rlc_get_coef(prng);
+
+    uint16_t packet_length = s->length(); // Cast in uint16_t because 16 bits for IPv6 packet length
+
+    // Encode the packet in the repair symbol
+    symbol_add_scaled(r->data() + repair_offset, coef, s->data(), packet_length, muls);
+
+    // Encode the packet length
+    uint16_t coded_length = repair_tlv->coded_length;
+    symbol_add_scaled(&coded_length, coef, &packet_length, sizeof(uint16_t), muls);
+    repair_tlv->coded_length = coded_length;
+}
+
+WritablePacket * IP6SRv6FECEncode::srv6_fec_add_source_tlv(Packet *p_in, source_tlv_t *tlv) {
+    const click_ip6 *ip6 = reinterpret_cast<const click_ip6 *>(p_in->data());
+    const click_ip6_sr *srv6 = (const click_ip6_sr *)ip6_find_header(ip6, IP6_EH_ROUTING, p_in->end_data());
+    if (!srv6) {
+        p_in->kill();
+        // click_chatter("Not an SRv6 packet!");
+        return 0;
+    }
+    
+    unsigned srv6_offset = (unsigned char*)srv6 - (unsigned char*)ip6;
+
+    // Extend the packet to add the TLV
+    WritablePacket *p = p_in->push(sizeof(source_tlv_t));
+    if (!p)
+        return 0;
+
+    uint16_t srv6_len = 8 + srv6->ip6_hdrlen * 8;
+
+    // Move headers and add TLV
+    memmove(p->data(), p->data() + sizeof(source_tlv_t), srv6_len + srv6_offset);
+    memcpy(p->data() + sizeof(click_ip6) + srv6_len, tlv, sizeof(source_tlv_t));
+
+    // Update the new length of the SRv6 Header
+    click_ip6_sr *srv6_update = reinterpret_cast<click_ip6_sr *>(p->data() + srv6_offset);
+    srv6_update->ip6_hdrlen += sizeof(source_tlv_t)/8;
+    click_ip6 *ip6_update = reinterpret_cast<click_ip6 *>(p->data());
+    ip6_update->ip6_plen = htons(ntohs(ip6_update->ip6_plen) + sizeof(source_tlv_t));
+    p->set_network_header(p->data(), srv6_offset + srv6_len + sizeof(source_tlv_t));
+    return p;
+}
+
+void IP6SRv6FECEncode::encapsulate_repair_payload(WritablePacket *p, repair_tlv_t *tlv, uint16_t packet_length) {
+    // IPv6 and SRv6 Header pointers
+    click_ip6 *r_ip6 = reinterpret_cast<click_ip6 *>(p->data());
+    click_ip6_sr *r_srv6 = reinterpret_cast<click_ip6_sr *>(p->data() + sizeof(click_ip6));
+    repair_tlv_t *r_tlv = reinterpret_cast<repair_tlv_t *>(p->data() + sizeof(click_ip6) + 8 + 32);
+
+    // IPv6 Header
+    memcpy(r_ip6, &_repair_ip6, sizeof(click_ip6));
+    r_ip6->ip6_plen = htons(packet_length + sizeof(click_ip6_sr) + sizeof(repair_tlv_t) + 2 * sizeof(IP6Address));
+
+    // SRv6 Header
+    memcpy(r_srv6, &_repair_srv6, sizeof(click_ip6_sr));
+    memcpy(&r_srv6->segments[0], enc.data(), sizeof(IP6Address));
+    memcpy(&r_srv6->segments[1], dec.data(), sizeof(IP6Address));
+    
+    // Add repair TLV
+    memcpy(r_tlv, tlv, sizeof(repair_tlv_t));
+
+    // Set annotations
+    _repair_packet->set_network_header(p->data(), sizeof(click_ip6) + sizeof(click_ip6_sr) + sizeof(repair_tlv_t) + 2 * sizeof(IP6Address)) ;
+}
+
+void
+IP6SRv6FECEncode::feedback_message(Packet *p_in, std::function<void(Packet*)>push)
+{
+    // // click_chatter("GETTING A FEEDBACK MESSAGE");
+    const click_ip6 *ip6 = reinterpret_cast<const click_ip6 *>(p_in->data());
+    const click_ip6_sr *srv6 = reinterpret_cast<const click_ip6_sr *>(p_in->data() + sizeof(click_ip6));
+    const feedback_tlv_t *tlv = reinterpret_cast<const feedback_tlv_t *>(p_in->data() + sizeof(click_ip6) + sizeof(click_ip6_sr) + (srv6->last_entry + 1) * 16);
+
+    // Hard thresholds defining the algorithm
+    // TODO: put somewhere else
+    double SRV6_FEC_FB_LOST_THRESH = 0.05;
+    double SRV6_FEC_FB_BURST_MIN = 2.0;
+    double SRV6_FEC_FB_ALPHA = 0.5;
+    
+    // No feedback, i.e. no protected traffic since last update
+    // Reset the values by default
+    if (tlv->nb_theoric == 0) {
+        _rlc_info.window_size = 4; // TODO: make auto
+        _rlc_info.window_step = 2; // TODO: make auto
+        // click_chatter("Reset the values of the FEC Scheme algorithm");
+        return;
+    }
+    
+    // No lost packet in the window
+    // Increase the window step to generate less repair symbols
+    // if (tlv->nb_lost == 0) {
+    //     _rlc_info.loss_estimation = 0;
+    //     _rlc_info.generate_repair_symbols = false;
+    //     return;
+    // }
+
+    // click_chatter("lost=%u theoric=%u", tlv->nb_lost, tlv->nb_theoric);
+
+    double lost = _rlc_info.loss_estimation;
+    // Update the lost estimation gradually
+    lost = (tlv->nb_lost / tlv->nb_theoric) * SRV6_FEC_FB_ALPHA + (lost) * (1.0 - SRV6_FEC_FB_ALPHA);
+    _rlc_info.loss_estimation = lost;
+
+    // Compute the redundancy ratio compared to the data packets
+    double redundancy = 1.0 / _rlc_info.window_step;
+
+    // There are more loss than redundancy packets
+    // Reduce the window step to increase redundancy
+    if (redundancy < lost) {
+        if (_rlc_info.window_step > 1) {
+            --_rlc_info.window_step;
+        }
+        // TODO: Also generate immediate repair symbols ?
+    } else {
+        _rlc_info.window_step = my_min(_rlc_info.window_step, RLC_MAX_STEP);
+    }
+
+    // Update the window size by inspecting the possible burst losses in the data
+    uint8_t max_seen_burst = 0;
+    uint8_t current_burst = 0;
+    uint64_t lost_bitstring = (~tlv->bit_string) & ((1 << tlv->nb_theoric) - 1);
+    while (lost_bitstring > 0) {
+        if (lost_bitstring & 1) {
+            ++current_burst;
+        } else {
+            max_seen_burst = my_max(max_seen_burst, current_burst);
+            current_burst = 0;
+        }
+        lost_bitstring >>=2;
+    }
+    // Compute the maximum burst length we can recover with the current 
+    uint8_t theoric_max_burst = ceil(_rlc_info.window_size / (double)_rlc_info.window_step);
+    
+    if (theoric_max_burst < max_seen_burst) {
+        _rlc_info.window_size = my_min(_rlc_info.window_size, RLC_MAX_WINDOW);
+    } else if (theoric_max_burst > max_seen_burst) {
+        _rlc_info.window_size = my_max(1, _rlc_info.window_size);
+    }
+
+    if (_rlc_info.window_size == RLC_MAX_WINDOW && _rlc_info.window_step == RLC_MAX_STEP && _rlc_info.loss_estimation < 0.0001) {
+        _rlc_info.generate_repair_symbols = false;
+    } else {
+        _rlc_info.generate_repair_symbols = true;
+    }
+
+    // click_chatter("loss=%f, size=%u, step=%u", _rlc_info.loss_estimation, _rlc_info.window_size, _rlc_info.window_step);
+}
+
+CLICK_ENDDECLS
+EXPORT_ELEMENT(IP6SRv6FECEncode)
+ELEMENT_MT_SAFE(IP6SRv6FECEncode)
diff --git a/elements/ip6/ip6srv6fec.hh b/elements/ip6/ip6srv6fec.hh
new file mode 100644
index 0000000000..c8361dfa08
--- /dev/null
+++ b/elements/ip6/ip6srv6fec.hh
@@ -0,0 +1,158 @@
+#ifndef CLICK_IP6SRv6FECEncode_HH
+#define CLICK_IP6SRv6FECEncode_HH
+#include <click/batchelement.hh>
+#include <click/glue.hh>
+#include <click/atomic.hh>
+#include <clicknet/ip6.h>
+#include <click/ip6address.hh>
+#include <tinymt32/tinymt32.h>
+#include <swifsymbol/swifsymbol.h>
+
+#ifndef SRV6FEC_HH
+#define SRV6FEC_HH
+#define SRV6_FEC_BUFFER_SIZE 64
+
+#define TLV_TYPE_FEC_SOURCE 28
+#define TLV_TYPE_FEC_REPAIR 29
+#define RLC_MAX_WINDOWS 8
+#define RLC_MAX_WINDOW 30
+#define RLC_MAX_STEP 10
+#define LOCAL_MTU 1500
+
+// SRv6-FEC TLV structures
+struct source_tlv_t {
+  uint8_t type;
+  uint8_t len;
+  uint16_t padding;
+  uint32_t sfpid; // Source FEC Payload ID
+} CLICK_SIZE_PACKED_ATTRIBUTE;
+
+struct repair_tlv_t {
+  uint8_t type;
+  uint8_t len;
+  uint16_t padding;
+  uint32_t rfpid; // Repair FEC Payload ID
+  union {
+    struct {
+      uint8_t previous_window_size;
+      uint8_t window_step;
+      uint16_t repair_key;
+    } rlc_rfi;
+    uint32_t rfi;
+  }; // Repair FEC Info
+  uint16_t coded_length;
+  uint8_t nss; // Number Source Symbol
+  uint8_t nrs; // Number Repair Symbol
+} CLICK_SIZE_PACKED_ATTRIBUTE;
+
+struct feedback_tlv_t {
+  uint8_t type;
+  uint8_t len;
+  uint16_t nb_theoric;
+  uint16_t nb_lost;
+  uint16_t padding;
+  uint64_t bit_string;
+} CLICK_SIZE_PACKED_ATTRIBUTE;
+
+#endif
+
+CLICK_DECLS
+
+/*
+=c
+
+IP6SRv6FECEncode(ENC, DEC)
+
+=s ip
+
+Forward Erasure Correction for IPv6 Segment Routing
+
+=d
+
+Takes the encoder and decoder SIDs
+
+=e
+
+
+  IP6SRv6FECEncode(fc00::a, fc00::9)
+
+=a IP6Encap */
+
+struct rlc_info_t {
+  uint32_t encoding_symbol_id;
+  uint16_t repair_key;
+  uint8_t window_size;
+  uint8_t window_step;
+  uint8_t buffer_size;
+  uint8_t previous_window_step;
+  bool generate_repair_symbols;
+  double loss_estimation;
+
+  // RLC relative information
+  tinymt32_t prng;
+  uint16_t max_length; // Seen for this window
+  uint8_t muls[256 * 256 * sizeof(uint8_t)];
+  Packet *source_buffer[SRV6_FEC_BUFFER_SIZE]; // Ring buffer
+};
+
+#define SRV6_FEC_RLC 0
+#define SRV6_FEC_XOR 1
+#define SRV6_FEC_FEEDBACK_INPUT 1
+
+class IP6SRv6FECEncode : public BatchElement { 
+ 
+ public:
+
+  IP6SRv6FECEncode();
+  ~IP6SRv6FECEncode();
+
+  const char *class_name() const override        { return "IP6SRv6FECEncode"; }
+  const char *port_count() const override        { return "2/1"; } // Two inputs for the feedback
+
+  int  configure(Vector<String> &, ErrorHandler *) CLICK_COLD;
+  bool can_live_reconfigure() const     { return true; }
+  void add_handlers() CLICK_COLD;
+
+  void push(int input, Packet * p_in) override;
+#if HAVE_BATCH
+  void push_batch(int input, PacketBatch * batch_in) override;
+#endif
+
+ private:
+
+  uint8_t _fec_scheme;
+  source_tlv_t _source_tlv;
+  repair_tlv_t _repair_tlv;
+  rlc_info_t   _rlc_info;
+  WritablePacket *_repair_packet;
+  click_ip6 _repair_ip6;
+  click_ip6_sr _repair_srv6;
+  bool _use_dst_anno;
+  bool _send_repair;
+  bool _do_fec;
+  IP6Address enc; // Encoder SID
+  IP6Address dec; // Decoder SID
+  IP6Address fed; // Feedback SID
+
+  static String read_handler(Element *, void *) CLICK_COLD;
+  void fec_framework(Packet *p_in, std::function<void(Packet*)> push);
+  int fec_scheme(Packet *p_in, std::function<void(Packet*)>push);
+  void store_source_symbol(Packet *p_in, uint32_t encodind_symbol_id);
+  void encapsulate_repair_payload(WritablePacket *p, repair_tlv_t *tlv, uint16_t packet_length);
+  WritablePacket *srv6_fec_add_source_tlv(Packet *p_in, source_tlv_t *tlv);
+  void rlc_encode_symbols(uint32_t encoding_symbol_id);
+  tinymt32_t rlc_reset_coefs();
+  void rlc_fill_muls(uint8_t muls[256 * 256]);
+  uint8_t rlc_get_coef(tinymt32_t *prng);
+  void rlc_encode_one_symbol(Packet *s, WritablePacket *r, tinymt32_t *prng, uint8_t muls[256 * 256 * sizeof(uint8_t)], repair_tlv_t *repair_tlv);
+
+  void xor_encode_symbols(uint32_t encoding_symbol_id);
+  void xor_encode_one_symbol(Packet *s, WritablePacket *r, repair_tlv_t *repair_tlv);
+
+  void feedback_message(Packet *p_in, std::function<void(Packet*)>push);
+};
+
+
+
+CLICK_ENDDECLS
+#endif
diff --git a/elements/ip6/ip6srv6fecdecode.cc b/elements/ip6/ip6srv6fecdecode.cc
new file mode 100644
index 0000000000..d0a52ab8bb
--- /dev/null
+++ b/elements/ip6/ip6srv6fecdecode.cc
@@ -0,0 +1,1087 @@
+/*
+ * ip6encap.{cc,hh} -- element encapsulates packet in IP6 header
+ * Louis Navarre
+ * Tom Barbette
+ *
+ * Copyright (c) 2021 IP Networking Lab, UCLouvain
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Click LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Click LICENSE file; the license in that file is
+ * legally binding.
+ */
+
+#include <click/config.h>
+#include "ip6srv6fecdecode.hh"
+#include <click/nameinfo.hh>
+#include <click/confparse.hh>
+#include <click/error.hh>
+#include <click/glue.hh>
+#define MAX(a, b) ((a > b) ? a : b)
+#define MIN(a, b) ((a < b) ? a : b)
+CLICK_DECLS
+
+IP6SRv6FECDecode::IP6SRv6FECDecode()
+{
+    _use_dst_anno = false;
+    memset(&_rlc_info, 0, sizeof(rlc_info_decoder_t));
+    memset(&_rlc_feedback, 0, sizeof(srv6_fec2_feedback_t));
+    assign_inv(_rlc_info.table_inv);
+    rlc_fill_muls(_rlc_info.muls);
+
+    // Malloc once the utils for the RLC recovering scheme
+    _rlc_utils.ss_array = (srv6_fec2_source_t **)CLICK_LALLOC(sizeof(srv6_fec2_source_t *) * SRV6_RLC_MAX_SYMBOLS);
+    memset(_rlc_utils.ss_array, 0, sizeof(srv6_fec2_source_t *) * SRV6_RLC_MAX_SYMBOLS);
+
+    _rlc_utils.rs_array = (srv6_fec2_repair_t **)CLICK_LALLOC(sizeof(srv6_fec2_repair_t *) * RLC_MAX_WINDOWS);
+    memset(_rlc_utils.rs_array, 0, sizeof(srv6_fec2_repair_t *) * RLC_MAX_WINDOWS);
+
+    _rlc_utils.x_to_source = (uint8_t *)CLICK_LALLOC(sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+    memset(_rlc_utils.x_to_source, 0, sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+    _rlc_utils.source_to_x = (uint8_t *)CLICK_LALLOC(sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+    memset(_rlc_utils.source_to_x, 0, sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+
+    _rlc_utils.protected_symbols = (bool *)CLICK_LALLOC(sizeof(bool) * SRV6_RLC_MAX_SYMBOLS);
+    memset(_rlc_utils.protected_symbols, 0, sizeof(bool) * SRV6_RLC_MAX_SYMBOLS);
+
+    // This is not optimal as it should be the "maximum window size"
+    _rlc_utils.coefs = (uint8_t *)CLICK_LALLOC(sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+    memset(_rlc_utils.coefs, 0, sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+    // Are memset really usefull at this stage?
+
+    _rlc_utils.unknowns = (srv6_fec2_term_t **)CLICK_LALLOC(sizeof(srv6_fec2_term_t *) * SRV6_RLC_MAX_SYMBOLS);
+    memset(_rlc_utils.unknowns, 0, sizeof(srv6_fec2_term_t *) * SRV6_RLC_MAX_SYMBOLS);
+    _rlc_utils.system_coefs = (uint8_t **)CLICK_LALLOC(sizeof(uint8_t *) * RLC_MAX_WINDOWS);
+    memset(_rlc_utils.system_coefs, 0, sizeof(uint8_t *) * RLC_MAX_WINDOWS);
+    _rlc_utils.constant_terms = (srv6_fec2_term_t **)CLICK_LALLOC(sizeof(srv6_fec2_term_t *) * SRV6_RLC_MAX_SYMBOLS);
+    memset(_rlc_utils.constant_terms, 0, sizeof(srv6_fec2_term_t *) * SRV6_RLC_MAX_SYMBOLS);
+    _rlc_utils.undetermined = (bool *)CLICK_LALLOC(sizeof(bool) * SRV6_RLC_MAX_SYMBOLS);
+    memset(_rlc_utils.undetermined, 0, sizeof(bool) * SRV6_RLC_MAX_SYMBOLS);
+
+    for (int i = 0; i < RLC_MAX_WINDOWS; ++i) {
+        _rlc_utils.system_coefs[i] = (uint8_t *)CLICK_LALLOC(sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+        memset(_rlc_utils.system_coefs[i], 0, sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+    }
+
+    // TODO: init the terms of "constant terms" but requires to know the maximum packet length...
+}
+
+IP6SRv6FECDecode::~IP6SRv6FECDecode()
+{
+    //click_chatter("Before the segmentation fault: %u", _rlc_info.encoding_symbol_id);
+    for (int i = 0; i < SRV6_FEC_BUFFER_SIZE; ++i) {
+        srv6_fec2_source_t *packet = _rlc_info.source_buffer[i];
+        if (packet) {
+            packet->p->kill();
+            CLICK_LFREE(packet, sizeof(srv6_fec2_source_t));
+        }
+        srv6_fec2_repair_t *repair = _rlc_info.repair_buffer[i];
+        if (repair) {
+            repair->p->kill();
+            CLICK_LFREE(repair, sizeof(srv6_fec2_repair_t));
+        }
+        srv6_fec2_source_t *recovered = _rlc_info.recovd_buffer[i];
+        if (recovered) {
+            recovered->p->kill();
+            CLICK_LFREE(recovered, sizeof(srv6_fec2_source_t));
+        }
+    }
+
+    // Free all utils from RLC system
+    CLICK_LFREE(_rlc_utils.ss_array, sizeof(srv6_fec2_source_t *) * SRV6_RLC_MAX_SYMBOLS);
+    CLICK_LFREE(_rlc_utils.rs_array, sizeof(srv6_fec2_repair_t *) * RLC_MAX_WINDOWS);
+    CLICK_LFREE(_rlc_utils.x_to_source, sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+    CLICK_LFREE(_rlc_utils.source_to_x, sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+    CLICK_LFREE(_rlc_utils.protected_symbols, sizeof(bool) * SRV6_RLC_MAX_SYMBOLS);
+    CLICK_LFREE(_rlc_utils.coefs, sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+    CLICK_LFREE(_rlc_utils.unknowns, sizeof(srv6_fec2_term_t *) * SRV6_RLC_MAX_SYMBOLS);
+    CLICK_LFREE(_rlc_utils.constant_terms, sizeof(srv6_fec2_term_t *) * SRV6_RLC_MAX_SYMBOLS);
+    CLICK_LFREE(_rlc_utils.undetermined, sizeof(bool) * SRV6_RLC_MAX_SYMBOLS);
+    for (int i = 0; i < RLC_MAX_WINDOWS; ++i) {
+        CLICK_LFREE(_rlc_utils.system_coefs[i], sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+    }
+    CLICK_LFREE(_rlc_utils.system_coefs, sizeof(uint8_t *) * RLC_MAX_WINDOWS);
+}
+
+int
+IP6SRv6FECDecode::configure(Vector<String> &conf, ErrorHandler *errh)
+{
+    if (Args(conf, this, errh)
+	.read_mp("ENC", enc)
+	.read_mp("DEC", dec)
+	.read_or_set("RECOVER", _do_recover, true)
+	.complete() < 0)
+        return -1;
+    return 0;
+}
+
+
+void
+IP6SRv6FECDecode::push(int, Packet *p_in)
+{
+    fec_framework(p_in, [this](Packet*p){output(0).push(p);});
+}
+
+#if HAVE_BATCH
+void 
+IP6SRv6FECDecode::push_batch(int, PacketBatch *batch) {
+    EXECUTE_FOR_EACH_PACKET_ADD( fec_framework, batch);
+    if (batch) {
+        output_push_batch(0, batch);
+    }
+
+}
+#endif
+
+String
+IP6SRv6FECDecode::read_handler(Element *e, void *thunk)
+{
+    return "<error>";
+}
+
+void
+IP6SRv6FECDecode::add_handlers()
+{
+    add_read_handler("src", read_handler, 0, Handler::CALM);
+    add_write_handler("src", reconfigure_keyword_handler, "1 SRC");
+    add_read_handler("dst", read_handler, 1, Handler::CALM);
+    add_write_handler("dst", reconfigure_keyword_handler, "2 DST");
+}
+
+void
+IP6SRv6FECDecode::fec_framework(Packet *p_in)
+{
+    return fec_framework(p_in, [this](Packet*p){output(0).push(p);});
+}
+
+
+void
+IP6SRv6FECDecode::fec_framework(Packet *p_in, std::function<void(Packet*)>push)
+{
+    const click_ip6 *ip6 = reinterpret_cast<const click_ip6 *>(p_in->data());
+    const click_ip6_sr *srv6 = reinterpret_cast<const click_ip6_sr *>(p_in->data() + sizeof(click_ip6));
+    int err;
+
+    // Find TLV: source or repair symbol
+    uint8_t tlv_type = 0;
+    // last_entry is 0-indexed => +1
+    //click_chatter("srv6 %u", srv6->ip6_sr_next);
+    uint16_t start_tlv_offset = 8 + (srv6->last_entry + 1) * 16;
+    // ip6_hdrlen does not include the 8 first bytes => + 1
+    uint16_t total_tlv_size = (srv6->ip6_hdrlen + 1) * 8 - start_tlv_offset;
+    uint16_t read_bytes = 0;
+    uint8_t *tlv_ptr;
+    //click_chatter("SRv6 len=%u, total_tlv_size=%u", srv6->ip6_hdrlen, total_tlv_size);
+    while (read_bytes < total_tlv_size) { // Iterate over all TLVs of the SRH
+        tlv_ptr = (uint8_t *)srv6 + start_tlv_offset + read_bytes;
+        if (tlv_ptr[0] == TLV_TYPE_FEC_SOURCE || tlv_ptr[0] == TLV_TYPE_FEC_REPAIR) {
+            tlv_type = tlv_ptr[0];
+            break;
+        }
+        read_bytes += tlv_ptr[1];
+    }
+
+    // Not a source or repair symbol
+    if (tlv_type != TLV_TYPE_FEC_SOURCE && tlv_type != TLV_TYPE_FEC_REPAIR) {
+        push(p_in);
+        return;
+    }
+
+    if (tlv_type == TLV_TYPE_FEC_SOURCE) {
+        // Load TLV locally
+        source_tlv_t source_tlv;
+        //memset(&source_tlv, 0, sizeof(source_tlv_t));
+        memcpy(&source_tlv, tlv_ptr, sizeof(source_tlv_t));
+        
+        // Remove the TLV from the source packet
+        // We do not remove the TLV anymore to remain efficient
+        // remove_tlv_source_symbol(p, tlv_ptr - p->data()); // Cleaner way?
+
+        // Call FEC Scheme
+        fec_scheme_source(p_in, &source_tlv);
+    } else {
+        // Load TLV locally
+        repair_tlv_t repair_tlv;
+        //memset(&repair_tlv, 0, sizeof(repair_tlv_t));
+        memcpy(&repair_tlv, tlv_ptr, sizeof(repair_tlv_t));
+
+        // Call FEC Scheme
+        fec_scheme_repair(p_in, &repair_tlv, push);
+    }
+
+    // Send the (modified, without TLV) source symbol
+    // i.e., do not send the repair symbol out of the tunnel
+    //if (tlv_type == TLV_TYPE_FEC_SOURCE) {
+        push(p);
+    //}
+
+    // if (_rlc_info.esid_last_feedback >= 2) {
+    //     ////click_chatter("Show last: %u", _rlc_info.esid_last_feedback);
+    //     //rlc_feedback();
+    //     _rlc_info.esid_last_feedback = 0;
+    // } else {
+    //     ++_rlc_info.esid_last_feedback;
+    // }
+}
+
+int
+IP6SRv6FECDecode::fec_scheme_source(Packet *p_in, source_tlv_t *tlv)
+{
+    // Store packet as source symbol
+    store_source_symbol(p_in, tlv);
+
+    return 0;
+}
+
+void
+IP6SRv6FECDecode::fec_scheme_repair(Packet *p_in, repair_tlv_t *tlv, std::function<void(Packet*)>push)
+{
+
+    // Store packet as source symbol
+    store_repair_symbol(p_in, tlv);
+	if (!_do_recover) {
+		return;	
+	}
+    Packet* p;
+
+    // Call RLC recovery
+    if (tlv->padding == SRV6_FEC_RLC) {
+        rlc_recover_symbols(push);
+    } else {
+        xor_recover_symbols(push);
+    }
+}
+
+void
+IP6SRv6FECDecode::store_source_symbol(Packet *p_in, source_tlv_t *tlv) {
+    uint32_t encoding_symbol_id = tlv->sfpid;
+    // Store the source symbol
+    srv6_fec2_source_t *symbol;
+    // Optimization: reuse the previous malloc if available
+    srv6_fec2_source_t *previous_symbol = _rlc_info.source_buffer[encoding_symbol_id % SRV6_FEC_BUFFER_SIZE];
+    if (previous_symbol) {
+        if (previous_symbol->p) {
+            previous_symbol->p->kill();
+            previous_symbol->p = 0;
+        }
+        symbol = previous_symbol;
+    } else {
+        symbol = (srv6_fec2_source_t *)CLICK_LALLOC(sizeof(srv6_fec2_source_t));
+    }
+    symbol->encoding_symbol_id = tlv->sfpid;
+    symbol->p = p_in->clone(true);
+
+    _rlc_info.source_buffer[encoding_symbol_id % SRV6_FEC_BUFFER_SIZE] = symbol;
+
+    // Update the feedback string bit
+    // TODO: this line generates a segmentation fault when we exit the program
+    // Not the only one apparently
+    // Fow now, assume that we receive all packets
+    // _rlc_feedback.received_string = -1;
+    ////click_chatter("Storing %u", symbol->encoding_symbol_id);
+    _rlc_feedback.received_string |= (1 << _rlc_feedback.nb_received++);
+
+    // Update the most recent encoding symbol ID
+    // TODO: wrap up
+    _rlc_feedback.last_received_esid = tlv->sfpid;
+    ////click_chatter("MALLOC: %u", _rlc_feedback.last_received_esid);
+}
+
+void
+IP6SRv6FECDecode::store_repair_symbol(Packet *p_in, repair_tlv_t *tlv)
+{
+    uint32_t encoding_symbol_id = tlv->rfpid;
+
+    // Store the repair symbol
+    srv6_fec2_repair_t *symbol;
+    // Optimization: reuse the previous malloc if available
+    srv6_fec2_repair_t *previous_symbol = _rlc_info.repair_buffer[encoding_symbol_id % SRV6_FEC_BUFFER_SIZE];
+    if (previous_symbol) {
+        if (previous_symbol->p) {
+            previous_symbol->p->kill();
+            previous_symbol->p = 0;
+        }
+        symbol = previous_symbol;
+    } else {
+        symbol = (srv6_fec2_repair_t *)CLICK_LALLOC(sizeof(srv6_fec2_repair_t));
+    }
+    memcpy(&symbol->tlv, tlv, sizeof(repair_tlv_t));
+    symbol->p = p_in->clone(); // TODO: see if correct with Tom
+
+    _rlc_info.repair_buffer[encoding_symbol_id % SRV6_FEC_BUFFER_SIZE] = symbol;
+    _rlc_info.encoding_symbol_id = encoding_symbol_id;
+}
+
+void
+IP6SRv6FECDecode::remove_tlv_source_symbol(WritablePacket *p, uint16_t offset_tlv)
+{
+    // Update payload length of IPv6 Header and SRv6 Header
+    unsigned len = p->network_header_length();
+    click_ip6 *ip6 = reinterpret_cast<click_ip6 *>(p->data());
+    click_ip6_sr *srv6 = reinterpret_cast<click_ip6_sr *>(p->data() + sizeof(click_ip6));
+    ip6->ip6_plen -= htons(sizeof(source_tlv_t));
+    srv6->ip6_hdrlen -= 1;
+
+    // Push everything before the TLV, sizeof(tlv) after
+    memmove(p->data() + sizeof(source_tlv_t), p->data(), offset_tlv);
+    p->pull(sizeof(source_tlv_t));
+    p->set_network_header(p->data(), len - sizeof(source_tlv_t));
+}
+
+void
+IP6SRv6FECDecode::rlc_get_coefs(tinymt32_t *prng, uint32_t seed, int n, uint8_t *coefs)
+{
+    tinymt32_init(prng, seed);
+    int i;
+    for (i = 0 ; i < n ; i++) {
+        coefs[i] = (uint8_t) tinymt32_generate_uint32(prng);
+        if (coefs[i] == 0)
+            coefs[i] = 1;
+    }
+}
+
+void
+IP6SRv6FECDecode::rlc_fill_muls(uint8_t muls[256 * 256])
+{
+    for (int i = 0; i < 256; ++i) {
+        for (int j = 0; j < 256; ++j) {
+            muls[i * 256 + j] = gf256_mul_formula(i, j);
+        }
+    }
+}
+
+srv6_fec2_term_t *
+IP6SRv6FECDecode::init_term(Packet *p, uint16_t offset, uint16_t max_packet_length)
+{
+    srv6_fec2_term_t *t = (srv6_fec2_term_t *)CLICK_LALLOC(sizeof(srv6_fec2_term_t));
+    uint8_t *data = (uint8_t *)CLICK_LALLOC(sizeof(uint8_t) * max_packet_length);
+    memcpy(data, p->data() + offset, p->length() - offset);
+    t->data = data;
+    t->length.coded_length = 0;
+    return t;
+}
+void kill_term(srv6_fec2_term_t *t)
+{
+    //click_chatter("Before kill term");
+    CLICK_LFREE(t->data, t->length.data_length);
+    CLICK_LFREE(t, sizeof(srv6_fec2_term_t));
+    //click_chatter("After kill term");
+}
+
+void
+IP6SRv6FECDecode::rlc_recover_symbols(std::function<void(Packet*)>push)
+{
+    uint16_t max_packet_length = 0; // decoding size
+    uint8_t nb_source_symbols = 0;
+    uint8_t window_size = 0;
+    uint8_t window_step = 0;
+    uint8_t previous_window_step = 0;
+    uint8_t max_seen_window_size = 0;
+    uint32_t encoding_symbol_id = _rlc_info.encoding_symbol_id;
+    ////click_chatter("TEST 1");
+
+    // Heuristic: we avoid to look for lost packets to recover if the current
+    // repair symbol does not protect any lost source symbol
+    bool useful_repair = false;
+    srv6_fec2_repair_t *repair_tmp = _rlc_info.repair_buffer[encoding_symbol_id % SRV6_FEC_BUFFER_SIZE];
+    uint8_t window_size_tmp = repair_tmp->tlv.nss;
+    for (int i = 0; i < window_size_tmp; ++i) {
+        uint32_t source_esid = encoding_symbol_id - i;
+        srv6_fec2_source_t *source = _rlc_info.source_buffer[source_esid % SRV6_FEC_BUFFER_SIZE];
+        if (!source || source->encoding_symbol_id != (source_esid)) {
+            useful_repair = true;
+            break;
+        }
+    }
+    if (!useful_repair) {
+        return;
+    }
+    ////click_chatter("TEST 2");
+
+
+    // Init the pseudo random number generator
+    tinymt32_t prng;
+    memset(&prng, 0, sizeof(tinymt32_t)),
+    prng.mat1 = 0x8f7011ee;
+    prng.mat2 = 0xfc78ff1f;
+    prng.tmat = 0x3793fdff;
+
+    // 1. Detect the size of the system:
+    //      - Number of rows (i.e., equations or repair symbols)
+    //      - Number of columns (i.e., unknowns or lost source symbols)
+    uint8_t nb_windows = 0; // One window = one repair symbol
+    uint32_t running_esid = encoding_symbol_id; // esid = encoding symbol id
+    for (int i = 0; i < RLC_MAX_WINDOWS; ++i) {
+        srv6_fec2_repair_t *repair = _rlc_info.repair_buffer[running_esid % SRV6_FEC_BUFFER_SIZE];
+        // Did not receive this repair symbol => stop iteration
+        if (!repair || repair->tlv.rfpid != running_esid) {
+            break;
+        }
+        const click_ip6_sr *srv6 = reinterpret_cast<const click_ip6_sr *>(repair->p->data() + sizeof(click_ip6));
+        uint16_t repair_offset = sizeof(click_ip6) + sizeof(click_ip6_sr) + srv6->ip6_hdrlen * 8;
+        max_packet_length = MAX(max_packet_length, repair->p->length() - repair_offset);
+        window_size = repair->tlv.nss;
+        max_seen_window_size = MAX(max_seen_window_size, window_size);
+        window_step = (repair->tlv.rfi >> 16) & 0xff;
+        previous_window_step = (repair->tlv.rfi >> 24);
+        // Constrain a bit the system
+        if (nb_source_symbols + window_size > SRV6_RLC_MAX_SYMBOLS) {
+            break; // Too much symbols otherwise
+        }
+        ++nb_windows;
+        nb_source_symbols += previous_window_step;
+        running_esid -= previous_window_step;
+        // No coding before this step
+        if (previous_window_step == 0) {
+            break;
+        }
+    }
+
+    // Still need to count the symbols at the beginning of the first window
+    nb_source_symbols += window_size - previous_window_step; // TODO: verify this: LGTM
+
+    // No valid window: no repair symbol received or no FEC applied
+    // Should not happen since this function is triggered by the
+    // reception of a repair symbol
+    if (unlikely(nb_windows == 0)) {
+        //click_chatter("Should not happen empty window");
+        return;
+    }
+
+    ////click_chatter("TEST 3");
+
+    // Received source symbols array
+    srv6_fec2_source_t **ss_array = _rlc_utils.ss_array;
+    memset(ss_array, 0, sizeof(srv6_fec2_source_t *) * SRV6_RLC_MAX_SYMBOLS);
+
+    // Received repair symbols array
+    srv6_fec2_repair_t **rs_array = _rlc_utils.rs_array;
+    memset(rs_array, 0, sizeof(srv6_fec2_repair_t *) * RLC_MAX_WINDOWS);
+
+    uint8_t nb_unknwons = 0;
+    uint8_t *x_to_source = _rlc_utils.x_to_source;
+    uint8_t *source_to_x = _rlc_utils.source_to_x;
+    memset(x_to_source, 0, sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+    memset(source_to_x, -1, sizeof(uint8_t) * SRV6_RLC_MAX_SYMBOLS);
+
+    bool *protected_symbol = _rlc_utils.protected_symbols;
+    memset(protected_symbol, 0, sizeof(bool) * SRV6_RLC_MAX_SYMBOLS);
+
+    uint32_t id_first_ss_first_window = encoding_symbol_id - nb_source_symbols + 1;
+    uint32_t id_first_rs_first_window = id_first_ss_first_window + window_size - 1; // TODO: check this: LGTM
+    // Locate the source and repair symbols and store separately
+    uint8_t idx = id_first_rs_first_window;
+    for (int i = 0; i < nb_windows; ++i) {
+        srv6_fec2_repair_t *repair = _rlc_info.repair_buffer[idx % SRV6_FEC_BUFFER_SIZE];
+        if (!repair) {
+            //click_chatter("ERROR 3");
+            return; // TODO: free all
+        }
+        rs_array[i] = repair;
+        // Update index for the next repair symbol
+        idx += (repair->tlv.rfi >> 16) & 0xff;
+        // //click_chatter("RFI=%x, window_step=%u, window_step=%u", repair->tlv.rfi, (repair->tlv.rfi >> 16) & 0xff, repair->tlv.rlc_rfi.window_step);
+    }
+    ////click_chatter("TEST 4");
+    for (int i = 0; i < nb_source_symbols; ++i) {
+        idx = (id_first_ss_first_window + i) % SRV6_FEC_BUFFER_SIZE;
+        srv6_fec2_source_t *source = _rlc_info.source_buffer[idx];
+        uint32_t id_theoric = id_first_ss_first_window + i;
+        bool is_lost = 0;
+        if (source) {
+            uint32_t id_from_buffer = source->encoding_symbol_id;
+            if (id_theoric == id_from_buffer) {
+                // Received symbol, store it in the buffer
+                ss_array[i] = source;
+                // //click_chatter("Source length=%u", source->p->length());
+            } else {
+                is_lost = 1;
+            }
+        } else {
+            is_lost = 1;
+        }
+        if (is_lost) { // Maybe this symbol was recovered earlier and stored in recovered buffer
+            srv6_fec2_source_t *rec = _rlc_info.recovd_buffer[idx];
+            if (rec) {
+                if (rec->encoding_symbol_id == id_theoric) {
+                    ss_array[i] = rec;
+                    // //click_chatter("Rec length=%u", rec->p->length());
+                    
+                    // Hence the packet is not lost anymore
+                    is_lost = 0;
+                }
+            }
+        }
+        if (is_lost) {
+            x_to_source[nb_unknwons] = i;
+            source_to_x[i] = nb_unknwons;
+            ++nb_unknwons;
+        }
+    }
+    //click_chatter("TEST 5 - possible no unk");
+
+    // Maybe no need for recovery
+    if (nb_unknwons == 0) {
+        return;
+    }
+
+    //click_chatter("TEST 6");
+
+    // Construct the system Ax=b
+    // click_chatter("Max seen window size=%u, SRV6 RLC MAX SYMBOLS=%u, nb unk=%u", max_seen_window_size, SRV6_RLC_MAX_SYMBOLS, nb_unknwons);
+    int n_eq = MIN(nb_unknwons, nb_windows);
+    uint8_t *coefs = _rlc_utils.coefs;
+    // TODO: directly make packets with maximum length ? See if legit but would be faster
+    srv6_fec2_term_t **unknowns = _rlc_utils.unknowns;
+    uint8_t **system_coefs = _rlc_utils.system_coefs;
+    srv6_fec2_term_t **constant_terms = _rlc_utils.constant_terms;
+    bool *undetermined = _rlc_utils.undetermined;
+    memset(coefs, 0, max_seen_window_size * sizeof(uint8_t));
+    memset(unknowns, 0, sizeof(srv6_fec2_term_t *) * nb_unknwons);
+    memset(constant_terms, 0, sizeof(srv6_fec2_term_t *) * nb_unknwons);
+    memset(undetermined, 0, sizeof(bool) * nb_unknwons); 
+
+    // //click_chatter("Nbunk=%u neq=%u alors que RLCMAXWINDOW=%u nbwind=%u", nb_unknwons, n_eq, RLC_MAX_WINDOWS, nb_windows);
+    for (int i = 0; i < n_eq; ++i) {
+        memset(system_coefs[i], 0, sizeof(uint8_t) * nb_unknwons);
+    }
+
+    //click_chatter("TEST 7");
+
+    int i = 0; // Index of the row in the system
+    for (int rs = 0; rs < nb_windows; ++rs) {
+        srv6_fec2_repair_t *repair = rs_array[rs];
+        uint8_t this_window_size = repair->tlv.nss;
+        uint32_t this_encoding_symbol_id = repair->tlv.rfpid;
+        bool protect_at_least_one = false;
+        // Check if this repair symbol protects at least one lost source symbol
+        // the following seems correct
+        int idx = this_encoding_symbol_id - id_first_ss_first_window - this_window_size + 1; // TODO: check if correct
+        for (int k = 0; k < this_window_size; ++k) {
+            if (!ss_array[idx + k] && !protected_symbol[idx + k]) {
+                protect_at_least_one = true;
+                protected_symbol[idx + k] = true;
+                break; // We know it protects at least one
+            }
+        }
+        if (!protect_at_least_one) {
+            continue; // Ignore this repair symbol if does not protect any lost
+        }
+
+        // 1) Independent term (b) ith row
+        const click_ip6_sr *srv6 = reinterpret_cast<const click_ip6_sr *>(repair->p->data() + sizeof(click_ip6));
+        uint16_t repair_offset = sizeof(click_ip6) + sizeof(click_ip6_sr) + srv6->ip6_hdrlen * 8;
+        constant_terms[i] = init_term(repair->p, repair_offset, max_packet_length);
+        constant_terms[i]->length.coded_length = repair->tlv.coded_length;
+
+        // 2) Coefficient matrix (A) ith row
+        uint16_t repair_key = repair->tlv.rlc_rfi.repair_key; // TODO
+        // rlc_get_coefs(&prng, repair_key, this_window_size, coefs);
+        rlc_get_coefs(&prng, 1, this_window_size, coefs); // TODO: put real coefficients
+        int current_unknown = 0; // Nb of unknown already discovered
+        idx = this_encoding_symbol_id - id_first_ss_first_window - this_window_size + 1; // TODO: check if correct
+        for (int j = 0; j < this_window_size; ++j) {
+            int idx_this_ss = idx + j; // Index of location of this source symbol
+            if (ss_array[idx_this_ss]) { // This protected symbol is received
+                // print_packet(ss_array[idx]);
+                // click_chatter("SUB %u %u", max_packet_length, ss_array[idx_this_ss]->p->length());
+                // if (ss_array[idx_this_ss]->p->length() > max_packet_length) {
+                //     click_chatter("ERREUR ICI CEST CA: %u %u", ss_array[idx_this_ss]->p->length(), max_packet_length);
+                //     click_chatter("ESID=%u and repair=%u", ss_array[idx_this_ss]->encoding_symbol_id, repair->tlv.rfpid);
+                //     break;
+                // }
+                // click_chatter("Taille de data de constant terms: %u", max_packet_length);
+                symbol_sub_scaled_term(constant_terms[i], coefs[j], ss_array[idx_this_ss], _rlc_info.muls);
+                // click_chatter("After sub");
+            } else {
+                if (source_to_x[idx_this_ss] != -1) {
+                    system_coefs[i][source_to_x[idx_this_ss]] = coefs[j]; // A[i][j] = coefs[j]
+                    ++current_unknown;
+                } else {
+                    //click_chatter("ERROR 4");
+                }
+            }
+        }
+        ++i;
+    }
+    uint8_t nb_effective_equations = i;
+
+    // Print system to see what is the fucking problem
+    // for (int row = 0; row < n_eq; ++row) {
+    //     for (int col = 0; col < nb_unknwons; ++col) {
+    //         fprintf(stderr, "%u ", system_coefs[row][col]);
+    //     }
+    //     fprintf(stderr, "\n");
+    // }
+
+    bool can_recover = nb_effective_equations >= nb_unknwons;
+    if (can_recover) {	
+
+        // Solve the system
+        gauss_elimination(nb_effective_equations, nb_unknwons, system_coefs, constant_terms, unknowns, undetermined, _rlc_info.muls, _rlc_info.table_inv, max_packet_length);
+        uint8_t current_unknown = 0;
+        int err = 0;
+        //click_chatter("TEST 9");
+        for (int j = 0; j < nb_unknwons; ++j) {
+            int idx = x_to_source[j];
+            if (!ss_array[idx] && !undetermined[current_unknown]) {
+                // Avoid stupid errors
+                if (unknowns[current_unknown]->length.data_length > max_packet_length) {
+                    //click_chatter("Wrong size");
+                    continue;
+                }
+                // Packet from the recovered data
+                WritablePacket *p_rec = recover_packet_fom_data(unknowns[current_unknown]);
+                if (!p_rec) {
+                    //click_chatter("Error from recovery confirmed");
+                    continue;
+                };
+
+                // //click_chatter("Recovered the packet with esid=%u", id_first_ss_first_window + idx);
+                // New pointer for the recovered values
+                srv6_fec2_source_t *recovered;
+                // Optimization: reuse the previous recovered symbol if present in the buffer
+                srv6_fec2_source_t *old_rec = _rlc_info.recovd_buffer[(id_first_ss_first_window + idx) % SRV6_FEC_BUFFER_SIZE];
+                if (old_rec) {
+                    if (old_rec->p) {
+                        old_rec->p->kill();
+                        old_rec->p = 0;
+                    }
+                    recovered = old_rec;
+                } else {
+                    recovered = (srv6_fec2_source_t *)CLICK_LALLOC(sizeof(srv6_fec2_source_t));
+                }
+                
+                recovered->encoding_symbol_id = id_first_ss_first_window + idx;
+
+                // Store a local copy of the packet for later recovery?
+                recovered->p = p_rec->clone();
+                // //click_chatter("Recovered packet %u", recovered->encoding_symbol_id);
+                push(p_rec);
+                // click_chatter("Recovered packet %u", recovered->encoding_symbol_id);
+
+                _rlc_info.recovd_buffer[recovered->encoding_symbol_id % SRV6_FEC_BUFFER_SIZE] = recovered;
+            }
+            ++current_unknown;
+        }
+        //click_chatter("TEST 10");
+    }
+
+    //click_chatter("TEST 11 max packet length=%u", max_packet_length);
+
+    // Free memory for the terms
+    for (int j = 0; j < i; ++j) {
+        if (constant_terms[j]) {
+            if (constant_terms[j]->data) {
+                CLICK_LFREE(constant_terms[j]->data, max_packet_length);
+            }
+            CLICK_LFREE(constant_terms[j], sizeof(srv6_fec2_term_t));
+        }
+    }
+
+    //click_chatter("TEST 12");
+
+    return;
+}
+
+void
+IP6SRv6FECDecode::symbol_add_scaled_term(srv6_fec2_term_t *symbol1, uint8_t coef, srv6_fec2_source_t *symbol2, uint8_t *mul)
+{
+    symbol_add_scaled(symbol1->data, coef, symbol2->p->data(), symbol2->p->length(), mul);
+    uint16_t pl = (uint16_t)symbol2->p->length();
+    symbol_add_scaled(&symbol1->length.coded_length, coef, &pl, sizeof(uint16_t), mul);
+}
+
+void
+IP6SRv6FECDecode::symbol_add_scaled_term(srv6_fec2_term_t *symbol1, uint8_t coef, srv6_fec2_term_t *symbol2, uint8_t *mul, uint16_t decoding_size)
+{
+    symbol_add_scaled(symbol1->data, coef, symbol2->data, decoding_size, mul);
+    uint16_t pl = (uint16_t)symbol2->length.coded_length;
+    symbol_add_scaled(&symbol1->length.coded_length, coef, &pl, sizeof(uint16_t), mul);
+}
+
+void
+IP6SRv6FECDecode::symbol_mul_term(srv6_fec2_term_t *symbol1, uint8_t coef, uint8_t *mul, uint16_t size)
+{
+    symbol_mul(symbol1->data, coef, size, mul);
+    symbol_mul((uint8_t *)&symbol1->length.coded_length, coef, sizeof(uint16_t), mul);
+}
+
+void
+IP6SRv6FECDecode::swap(uint8_t **a, int i, int j) {
+    uint8_t *tmp = a[j];
+    a[j] = a[i];
+    a[i] = tmp;
+}
+
+void
+IP6SRv6FECDecode::swap_b(srv6_fec2_term_t **a, int i, int j)
+{
+    srv6_fec2_term_t *tmp = a[j];
+    a[j] = a[i];
+    a[i] = tmp;
+}
+
+int
+IP6SRv6FECDecode::cmp_eq_i(uint8_t *a, uint8_t *b, int idx, int n_unknowns)
+{
+    if (a[idx] < b[idx]) return -1;
+    else if (a[idx] > b[idx]) return 1;
+    else if (a[idx] != 0) return 0;
+    return 0;
+}
+
+int
+IP6SRv6FECDecode::cmp_eq(uint8_t *a, uint8_t *b, int idx, int n_unknowns)
+{
+    for (int i = 0 ; i < n_unknowns; i++) {
+        int cmp = 0;
+        if ((cmp = cmp_eq_i(a, b, i, n_unknowns)) != 0) {
+            return cmp;
+        }
+    }
+    return 0;
+}
+
+void
+IP6SRv6FECDecode::sort_system(uint8_t **a, srv6_fec2_term_t **constant_terms, int n_eq, int n_unknowns)
+{
+    for (int i = 0; i < n_eq; ++i) {
+        int max = i;
+        for (int j = i + 1; j < n_eq; ++j) {
+            if (cmp_eq(a[max], a[j], i, n_unknowns) < 0) {
+                max = j;
+            }
+        }
+        swap(a, i, max);
+        swap_b(constant_terms, i, max);
+    }
+}
+
+int
+IP6SRv6FECDecode::first_non_zero_idx(const uint8_t *a, int n_unknowns) {
+    for (int i = 0 ; i < n_unknowns ; i++) {
+        if (a[i] != 0) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+void
+IP6SRv6FECDecode::gauss_elimination(int n_eq, int n_unknowns, uint8_t **a, srv6_fec2_term_t **constant_terms, srv6_fec2_term_t **x, bool *undetermined, uint8_t *mul, uint8_t *inv, uint16_t max_packet_length)
+{
+    sort_system(a, constant_terms, n_eq, n_unknowns);
+    for (int i = 0; i < n_eq - 1; ++i) {
+        for (int k = i + 1; k < n_eq; ++k) {
+            uint8_t mul_num = a[k][i];
+            uint8_t mul_den = a[i][i];
+            uint8_t term = gf256_mul(mul_num, inv[mul_den], mul);
+            for (int j = 0; j < n_unknowns; ++j) {
+                a[k][j] = gf256_sub(a[k][j], gf256_mul(term, a[i][j], mul));
+            }
+            symbol_sub_scaled_term(constant_terms[k], term, constant_terms[i], mul, max_packet_length);
+        }
+    }
+
+    sort_system(a, constant_terms, n_eq, n_unknowns);
+
+    for (int i = 0; i < n_eq - 1; ++i) {
+        int first_nz_id = first_non_zero_idx(a[i], n_unknowns);
+        if (first_nz_id == -1) {
+            break;
+        }
+        for (int j = first_nz_id + 1; j < n_unknowns && a[i][j] != 0; j++) {
+            for (int k = i + 1; k < n_eq; k++) {
+                int first_nz_id_below = first_non_zero_idx(a[k], n_unknowns);
+                if (j > first_nz_id_below) {
+                    break;
+                } else if (first_nz_id_below == j) {
+                    uint8_t term = gf256_mul(a[i][j], inv[a[k][j]], mul);
+                    for (int l = j; l < n_unknowns; l++) {
+                        a[i][l] = gf256_sub(a[i][l], gf256_mul(term, a[k][l], mul));
+                    }
+                    symbol_sub_scaled_term(constant_terms[i], term, constant_terms[k], mul, max_packet_length);
+                    break;
+                }
+            }
+        }
+    }
+
+    int candidate = n_unknowns - 1;
+    for (int i = n_eq - 1; i >= 0; --i) {
+        bool only_zeroes = true;
+        for (int j = 0; j < n_unknowns; ++j) {
+            if (a[i][j] != 0) {
+                only_zeroes = false;
+                break;
+            }
+        }
+        if (!only_zeroes) {
+            while (a[i][candidate] == 0 && candidate >= 0) {
+                undetermined[candidate--] = true;
+            }
+            if (candidate < 0) {
+                break;
+            }
+            // TODO: not optimal because of aliasing
+            x[candidate] = constant_terms[i]; // Simply pointer copy
+            for (int j = 0; j < candidate; ++j) {
+                if (a[i][j] != 0) {
+                    undetermined[candidate] = true;
+                    break;
+                }
+            }
+            for (int j = candidate + 1; j < n_unknowns; ++j) {
+                if (a[i][j] != 0) {
+                    if (undetermined[j]) {
+                        undetermined[candidate] = true;
+                    } else {
+                        symbol_sub_scaled_term(x[candidate], a[i][j], x[j], mul, max_packet_length);
+                        a[i][j] = 0;
+                    }
+                }
+            }
+            if (symbol_is_zero(x[candidate]->data, x[candidate]->length.data_length) || a[i][candidate] == 0) {
+                undetermined[candidate] = true;
+            } else if (!undetermined[candidate]) {
+                symbol_mul_term(x[candidate], inv[a[i][candidate]], mul, max_packet_length);
+                a[i][candidate] = gf256_mul(a[i][candidate], inv[a[i][candidate]], mul);
+            } 
+            candidate--;
+        }
+    }
+    if (candidate >= 0) {
+        memset(undetermined, true, (candidate + 1) * sizeof(bool));
+    }
+}
+
+WritablePacket *
+IP6SRv6FECDecode::recover_packet_fom_data(srv6_fec2_term_t *rec)
+{
+    // Create new packet for the recovered data
+    WritablePacket *p = Packet::make(rec->length.data_length);
+
+    // Copy the data from the buffer inside the new packet
+    // TODO: optimization: direclty un a Packet ?
+    memcpy(p->data(), rec->data, rec->length.data_length);
+
+    // Recover from varying fields
+    click_ip6 *ip6 = reinterpret_cast<click_ip6 *>(p->data());
+    click_ip6_sr *srv6 = reinterpret_cast<click_ip6_sr *>(p->data() + sizeof(click_ip6));
+    // 1. Detect the correct new next hop
+    uint32_t *dec_ip_32 = dec.data32();
+    bool found_next_sid = false;
+    struct in6_addr ip6_next;
+    int i = srv6->last_entry;
+    uint8_t *dec_8 = (uint8_t *)dec_ip_32;
+    while (i >= 0) {
+        uint16_t offset = sizeof(click_ip6_sr) + sizeof(struct in6_addr) * i;
+        memcpy(&ip6_next, ((uint8_t *)srv6) + offset, sizeof(struct in6_addr));
+        // Compare this SID with the decoder ID
+        uint32_t *in6_32 = (uint32_t *)ip6_next.s6_addr;
+        uint8_t *en_8 = (uint8_t *)&ip6_next;
+        if (dec_ip_32[0] == in6_32[0] && dec_ip_32[1] == in6_32[1] && dec_ip_32[2] == in6_32[2] && dec_ip_32[3] == in6_32[3]) {
+            found_next_sid = true;
+//            //click_chatter("I HAVE FOUND THE SID");
+            break;
+        }
+        --i;
+    }
+    if (!found_next_sid || i == 0) {
+        //click_chatter("Did not find the SID => error");
+        p->kill();
+        return 0;
+    }
+
+    --i; // Next segment is the next hop
+    uint16_t offset = sizeof(click_ip6_sr) + sizeof(struct in6_addr) * i;
+
+    // 2. Replace the destination address with the correct
+    memcpy(&ip6->ip6_dst, ((uint8_t *)srv6) + offset, sizeof(struct in6_addr));
+
+    // 3. Replace the Segment Left pointer
+    srv6->segment_left = i;
+
+    // 4. New hop limit
+    ip6->ip6_hlim = 51;
+
+    // 5. Remove possible ECN bits
+    ip6->ip6_vfc &= 0b11111100;
+
+    // 6. Compute the checksum
+    // TODO : utiliser un element si besoin
+
+    // 7. Set annotations
+    p->set_network_header((unsigned char*)ip6, sizeof(click_ip6) + sizeof(click_ip6_sr) + sizeof(struct in6_addr) * srv6->last_entry + 8 );
+
+    return p;
+}
+
+void
+IP6SRv6FECDecode::xor_recover_symbols(std::function<void(Packet*)>push)
+{
+    uint32_t esid = _rlc_info.encoding_symbol_id;
+    srv6_fec2_repair_t *repair = _rlc_info.repair_buffer[esid % SRV6_FEC_BUFFER_SIZE];
+    uint8_t window_size = repair->tlv.nss;
+    const click_ip6_sr *srv6 = reinterpret_cast<const click_ip6_sr *>(repair->p->data() + sizeof(click_ip6));
+    uint16_t repair_offset = sizeof(click_ip6) + sizeof(click_ip6_sr) + srv6->ip6_hdrlen * 8;
+    uint16_t max_packet_length = repair->p->length() - repair_offset;
+
+    // 1. Detect if we can recover a lost source symbol in the window
+    //    If there are more than one lost symbol in the window, we cannot recover it
+    //    Store them in a separate buffer for easier access
+    Packet *xor_buff[window_size];
+    uint8_t nb_source = 0;
+    bool lost_one_symbol = false;
+    uint32_t lost_esid = 0;
+    for (uint32_t i = 0; i < window_size; ++i) {
+        // Iterate from the end but XOR is commutative and associative
+        uint16_t idx = (esid - i) % SRV6_FEC_BUFFER_SIZE;
+        uint32_t theoric_esid = esid - i;
+        srv6_fec2_source_t *source = _rlc_info.source_buffer[idx];
+        srv6_fec2_source_t *rec = _rlc_info.recovd_buffer[idx];
+        if (source && source->encoding_symbol_id == theoric_esid) {
+            xor_buff[nb_source++] = source->p;
+        } else if (rec && rec->encoding_symbol_id == theoric_esid) {
+            // Maybe the symbol was recovered earlier in a previous window
+            xor_buff[nb_source++] = rec->p;
+        } else {
+            if (lost_one_symbol) {
+                return; // More than one lost symbol, cannot recover
+            }
+            lost_one_symbol = true;
+            lost_esid = theoric_esid;
+        }
+    }
+
+    if (!lost_one_symbol) {
+        return;
+    }
+
+    // 2. We know we have lost exactly one source symbol
+    //    We can recover it by XORing the repair and source symbols
+    srv6_fec2_term_t *rec = (srv6_fec2_term_t *)CLICK_LALLOC(sizeof(srv6_fec2_term_t));
+    uint8_t *data = (uint8_t *)CLICK_LALLOC(sizeof(uint8_t) * max_packet_length);
+    rec->length.coded_length = repair->tlv.coded_length;
+    rec->data = data;
+    // Copy data from the repair symbol
+    memcpy(data, repair->p->data() + repair_offset, max_packet_length);
+    for (uint32_t i = 0; i < nb_source; ++i) {
+        xor_one_symbol(rec, xor_buff[i]);
+    }
+
+    // 3. Send the recovered symbol and store it in the recovered buffer
+    //    Also make room if there was a previous recovered buffer
+    WritablePacket *p_rec = recover_packet_fom_data(rec);
+    if (!p_rec) {
+        //click_chatter("Error confirmed");
+        CLICK_LFREE(rec, sizeof(srv6_fec2_term_t));
+        return;
+    }
+    srv6_fec2_source_t *prev_rec = _rlc_info.recovd_buffer[lost_esid % SRV6_FEC_BUFFER_SIZE];
+    if (prev_rec) {
+        prev_rec->encoding_symbol_id = lost_esid;
+        prev_rec->p = p_rec->clone();
+    } else {
+        srv6_fec2_source_t *rec = (srv6_fec2_source_t *)CLICK_LALLOC(sizeof(srv6_fec2_source_t));
+        rec->encoding_symbol_id = lost_esid;
+        rec->p = p_rec->clone();
+        _rlc_info.recovd_buffer[lost_esid % SRV6_FEC_BUFFER_SIZE] = rec;
+    }
+
+    //click_chatter("No pas ici");
+
+    push(p_rec);
+    
+    CLICK_LFREE(data, sizeof(uint8_t) * max_packet_length);
+    CLICK_LFREE(rec, sizeof(srv6_fec2_term_t));
+}
+
+void
+IP6SRv6FECDecode::xor_one_symbol(srv6_fec2_term_t *rec, Packet *s)
+{
+    uint8_t *s_64 = (uint8_t *)s->data();
+    uint8_t *r_64 = (uint8_t *)rec->data;
+
+    for (uint16_t i = 0; i < s->length() / sizeof(uint8_t); ++i) {
+        // //click_chatter("XOR with source i=%u  %x, repair before=%x", i, s_64[i], r_64[i]);
+        r_64[i] ^= s_64[i];
+    }
+
+    // Also code the potential remaining data
+    uint8_t *s_8 = (uint8_t *)s->data();
+    uint8_t *r_8 = (uint8_t *)rec->data;
+    for (uint16_t i = (s->length() / sizeof(uint8_t)) * sizeof(uint8_t); i < s->length(); ++i) {
+        r_8[i] ^= s_8[i];
+    }
+
+    // Encode the packet length
+    rec->length.coded_length ^= s->length();
+}
+
+void
+IP6SRv6FECDecode::rlc_feedback()
+{
+    uint16_t packet_size = sizeof(click_ip6) + sizeof(click_ip6_sr) + 2 * sizeof(IP6Address) + sizeof(feedback_tlv_t);
+    WritablePacket *p = Packet::make(packet_size);
+    if (!p) {
+        return;
+    }
+
+    click_ip6 *ip6 = reinterpret_cast<click_ip6 *>(p->data());
+    click_ip6_sr *srv6 = reinterpret_cast<click_ip6_sr *>(p->data() + sizeof(click_ip6));
+    feedback_tlv_t *tlv = reinterpret_cast<feedback_tlv_t *>(p->data() + sizeof(click_ip6) + sizeof(click_ip6_sr) + sizeof(IP6Address) * 2);
+
+    // IPv6 Header
+    memcpy(&ip6->ip6_src, dec.data(), sizeof(IP6Address));
+    memcpy(&ip6->ip6_dst, enc.data(), sizeof(IP6Address));
+    ip6->ip6_flow = htonl(6 << IP6_V_SHIFT);
+    ip6->ip6_plen = htons(packet_size - sizeof(click_ip6));
+    ip6->ip6_nxt = IPPROTO_ROUTING;
+    ip6->ip6_hlim = 51;
+
+    // SRv6 Header
+    srv6->type = IP6PROTO_SEGMENT_ROUTING;
+    srv6->segment_left = 1;
+    srv6->last_entry = 1;
+    srv6->flags = 0;
+    srv6->tag = 0;
+    srv6->ip6_sr_next = 253;
+    srv6->ip6_hdrlen = (sizeof(feedback_tlv_t) + 2 * sizeof(IP6Address)) / 8;
+    memcpy(&srv6->segments[0], dec.data(), sizeof(IP6Address));
+    memcpy(&srv6->segments[1], enc.data(), sizeof(IP6Address));
+
+    // Add feedback TLV
+    tlv->type = TLV_TYPE_FEC_FEEDBACK;
+    tlv->len = sizeof(feedback_tlv_t) - 2;
+    tlv->nb_theoric = _rlc_feedback.last_received_esid - _rlc_feedback.esid_last_feedback;
+    //("esid=%u last_esid=%u nb_received=%u", _rlc_feedback.last_received_esid, _rlc_feedback.esid_last_feedback, _rlc_feedback.nb_received);
+    tlv->nb_lost = tlv->nb_theoric - _rlc_feedback.nb_received;
+    tlv->bit_string = _rlc_feedback.received_string;
+    tlv->padding = 0;
+
+    // Set annotations
+    p->set_network_header(p->data(), (unsigned char*)(tlv + 1) - p->data());
+
+    // Send packet with feedback
+    abort();
+    //In batch mode this will FAILç
+    output(1).push(p);
+
+    // Reset parameters for next feedback
+    _rlc_feedback.esid_last_feedback = _rlc_feedback.last_received_esid;
+    _rlc_feedback.nb_received = 0;
+    _rlc_feedback.received_string = 0;
+
+}
+
+CLICK_ENDDECLS
+EXPORT_ELEMENT(IP6SRv6FECDecode)
+ELEMENT_MT_SAFE(IP6SRv6FECDecode)
+
diff --git a/elements/ip6/ip6srv6fecdecode.hh b/elements/ip6/ip6srv6fecdecode.hh
new file mode 100644
index 0000000000..eff8e4d970
--- /dev/null
+++ b/elements/ip6/ip6srv6fecdecode.hh
@@ -0,0 +1,204 @@
+#ifndef CLICK_IP6SRv6FECDecode_HH
+#define CLICK_IP6SRv6FECDecode_HH
+#include <click/batchelement.hh>
+#include <click/glue.hh>
+#include <click/atomic.hh>
+#include <clicknet/ip6.h>
+#include <click/ip6address.hh>
+#include <tinymt32/tinymt32.h>
+#include <swifsymbol/swifsymbol.h>
+
+#define symbol_sub_scaled_term symbol_add_scaled_term
+
+#ifndef SRV6FEC_HH
+#define SRV6FEC_HH
+#define SRV6_FEC_BUFFER_SIZE 64
+
+#define TLV_TYPE_FEC_SOURCE 28
+#define TLV_TYPE_FEC_REPAIR 29
+#define TLV_TYPE_FEC_FEEDBACK 30
+#define RLC_MAX_WINDOWS 10
+#define LOCAL_MTU 1500
+
+// SRv6-FEC TLV structures
+struct source_tlv_t {
+  uint8_t type;
+  uint8_t len;
+  uint16_t padding;
+  uint32_t sfpid; // Source FEC Payload ID
+} CLICK_SIZE_PACKED_ATTRIBUTE;
+
+struct repair_tlv_t {
+  uint8_t type;
+  uint8_t len;
+  uint16_t padding;
+  uint32_t rfpid; // Repair FEC Payload ID
+  union {
+    struct {
+      uint8_t previous_window_size;
+      uint8_t window_step;
+      uint16_t repair_key;
+    } rlc_rfi;
+    uint32_t rfi;
+  }; // Repair FEC Info
+  uint16_t coded_length;
+  uint8_t nss; // Number Source Symbol
+  uint8_t nrs; // Number Repair Symbol
+} CLICK_SIZE_PACKED_ATTRIBUTE;
+
+struct feedback_tlv_t {
+  uint8_t type;
+  uint8_t len;
+  uint16_t nb_theoric;
+  uint16_t nb_lost;
+  uint16_t padding;
+  uint64_t bit_string;
+} CLICK_SIZE_PACKED_ATTRIBUTE;
+
+#endif
+
+CLICK_DECLS
+
+/*
+=c
+
+IP6SRv6FECDecode()
+
+=s ip
+
+Forward Erasure Correction for IPv6 Segment Routing
+
+=d
+
+=e
+
+
+  IP6SRv6FECDecode(fc00::a, fc00::9)
+
+=a IP6Encap */
+
+struct srv6_fec2_source_t {
+  Packet *p;
+  uint32_t encoding_symbol_id;
+};
+
+struct srv6_fec2_repair_t {
+  Packet *p;
+  repair_tlv_t tlv;
+};
+
+struct srv6_fec2_term_t {
+  uint8_t *data;
+  union {
+    uint16_t coded_length;
+    uint16_t data_length;
+  } length;
+};
+
+struct srv6_fec2_feedback_t {
+  uint64_t received_string;
+  uint32_t nb_received;
+  uint32_t esid_last_feedback;
+  uint32_t last_received_esid;
+};
+
+struct rlc_info_decoder_t {
+
+  // RLC relative information
+  tinymt32_t prng;
+  uint8_t muls[256 * 256 * sizeof(uint8_t)];
+  uint8_t table_inv[256 * sizeof(uint8_t)];
+  srv6_fec2_source_t *source_buffer[SRV6_FEC_BUFFER_SIZE];
+  srv6_fec2_repair_t *repair_buffer[SRV6_FEC_BUFFER_SIZE];
+  srv6_fec2_source_t *recovd_buffer[SRV6_FEC_BUFFER_SIZE];
+  uint32_t encoding_symbol_id;
+  uint32_t esid_last_feedback;
+};
+
+#define SRV6_RLC_MAX_SYMBOLS 32
+
+struct rlc_recover_utils_t {
+  srv6_fec2_source_t **ss_array;
+  srv6_fec2_repair_t **rs_array;
+  uint8_t *x_to_source;
+  uint8_t *source_to_x;
+  bool *protected_symbols;
+  uint8_t *coefs;
+  srv6_fec2_term_t **unknowns;
+  uint8_t **system_coefs;
+  srv6_fec2_term_t **constant_terms;
+  bool *undetermined;
+};
+
+#define SRV6_FEC_RLC 0
+#define SRV6_FEC_XOR 1
+
+class IP6SRv6FECDecode : public BatchElement { 
+ 
+ public:
+
+  IP6SRv6FECDecode();
+  ~IP6SRv6FECDecode();
+
+  const char *class_name() const override        { return "IP6SRv6FECDecode"; }
+  const char *port_count() const override        { return "1/2"; }
+
+  int  configure(Vector<String> &, ErrorHandler *) CLICK_COLD;
+  bool can_live_reconfigure() const     { return true; }
+  void add_handlers() CLICK_COLD;
+
+  void push(int, Packet *p_in) override;
+#if HAVE_BATCH
+  void push_batch(int, PacketBatch * batch_in) override;
+#endif
+
+ private:
+
+  IP6Address enc; // Encoder SID
+  IP6Address dec; // Decoder SID
+  bool _use_dst_anno;
+  bool _do_recover;
+  bool _do_fec;
+  rlc_info_decoder_t _rlc_info;
+  srv6_fec2_feedback_t _rlc_feedback;
+  rlc_recover_utils_t _rlc_utils;
+
+  static String read_handler(Element *, void *) CLICK_COLD;
+
+  void fec_framework(Packet *p_in, std::function<void(Packet*)>push);
+  void fec_framework(Packet *p_in);
+  int fec_scheme_source(Packet *p_in, source_tlv_t *tlv);
+  void fec_scheme_repair(Packet *p_in, repair_tlv_t *tlv, std::function<void(Packet*)>push);
+  WritablePacket *recover_packet_fom_data(srv6_fec2_term_t *rec);
+  srv6_fec2_term_t *init_term(Packet *p, uint16_t offset, uint16_t max_packet_length);
+  void kill_term(srv6_fec2_term_t *t);
+
+  void rlc_fill_muls(uint8_t muls[256 * 256]);
+
+  void store_source_symbol(Packet *p_in, source_tlv_t *tlv);
+  void store_repair_symbol(Packet *p_in, repair_tlv_t *tlv);
+  void remove_tlv_source_symbol(WritablePacket *p, uint16_t offset_tlv);
+
+  void rlc_recover_symbols(std::function<void(Packet*)>push);
+  void rlc_get_coefs(tinymt32_t *prng, uint32_t seed, int n, uint8_t *coefs);
+  void symbol_add_scaled_term(srv6_fec2_term_t *symbol1, uint8_t coef, srv6_fec2_source_t *symbol2, uint8_t *mul);
+  void symbol_add_scaled_term(srv6_fec2_term_t *symbol1, uint8_t coef, srv6_fec2_term_t *symbol2, uint8_t *mul, uint16_t decoding_size);
+  void symbol_mul_term(srv6_fec2_term_t *symbol1, uint8_t coef, uint8_t *mul, uint16_t size);
+
+  void swap(uint8_t **a, int i, int j);
+  void swap_b(srv6_fec2_term_t **a, int i, int j);
+  int cmp_eq_i(uint8_t *a, uint8_t *b, int idx, int n_unknowns);
+  int cmp_eq(uint8_t *a, uint8_t *b, int idx, int n_unknowns);
+  void sort_system(uint8_t **a, srv6_fec2_term_t **constant_terms, int n_eq, int n_unknowns);
+  int first_non_zero_idx(const uint8_t *a, int n_unknowns);
+  void gauss_elimination(int n_eq, int n_unknowns, uint8_t **a, srv6_fec2_term_t **constant_terms, srv6_fec2_term_t **x, bool *undetermined, uint8_t *mul, uint8_t *inv, uint16_t max_packet_length);
+
+  void xor_recover_symbols(std::function<void(Packet*)>push);
+  void xor_one_symbol(srv6_fec2_term_t *rec, Packet *s);
+
+  void rlc_feedback();
+};
+
+CLICK_ENDDECLS
+#endif
+
diff --git a/elements/ip6/lookupip6route.cc b/elements/ip6/lookupip6route.cc
index a42f07556e..94e9f42125 100644
--- a/elements/ip6/lookupip6route.cc
+++ b/elements/ip6/lookupip6route.cc
@@ -138,7 +138,7 @@ LookupIP6Route::classify(Packet *p)
     _last_addr = a;
     _last_gw = gw;
     _last_output = ifi;
-    if (gw != IP6Address("::0")) {
+    if (gw != IP6Address::make_zero()) {
 	SET_DST_IP6_ANNO(p, IP6Address(gw));
     }
     return ifi;
diff --git a/elements/test/packettest.cc b/elements/test/packettest.cc
index abd989a920..20cefde6b4 100644
--- a/elements/test/packettest.cc
+++ b/elements/test/packettest.cc
@@ -175,6 +175,31 @@ PacketTest::initialize(ErrorHandler *errh)
     CHECK_ALIGNED(p->data());
     p->kill();
 
+    PacketBatch* batch = PacketBatch::make_from_packet(Packet::make(1, lowers, 60, 2));
+    for (int i = 2; i <= 100; i++)
+        batch->append_packet(Packet::make(i, lowers, 60, 2));
+        int i = 0;
+        auto fnt = [](Packet *p_in, std::function<void(Packet*)>add){
+            int r = click_random() % 3;
+            if (r == 0) {
+                add(p_in->clone()->uniqueify());
+                p_in->kill();
+            } else if (r == 1) {
+                Packet* c = p_in->clone();
+                add(p_in->uniqueify());
+                c->kill();
+            } else {
+                add(p_in);
+            }
+    };
+    EXECUTE_FOR_EACH_PACKET_ADD(fnt, batch);
+    CHECK(batch->count() == 100);
+    CHECK(batch->first()->find_count() == 100);
+    i = 1;
+    FOR_EACH_PACKET(batch,p) {
+        CHECK(p->headroom() == i++);
+    }
+
     // Also check some packet header definition properties.
     union {
 	click_ip ip4;
diff --git a/elements/userlevel/fromdevice.cc b/elements/userlevel/fromdevice.cc
index b5d2c99fc9..92072a1cbb 100644
--- a/elements/userlevel/fromdevice.cc
+++ b/elements/userlevel/fromdevice.cc
@@ -463,7 +463,7 @@ FromDevice::initialize(ErrorHandler *errh)
     if (!_sniffer)
         if (KernelFilter::device_filter(_ifname, true, errh) < 0
 #if HAVE_IP6
-                && KernelFilter::device_filter6(_ifname, true, errh) < 0
+                || KernelFilter::device_filter6(_ifname, true, errh) < 0
 #endif
                 ) {
             _sniffer = true;
diff --git a/elements/userlevel/kernelfilter.cc b/elements/userlevel/kernelfilter.cc
index 946b639690..a11e9fa80c 100644
--- a/elements/userlevel/kernelfilter.cc
+++ b/elements/userlevel/kernelfilter.cc
@@ -110,7 +110,7 @@ KernelFilter::device_filter6(const String &devname, bool add_filter,
     if (iptables_command)
 	cmda << iptables_command;
     else if (access("/sbin/ip6tables", X_OK) == 0)
-	cmda << "/sbin/iptables";
+	cmda << "/sbin/ip6tables";
     else if (access("/usr/sbin/ip6tables", X_OK) == 0)
 	cmda << "/usr/sbin/ip6tables";
     else
diff --git a/etc/ron/divertsocket.cc b/etc/ron/divertsocket.cc
index 734a49de87..c116a7a15f 100644
--- a/etc/ron/divertsocket.cc
+++ b/etc/ron/divertsocket.cc
@@ -42,7 +42,7 @@
 # include <linux/types.h>
 # include <linux/icmp.h>
 
-# if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+# if HAVE_NETPACKET_PACKET_H && !HAVE_LINUX_IF_PACKET_H
 #  include <netpacket/packet.h>
 #  include <net/ethernet.h>
 # else
diff --git a/etc/ron/todevicenotify.cc b/etc/ron/todevicenotify.cc
index 25d82c88ce..b4a74dcfb3 100644
--- a/etc/ron/todevicenotify.cc
+++ b/etc/ron/todevicenotify.cc
@@ -40,7 +40,7 @@
 # include <net/if.h>
 # include <net/if_packet.h>
 # include <features.h>
-# if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+# if HAVE_NETPACKET_PACKET_H && !HAVE_LINUX_IF_PACKET_H
 #  include <netpacket/packet.h>
 # else
 #  include <linux/if_packet.h>
diff --git a/include/click/ip6address.hh b/include/click/ip6address.hh
index d562137484..e008eb7332 100644
--- a/include/click/ip6address.hh
+++ b/include/click/ip6address.hh
@@ -89,6 +89,9 @@ class IP6Address { public:
     int mask_to_prefix_len() const;
     inline bool matches_prefix(const IP6Address &addr, const IP6Address &mask) const;
     inline bool mask_as_specific(const IP6Address &) const;
+    static inline IP6Address make_zero() {
+	    return IP6Address();
+    }
 
     /** @brief Test if this address contains an embedded Ethernet address.
      *
diff --git a/include/click/packetbatch.hh b/include/click/packetbatch.hh
index 69146c89a5..4881910e23 100644
--- a/include/click/packetbatch.hh
+++ b/include/click/packetbatch.hh
@@ -203,6 +203,32 @@ CLICK_DECLS
         }\
 
 
+/**
+ * Execute a function for each packet, passing parameters to easily add multiple packets to the list
+ */
+#define EXECUTE_FOR_EACH_PACKET_ADD(fnt,batch) {\
+            Packet* next = ((batch != 0)? batch->first()->next() : 0 );\
+            Packet* p = batch->first();\
+            Packet* last = 0;\
+            int count = 0;\
+            for (;p != 0;p=next,next=(p==0?0:p->next())) {\
+                auto add = [&batch,&last,&count](Packet*q) {\
+                    if (last) { \
+                        last->set_next(q); \
+                    } else { \
+                        batch = reinterpret_cast<PacketBatch*>(q);\
+                    }\
+                    last = q;\
+                    count++;\
+                };\
+                fnt(p,add);\
+            }\
+            if (last) {\
+                batch->set_count(count);\
+                batch->set_tail(last);\
+            }\
+        }\
+
 /**
  * Split a batch into multiple batch according to a given function which will
  * give the index of an output to choose.
diff --git a/mininet/fastclick/Vagrantfile b/mininet/fastclick/Vagrantfile
index e886de8db9..2ab1e5f8b2 100644
--- a/mininet/fastclick/Vagrantfile
+++ b/mininet/fastclick/Vagrantfile
@@ -1,7 +1,8 @@
 $init = <<SCRIPT
   apt-get update
-  DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential fakeroot debhelper autoconf automake libssl-dev graphviz python-all libtool git tmux vim python3-pip python3-sphinx graphviz autoconf automake bzip2 debhelper dh-autoreconf libssl-dev libtool openssl procps python-all python-zopeinterface module-assistant dkms make libc6-dev python-argparse uuid-runtime netbase kmod  iproute2 openvswitch-switch libpcap-dev libnuma-dev libmicrohttpd-dev python3-pip python3-matplotlib htop wireshark-gtk python3 linux-tools-generic lynx gdb evince nginx libre2-dev openvswitch-switch
-  pip install alabaster
+  DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential fakeroot debhelper autoconf automake libssl-dev graphviz python-all python-qt4 python-twisted-conch libtool git tmux vim python-pip python-paramiko python-sphinx graphviz autoconf automake bzip2 debhelper dh-autoreconf libssl-dev libtool openssl procps python-all python-qt4 python-twisted-conch python-zopeinterface module-assistant dkms make libc6-dev python-argparse uuid-runtime netbase kmod python-twisted-web iproute2 ipsec-tools openvswitch-switch libpcap-dev libnuma-dev libmicrohttpd-dev python3-pip python3-matplotlib htop wireshark-gtk python3 linux-tools-generic lynx gdb evince nginx libre2-dev
+  python2 -m pip install alabaster
+  curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output /dev/stdout | python3
   python3 -m pip install --upgrade pip
 SCRIPT
 
@@ -16,10 +17,10 @@ $mininet = <<SCRIPT
 SCRIPT
 
 $dpdk = <<SCRIPT
-  wget http://fast.dpdk.org/rel/dpdk-19.11.1.tar.xz
-  tar xf dpdk-19.11.1.tar.xz
-  rm dpdk-19.11.1.tar.xz
-  pushd dpdk-stable-19.11.1
+  wget http://fast.dpdk.org/rel/dpdk-19.11.10.tar.xz
+  tar xf dpdk-19.11.10.tar.xz
+  rm dpdk-19.11.10.tar.xz
+  pushd dpdk-stable-19.11.10
   make config T=x86_64-native-linuxapp-gcc O=x86_64-native-linuxapp-gcc
   echo "export RTE_SDK=$(pwd)" >> ~/.profile
   echo "export RTE_TARGET=x86_64-native-linuxapp-gcc" >> ~/.profile
@@ -75,7 +76,8 @@ Vagrant.configure("2") do |config|
 	v.memory = 3072
   end
 
-  config.vm.synced_folder "../../conf", "/conf"
+  config.vm.synced_folder "../conf", "/conf"
+  config.vm.synced_folder "../", "/fastclick"
 
   ## Provisioning
   config.vm.provision :shell, :inline => $init
diff --git a/tools/click-devirtualize/cxxclass.cc b/tools/click-devirtualize/cxxclass.cc
index 9cdbb011a9..d41f213b50 100644
--- a/tools/click-devirtualize/cxxclass.cc
+++ b/tools/click-devirtualize/cxxclass.cc
@@ -23,69 +23,75 @@
 bool CxxFunction::parsing_header_file;
 
 CxxFunction::CxxFunction(const String &name, bool in_header,
-             const String &ret_type, const String &args,
-             const String &body, const String &clean_body)
-  : _name(name), _in_header(in_header), _from_header_file(parsing_header_file),
-    _alive(true), _ret_type(ret_type), _args(args),
-    _body(body), _clean_body(clean_body)
+                         const String &ret_type, const String &args,
+                         const String &body, const String &clean_body)
+    : _name(name), _in_header(in_header), _from_header_file(parsing_header_file),
+      _alive(true), _ret_type(ret_type), _args(args),
+      _body(body), _clean_body(clean_body)
 {
   //fprintf(stderr, "%s::%s\n", _name.c_str(), _body.c_str());
 }
 
 void CxxFunction::set_inline()
 {
-    if (_ret_type.find_left("inline"))
-        _ret_type = "inline "+_ret_type;
+  if (_ret_type.find_left("inline"))
+    _ret_type = "inline " + _ret_type;
 }
 
 String
 compile_pattern(const String &pattern0)
 {
-  static const char *three_tokens[] = { ">>=", "<<=", "->*", "::*", 0 };
-  static const char *two_tokens[] = { "++", "--", "+=", "-=", "*=", "/=", "->",
-                      "%=", "^=", "&=", "~=", "==", "!=",
-                      "&&", "||",
-                      ">=", "<=", "::", "<<", ">>", ".*", 0 };
+  static const char *three_tokens[] = {">>=", "<<=", "->*", "::*", 0};
+  static const char *two_tokens[] = {"++", "--", "+=", "-=", "*=", "/=", "->",
+                                     "%=", "^=", "&=", "~=", "==", "!=",
+                                     "&&", "||",
+                                     ">=", "<=", "::", "<<", ">>", ".*", 0};
 
   StringAccum sa;
   const char *s = pattern0.data();
   const char *end_s = s + pattern0.length();
 
-  while (s < end_s && isspace((unsigned char) *s)) // skip leading space
+  while (s < end_s && isspace((unsigned char)*s)) // skip leading space
     s++;
 
   // XXX not all constraints on patterns are expressed here
-  while (s < end_s) {
-    if (isspace((unsigned char) *s)) {
+  while (s < end_s)
+  {
+    if (isspace((unsigned char)*s))
+    {
       sa << ' ';
-      while (s < end_s && isspace((unsigned char) *s))
-    s++;
-
-    } else if (isalnum((unsigned char) *s) || *s == '_') {
-      while (s < end_s && (isalnum((unsigned char) *s) || *s == '_'))
-    sa << *s++;
+      while (s < end_s && isspace((unsigned char)*s))
+        s++;
+    }
+    else if (isalnum((unsigned char)*s) || *s == '_')
+    {
+      while (s < end_s && (isalnum((unsigned char)*s) || *s == '_'))
+        sa << *s++;
       sa << ' ';
-
-    } else if (*s == '#') {
-      assert(s < end_s - 1 && isdigit((unsigned char) s[1]));
+    }
+    else if (*s == '#')
+    {
+      assert(s < end_s - 1 && isdigit((unsigned char)s[1]));
       sa << s[0] << s[1];
       s += 2;
-
-    } else {
+    }
+    else
+    {
       const char *token = 0;
       if (s < end_s - 2)
-    for (int i = 0; !token && three_tokens[i]; i++)
-      if (strncmp(three_tokens[i], s, 3) == 0)
-        token = three_tokens[i];
+        for (int i = 0; !token && three_tokens[i]; i++)
+          if (strncmp(three_tokens[i], s, 3) == 0)
+            token = three_tokens[i];
       if (!token && s < end_s - 1)
-    for (int i = 0; !token && two_tokens[i]; i++)
-      if (strncmp(two_tokens[i], s, 2) == 0)
-        token = two_tokens[i];
+        for (int i = 0; !token && two_tokens[i]; i++)
+          if (strncmp(two_tokens[i], s, 2) == 0)
+            token = two_tokens[i];
       if (!token)
-    sa << *s++ << ' ';
-      else {
-    sa << token << ' ';
-    s += strlen(token);
+        sa << *s++ << ' ';
+      else
+      {
+        sa << token << ' ';
+        s += strlen(token);
       }
     }
   }
@@ -103,9 +109,8 @@ compile_pattern(const String &pattern0)
  *       be a match in "_n"
  *
  */
-bool
-CxxFunction::find_expr(const String &pattern, int *pos1, int *pos2,
-               int match_pos[10], int match_len[10], bool allow_call, bool full_symbol, int start_at, int stop_at) const
+bool CxxFunction::find_expr(const String &pattern, int *pos1, int *pos2,
+                            int match_pos[10], int match_len[10], bool allow_call, bool full_symbol, int start_at, int stop_at) const
 {
   const char *ps = pattern.data();
   int plen = pattern.length();
@@ -114,8 +119,9 @@ CxxFunction::find_expr(const String &pattern, int *pos1, int *pos2,
   int tpos = start_at;
   int tlen = _clean_body.length();
   if (stop_at == -1)
-      stop_at = tlen;
-  while (tpos < stop_at) {
+    stop_at = tlen;
+  while (tpos < stop_at)
+  {
 
     // fast loop: look for occurrences of first character in pattern
     while (tpos < stop_at && ts[tpos] != ps[0])
@@ -126,33 +132,42 @@ CxxFunction::find_expr(const String &pattern, int *pos1, int *pos2,
     //position in pattern
     int ppos = 1;
 
-    while (tpos < stop_at && ppos < plen) {
+    while (tpos < stop_at && ppos < plen)
+    {
 
-      if (isspace((unsigned char) ps[ppos])) {
-          if (ppos > 0 && (isalnum((unsigned char) ps[ppos-1]) || ps[ppos-1] == '_')
-              && (isalnum((unsigned char) ts[tpos]) || ts[tpos] == '_'))
+      if (isspace((unsigned char)ps[ppos]))
+      {
+        if (ppos > 0 && (isalnum((unsigned char)ps[ppos - 1]) || ps[ppos - 1] == '_') && (isalnum((unsigned char)ts[tpos]) || ts[tpos] == '_'))
           break;
-          while (tpos < stop_at && isspace((unsigned char) ts[tpos])) {
-              tpos++;
-          }
-          ppos++;
-      } else if (ps[ppos] == '#') {
+        while (tpos < stop_at && isspace((unsigned char)ts[tpos]))
+        {
+          tpos++;
+        }
+        ppos++;
+      }
+      else if (ps[ppos] == '#')
+      {
         // save expr and skip over it
         int paren_level = 0;
         int question_level = 0;
-        int which = ps[ppos+1] - '0';
+        int which = ps[ppos + 1] - '0';
         match_pos[which] = tpos;
-        while (tpos < stop_at) {
+        while (tpos < stop_at)
+        {
           if (ts[tpos] == '(')
             paren_level++;
-          else if (ts[tpos] == ')') {
+          else if (ts[tpos] == ')')
+          {
             if (paren_level == 0)
               break;
             paren_level--;
-          } else if (ts[tpos] == ',') {
+          }
+          else if (ts[tpos] == ',')
+          {
             if (paren_level == 0 && question_level == 0)
               break;
-          } else if (ts[tpos] == '?')
+          }
+          else if (ts[tpos] == '?')
             question_level++;
           else if (ts[tpos] == ':' && question_level)
             question_level--;
@@ -160,16 +175,20 @@ CxxFunction::find_expr(const String &pattern, int *pos1, int *pos2,
         }
         match_len[which] = tpos - match_pos[which];
         ppos += 2;
-      } else if (ps[ppos] == ts[tpos])
+      }
+      else if (ps[ppos] == ts[tpos])
         ppos++, tpos++;
       else
         break;
     }
 
-    if (ppos >= plen) {
+    if (ppos >= plen)
+    {
       // if full_symbol, check that the pattern was complete
-      if (full_symbol) {
-        if (tpos < stop_at && !(isspace(ts[tpos]) || (allow_call && ts[tpos] == '(') || ts[tpos] == ';' || ts[tpos] == ')')) {
+      if (full_symbol)
+      {
+        if (tpos < stop_at && !(isspace(ts[tpos]) || (allow_call && ts[tpos] == '(') || ts[tpos] == ';' || ts[tpos] == ')'))
+        {
           continue;
         }
       }
@@ -177,14 +196,12 @@ CxxFunction::find_expr(const String &pattern, int *pos1, int *pos2,
       // check that this pattern match didn't occur after some evil qualifier,
       // namely '.', '::', or '->'
       int p = tpos1 - 1;
-      while (p >= 0 && isspace((unsigned char) ts[p]))
-         p--;
+      while (p >= 0 && isspace((unsigned char)ts[p]))
+        p--;
       if (full_symbol && (isalnum(ts[p]) || (ts[p] == '_')))
         continue;
-      if (allow_call || p < 0
-      || (ts[p] != '.'
-          && (p == 0 || ts[p-1] != ':' || ts[p] != ':')
-          && (p == 0 || ts[p-1] != '-' || ts[p] != '>'))) {
+      if (allow_call || p < 0 || (ts[p] != '.' && (p == 0 || ts[p - 1] != ':' || ts[p] != ':') && (p == 0 || ts[p - 1] != '-' || ts[p] != '>')))
+      {
         *pos1 = tpos1;
         *pos2 = tpos;
         return true;
@@ -198,112 +215,136 @@ CxxFunction::find_expr(const String &pattern, int *pos1, int *pos2,
   return false;
 }
 
-bool
-CxxFunction::find_expr(const String &pattern) const
+bool CxxFunction::find_expr(const String &pattern) const
 {
   int pos1, pos2, match_pos[10], match_len[10];
   return find_expr(pattern, &pos1, &pos2, match_pos, match_len);
 }
 
-enum expr_type_t {EX_ASSIGNMENT,EX_PARAM,EX_VAL,EX_COMPARISON,EX_CALL,EX_OTHER};
-
-bool is_sign(char c) {
-    return c == '=' || c == '<' || c == '>' || c == '!';
+enum expr_type_t
+{
+  EX_ASSIGNMENT,
+  EX_PARAM,
+  EX_VAL,
+  EX_COMPARISON,
+  EX_CALL,
+  EX_OTHER
+};
+
+bool is_sign(char c)
+{
+  return c == '=' || c == '<' || c == '>' || c == '!';
 }
 
 /*
  * Get an approximate understanding of what a given expression is
  */
-expr_type_t expr_type(String fnt, int left, int right) {
-    while (1) {
-        char c  = fnt[left];
-        if (c == ';' || c == '{' || c == ')') {
-            left++;
-            break;
-        } else if (c == '(')
-            return EX_PARAM;
-        else if (c == '=') {
-            char b = fnt[left-1];
-            if (is_sign(b))
-                return EX_COMPARISON;
-            return EX_VAL;
-        }
-
-        left--;
-        if (left == 0)
-            break;
+expr_type_t expr_type(String fnt, int left, int right)
+{
+  while (1)
+  {
+    char c = fnt[left];
+    if (c == ';' || c == '{' || c == ')')
+    {
+      left++;
+      break;
+    }
+    else if (c == '(')
+      return EX_PARAM;
+    else if (c == '=')
+    {
+      char b = fnt[left - 1];
+      if (is_sign(b))
+        return EX_COMPARISON;
+      return EX_VAL;
     }
 
-    right -= 1;
-    //There is no comparison operator, or assigment operator on the left
-    while (1) {
-        right++;
-        if (right == fnt.length()) {
-            break;
-        }
-        char c = fnt[right];
-        if (c == ';' || c == '}') {
-            right --;
-            break;
-        } else if (c == ')')
-            return EX_PARAM;
-        else if (c == '(')
-            return EX_CALL;
-        else if (c == '=') {
-            char b = fnt[right + 1];
-            if (is_sign(b))
-                return EX_COMPARISON;
-            return EX_ASSIGNMENT;
-        }
+    left--;
+    if (left == 0)
+      break;
+  }
+
+  right -= 1;
+  //There is no comparison operator, or assigment operator on the left
+  while (1)
+  {
+    right++;
+    if (right == fnt.length())
+    {
+      break;
+    }
+    char c = fnt[right];
+    if (c == ';' || c == '}')
+    {
+      right--;
+      break;
+    }
+    else if (c == ')')
+      return EX_PARAM;
+    else if (c == '(')
+      return EX_CALL;
+    else if (c == '=')
+    {
+      char b = fnt[right + 1];
+      if (is_sign(b))
+        return EX_COMPARISON;
+      return EX_ASSIGNMENT;
     }
-    //There is no comparison operator on the right, no assigment operator, and not a fnt call
-    return EX_OTHER;
+  }
+  //There is no comparison operator on the right, no assigment operator, and not a fnt call
+  return EX_OTHER;
 }
 
 String
-CxxFunction::find_assignment(const String symbol, int stop_at) {
+CxxFunction::find_assignment(const String symbol, int stop_at)
+{
 
-    int n = 0;
-    int start_at = 0;
-    String val;
+  int n = 0;
+  int start_at = 0;
+  String val;
 
-    //click_chatter("Searching assignment for %s...", symbol.c_str());
+  //click_chatter("Searching assignment for %s...", symbol.c_str());
 again:
-    int pos1, pos2,pos3, match_pos[10], match_len[10];
-    if (!find_expr(symbol, &pos1, &pos2, match_pos, match_len, false, false, start_at, stop_at))
-        goto done;
-     pos3 = pos2;
-    if (expr_type(_clean_body, pos1, pos2) == EX_ASSIGNMENT) {
-        char c;
-        do {
-
-            c = _clean_body[pos2];
-            pos2++;
-        } while (c != '=');
-
-        int pos3 = pos2 + 1;
-        do {
-            c = _clean_body[pos3];
-            pos3++;
-        } while (c != ';' && c != '\n');
-
-        val = _body.substring(pos2, pos3 - pos2 - 1 ).trim();
-        //click_chatter("Found assignment for %s : '%s'", symbol.c_str(), val.c_str());
-    }
-    start_at = pos3;
-    n++;
-    goto again;
+  int pos1, pos2, pos3, match_pos[10], match_len[10];
+  if (!find_expr(symbol, &pos1, &pos2, match_pos, match_len, false, false, start_at, stop_at))
+    goto done;
+  pos3 = pos2;
+  if (expr_type(_clean_body, pos1, pos2) == EX_ASSIGNMENT)
+  {
+    char c;
+    do
+    {
+
+      c = _clean_body[pos2];
+      pos2++;
+    } while (c != '=');
+
+    int pos3 = pos2 + 1;
+    do
+    {
+      c = _clean_body[pos3];
+      pos3++;
+    } while (c != ';' && c != '\n');
+
+    val = _body.substring(pos2, pos3 - pos2 - 1).trim();
+    //click_chatter("Found assignment for %s : '%s'", symbol.c_str(), val.c_str());
+  }
+  start_at = pos3;
+  n++;
+  goto again;
 done:
-    if (n > 1) {
-        click_chatter("Multiple assignment found, ignoring");
-    } else if (n == 1){
-       return val;
-    }
-    return "";
+  if (n > 1)
+  {
+    click_chatter("Multiple assignment found, ignoring");
+  }
+  else if (n == 1)
+  {
+    return val;
+  }
+  return "";
 }
 
-bool
-CxxFunction::replace_expr(const String &pattern, const String &replacement, bool full_symbol, bool all, int start_at)
+bool CxxFunction::replace_expr(const String &pattern, const String &replacement, bool full_symbol, bool all, int start_at)
 {
   int pos1, pos2, match_pos[10], match_len[10];
   bool done = false;
@@ -312,138 +353,147 @@ CxxFunction::replace_expr(const String &pattern, const String &replacement, bool
   if (!find_expr(pattern, &pos1, &pos2, match_pos, match_len, false, full_symbol, start_at))
     return done;
   //XXX horrible bugfix
-  if (pos2 -pos1 == 1 && pattern.length() == 1)
-     if (_clean_body[pos1] != pattern[0])
-         return done;
+  if (pos2 - pos1 == 1 && pattern.length() == 1)
+    if (_clean_body[pos1] != pattern[0])
+      return done;
   done = true;
 
   start_at = pos2 + 1;
-  if (expr_type(_clean_body, pos1, pos2) == EX_ASSIGNMENT) {
-//     click_chatter("Replacement of %s avoided at %d because it's an assignment", pattern.c_str(), start_at);
-//     click_chatter("%s", _clean_body.c_str());
-//     click_chatter("-->%s", _clean_body.substring(start_at).c_str());
-  } else {
-      StringAccum sa, clean_sa;
-      const char *s = replacement.data();
-      const char *end_s = s + replacement.length();
-      while (s < end_s) {
-        if (*s == '#') {
-          assert(s < end_s - 1 && isdigit((unsigned char) s[1]));
-          int which = s[1] - '0';
-          sa << _body.substring(match_pos[which], match_len[which]);
-          clean_sa << _clean_body.substring(match_pos[which], match_len[which]);
-          s += 2;
-        } else {
-          sa << *s;
-          clean_sa << *s;
-          s++;
-        }
+  if (expr_type(_clean_body, pos1, pos2) == EX_ASSIGNMENT)
+  {
+    //     click_chatter("Replacement of %s avoided at %d because it's an assignment", pattern.c_str(), start_at);
+    //     click_chatter("%s", _clean_body.c_str());
+    //     click_chatter("-->%s", _clean_body.substring(start_at).c_str());
+  }
+  else
+  {
+    StringAccum sa, clean_sa;
+    const char *s = replacement.data();
+    const char *end_s = s + replacement.length();
+    while (s < end_s)
+    {
+      if (*s == '#')
+      {
+        assert(s < end_s - 1 && isdigit((unsigned char)s[1]));
+        int which = s[1] - '0';
+        sa << _body.substring(match_pos[which], match_len[which]);
+        clean_sa << _clean_body.substring(match_pos[which], match_len[which]);
+        s += 2;
       }
+      else
+      {
+        sa << *s;
+        clean_sa << *s;
+        s++;
+      }
+    }
 
-      String new_body =
+    String new_body =
         _body.substring(0, pos1) + sa.take_string() + _body.substring(pos2);
-      String new_clean_body =
-        _clean_body.substring(0, pos1) + clean_sa.take_string()
-        + _clean_body.substring(pos2);
-      _body = new_body;
-      _clean_body = new_clean_body;
+    String new_clean_body =
+        _clean_body.substring(0, pos1) + clean_sa.take_string() + _clean_body.substring(pos2);
+    _body = new_body;
+    _clean_body = new_clean_body;
   }
 
   if (all)
-      goto again;
+    goto again;
   //fprintf(stderr, ">>>>>> %s\n", _body.c_str());
   return true;
 }
 
-int
-CxxFunction::replace_call(const String &pattern, const String &replacement, Vector<String> &args) {
+int CxxFunction::replace_call(const String &pattern, const String &replacement, Vector<String> &args)
+{
 
-      int pos1, pos2, match_pos[10], match_len[10] = {-1};
-      if (!find_expr(pattern, &pos1, &pos2, match_pos, match_len, true, true))
-        return -1;
+  int pos1, pos2, match_pos[10], match_len[10] = {-1};
+  if (!find_expr(pattern, &pos1, &pos2, match_pos, match_len, true, true))
+    return -1;
 
-      int i = 0;
-      while (match_len[i] > -1) {
-          args.push_back(_body.substring(match_pos[i], match_len[i]));
-          i++;
-      }
+  int i = 0;
+  while (match_len[i] > -1)
+  {
+    args.push_back(_body.substring(match_pos[i], match_len[i]));
+    i++;
+  }
 
-      //click_chatter("Found %s pos %d pos %d match %d %d : %s",pattern.c_str(), pos1, pos2, match_pos[0], match_len[0], _body.substring(pos1, pos2 - pos1).c_str());
-      //fprintf(stderr, ":::::: %s\n", _body.c_str());
-
-      StringAccum sa, clean_sa;
-      const char *s = replacement.data();
-      const char *end_s = s + replacement.length();
-      while (s < end_s) {
-        if (*s == '#') {
-          assert(s < end_s - 1 && isdigit((unsigned char) s[1]));
-          int which = s[1] - '0';
-          sa << _body.substring(match_pos[which], match_len[which]);
-          clean_sa << _clean_body.substring(match_pos[which], match_len[which]);
-          s += 2;
-        } else {
-          sa << *s;
-          clean_sa << *s;
-          s++;
-        }
-      }
+  //click_chatter("Found %s pos %d pos %d match %d %d : %s",pattern.c_str(), pos1, pos2, match_pos[0], match_len[0], _body.substring(pos1, pos2 - pos1).c_str());
+  //fprintf(stderr, ":::::: %s\n", _body.c_str());
+
+  StringAccum sa, clean_sa;
+  const char *s = replacement.data();
+  const char *end_s = s + replacement.length();
+  while (s < end_s)
+  {
+    if (*s == '#')
+    {
+      assert(s < end_s - 1 && isdigit((unsigned char)s[1]));
+      int which = s[1] - '0';
+      sa << _body.substring(match_pos[which], match_len[which]);
+      clean_sa << _clean_body.substring(match_pos[which], match_len[which]);
+      s += 2;
+    }
+    else
+    {
+      sa << *s;
+      clean_sa << *s;
+      s++;
+    }
+  }
 
-      String new_body =
-        _body.substring(0, pos1) + sa.take_string() + _body.substring(pos2);
-      String new_clean_body =
-        _clean_body.substring(0, pos1) + clean_sa.take_string()
-        + _clean_body.substring(pos2);
-      _body = new_body;
-      _clean_body = new_clean_body;
-
-      //fprintf(stderr, ">>>>>> %s\n", _body.c_str());
-      return pos2;
-}
+  String new_body =
+      _body.substring(0, pos1) + sa.take_string() + _body.substring(pos2);
+  String new_clean_body =
+      _clean_body.substring(0, pos1) + clean_sa.take_string() + _clean_body.substring(pos2);
+  _body = new_body;
+  _clean_body = new_clean_body;
 
+  //fprintf(stderr, ">>>>>> %s\n", _body.c_str());
+  return pos2;
+}
 
 /*****
  * CxxClass
  **/
 
 CxxClass::CxxClass(const String &name)
-  : _name(name), _fn_map(-1)
+    : _name(name), _fn_map(-1)
 {
   //fprintf(stderr, "!!!!!%s\n", _name.c_str());
 }
 
-void
-CxxClass::add_parent(CxxClass *cxx,const String str)
+void CxxClass::add_parent(CxxClass *cxx, const String str)
 {
-    ParentalLink l;
-    l.parent = cxx;
-    l.template_params = str;
+  ParentalLink l;
+  l.parent = cxx;
+  l.template_params = str;
   _parents.push_back(l);
 }
 
 CxxFunction &
 CxxClass::defun(const CxxFunction &fn, const bool &rewrite)
 {
-    for (int i = 0; i < _functions.size(); i++) {
-        if (_functions[i].name() == fn.name()) {
-            _functions[i] = fn;
-              if (rewrite)
-               _should_rewrite[i] = true;
-            return _functions[i];
-        }
+  for (int i = 0; i < _functions.size(); i++)
+  {
+    if (_functions[i].name() == fn.name())
+    {
+      _functions[i] = fn;
+      if (rewrite)
+        _should_rewrite[i] = true;
+      return _functions[i];
     }
-    int which = _functions.size();
+  }
+  int which = _functions.size();
   _functions.push_back(fn);
   _fn_map.set(fn.name(), which);
   _functions.back().unkill();
-  if (_should_rewrite.size() < which+1)
+  if (_should_rewrite.size() < which + 1)
     _should_rewrite.resize(which + 1);
   if (rewrite)
-      _should_rewrite[which] = true;
+    _should_rewrite[which] = true;
   return _functions.back();
 }
 
-bool
-CxxClass::reach(int findex, Vector<int> &reached)
+bool CxxClass::reach(int findex, Vector<int> &reached)
 {
   if (findex < 0)
     return false;
@@ -458,7 +508,8 @@ CxxClass::reach(int findex, Vector<int> &reached)
   int len = clean_body.length();
   bool should_rewrite = _has_push[findex] || _has_pull[findex];
 
-  while (p < len) {
+  while (p < len)
+  {
 
     // look for a function call
     while (p < len && s[p] != '(')
@@ -467,47 +518,48 @@ CxxClass::reach(int findex, Vector<int> &reached)
       break;
 
     int paren_p = p;
-    for (p--; p >= 0 && isspace((unsigned char) s[p]); p--)
+    for (p--; p >= 0 && isspace((unsigned char)s[p]); p--)
       /* nada */;
-    if (p < 0 || (!isalnum((unsigned char) s[p]) && s[p] != '_')) {
+    if (p < 0 || (!isalnum((unsigned char)s[p]) && s[p] != '_'))
+    {
       p = paren_p + 1;
       continue;
     }
     int end_word_p = p + 1;
-    while (p >= 0 && (isalnum((unsigned char) s[p]) || s[p] == '_'))
+    while (p >= 0 && (isalnum((unsigned char)s[p]) || s[p] == '_'))
       p--;
     int start_word_p = p + 1;
-    while (p >= 0 && isspace((unsigned char) s[p]))
+    while (p >= 0 && isspace((unsigned char)s[p]))
       p--;
 
     // have found word; check that it is a direct call
-    if (p >= 0 && (s[p] == '.' || (p > 0 && s[p-1] == '-' && s[p] == '>')))
+    if (p >= 0 && (s[p] == '.' || (p > 0 && s[p - 1] == '-' && s[p] == '>')))
       /* do nothing; a call of some object */;
-    else {
+    else
+    {
       // XXX class-qualified?
       String name = clean_body.substring(start_word_p, end_word_p - start_word_p);
       int findex2 = _fn_map.get(name);
       if (findex2 >= 0 && reach(findex2, reached))
-    should_rewrite = true;
+        should_rewrite = true;
     }
 
     // skip past word
     p = paren_p + 1;
   }
 
-  if (!should_rewrite && !_functions[findex].from_header_file()) {
+  if (!should_rewrite && !_functions[findex].from_header_file())
+  {
     // might still be rewritable if it's inlined from the
     // .cc file, which we can't #include
     const String &ret_type = _functions[findex].ret_type();
     const char *s = ret_type.data();
     int len = ret_type.length();
     for (int p = 0; p < len - 6; p++)
-      if (s[p+0] == 'i' && s[p+1] == 'n' && s[p+2] == 'l'
-      && s[p+3] == 'i' && s[p+4] == 'n' && s[p+5] == 'e'
-      && (p == 0 || isspace((unsigned char) s[p-1]))
-      && (p == len-6 || isspace((unsigned char) s[p+6]))) {
-    should_rewrite = true;
-    break;
+      if (s[p + 0] == 'i' && s[p + 1] == 'n' && s[p + 2] == 'l' && s[p + 3] == 'i' && s[p + 4] == 'n' && s[p + 5] == 'e' && (p == 0 || isspace((unsigned char)s[p - 1])) && (p == len - 6 || isspace((unsigned char)s[p + 6])))
+      {
+        should_rewrite = true;
+        break;
       }
   }
 
@@ -535,15 +587,13 @@ CxxClass::find_should_rewrite()
   static String o_push_batch_pattern = compile_pattern("output_push_batch(#0,#1)");
   static String checked_push_batch_pattern = compile_pattern("checked_output_push_batch(#0,#1)");
 #endif
-  for (int i = 0; i < nfunctions(); i++) {
-    if (_functions[i].find_expr(push_pattern)
-    || _functions[i].find_expr(o_push_pattern)
+  for (int i = 0; i < nfunctions(); i++)
+  {
+    if (_functions[i].find_expr(push_pattern) || _functions[i].find_expr(o_push_pattern)
 #if HAVE_BATCH
-    || _functions[i].find_expr(push_batch_pattern)
-    || _functions[i].find_expr(o_push_batch_pattern)
-    || _functions[i].find_expr(checked_push_batch_pattern)
+        || _functions[i].find_expr(push_batch_pattern) || _functions[i].find_expr(o_push_batch_pattern) || _functions[i].find_expr(checked_push_batch_pattern)
 #endif
-    || _functions[i].find_expr(checked_push_pattern))
+        || _functions[i].find_expr(checked_push_pattern))
       _has_push[i] = 1;
     if (_functions[i].find_expr(pull_pattern))
       _has_pull[i] = 1;
@@ -559,77 +609,86 @@ CxxClass::find_should_rewrite()
   any |= reach(_fn_map.get("run_timer"), reached);
   any |= reach(_fn_map.get("selected"), reached);
   int simple_action = _fn_map.get("simple_action");
-  if (simple_action >= 0) {
+  if (simple_action >= 0)
+  {
     reach(simple_action, reached);
     _should_rewrite[simple_action] = any = true;
   }
 #if HAVE_BATCH
   int simple_action_batch = _fn_map.get("simple_action_batch");
-  if (simple_action_batch >= 0) {
+  if (simple_action_batch >= 0)
+  {
     reach(simple_action_batch, reached);
     _should_rewrite[simple_action_batch] = any = true;
   }
 #endif
-  if (_fn_map.get("devirtualize_all") >= 0) {
-    for (int i = 0; i < nfunctions(); i++) {
+  if (_fn_map.get("devirtualize_all") >= 0)
+  {
+    for (int i = 0; i < nfunctions(); i++)
+    {
       const String &n = _functions[i].name();
       if (n != _name && n[0] != '~')
-    _should_rewrite[i] = any = true;
+        _should_rewrite[i] = any = true;
     }
   }
 
   if (!any)
-      click_chatter("Shouldn't rewrite %s", name().c_str());
-  return any?REWRITE_YES:REWRITE_NO;
+    click_chatter("Shouldn't rewrite %s", name().c_str());
+  return any ? REWRITE_YES : REWRITE_NO;
 }
 
-void
-CxxClass::header_text(StringAccum &sa, int align=0) const
+void CxxClass::header_text(StringAccum &sa, int align = 0) const
 {
   sa << "class ";
   if (align)
-    sa << "alignas(" << align <<") ";
+    sa << "alignas(" << align << ") ";
   sa << _name;
-  if (_parents.size()) {
+  if (_parents.size())
+  {
     sa << " : ";
-    for (int i = 0; i < _parents.size(); i++) {
-      if (i) sa << ", ";
+    for (int i = 0; i < _parents.size(); i++)
+    {
+      if (i)
+        sa << ", ";
       sa << "public " << _parents[i].parent->name();
     }
   }
   sa << " {\n public:\n";
-  for (int i = 0; i < _functions.size(); i++) {
+  for (int i = 0; i < _functions.size(); i++)
+  {
     const CxxFunction &fn = _functions[i];
-    if (fn.alive()) {
+    if (fn.alive())
+    {
       sa << "  " << fn.ret_type() << " " << fn.name() << fn.args();
       if (fn.in_header())
-    sa << " {" << fn.body() << "}\n";
+        sa << " {" << fn.body() << "}\n";
       else
-    sa << ";\n";
+        sa << ";\n";
     }
   }
   sa << "};\n";
 }
 
-void
-CxxClass::source_text(StringAccum &sa) const
+void CxxClass::source_text(StringAccum &sa) const
 {
-  for (int i = 0; i < _functions.size(); i++) {
+  for (int i = 0; i < _functions.size(); i++)
+  {
     const CxxFunction &fn = _functions[i];
-    if (fn.alive() && !fn.in_header()) {
-      sa << fn.ret_type() << "\n" << _name << "::" << fn.name() << fn.args();
+    if (fn.alive() && !fn.in_header())
+    {
+      sa << fn.ret_type() << "\n"
+         << _name << "::" << fn.name() << fn.args();
       sa << "\n{" << fn.body() << "}\n";
     }
   }
 }
 
-
 /*****
  * CxxInfo
  **/
 
 CxxInfo::CxxInfo()
-  : _class_map(-1)
+    : _class_map(-1)
 {
 }
 
@@ -643,7 +702,8 @@ CxxClass *
 CxxInfo::make_class(const String &name)
 {
   int &which = _class_map[name];
-  if (which < 0) {
+  if (which < 0)
+  {
     which = _classes.size();
     _classes.push_back(new CxxClass(name));
   }
@@ -664,93 +724,105 @@ remove_crap(const String &original_text)
 
   char *if0_o_ptr = 0;
 
-  while (s < end_s) {
+  while (s < end_s)
+  {
     // read one line
 
     // skip spaces at beginning of line
-    while (s < end_s && isspace((unsigned char) *s))
+    while (s < end_s && isspace((unsigned char)*s))
       *o++ = *s++;
 
-    if (s >= end_s)        // end of data
+    if (s >= end_s) // end of data
       break;
 
-    if (*s == '#') {        // preprocessor directive
+    if (*s == '#')
+    { // preprocessor directive
       const char *first_s = s;
-      while (1) {
-    while (s < end_s && *s != '\n' && *s != '\r')
-      *o++ = ' ', s++;
-    bool backslash = (s[-1] == '\\');
-    while (s < end_s && (*s == '\n' || *s == '\r'))
-      *o++ = *s++;
-    if (!backslash)
-      break;
+      while (1)
+      {
+        while (s < end_s && *s != '\n' && *s != '\r')
+          *o++ = ' ', s++;
+        bool backslash = (s[-1] == '\\');
+        while (s < end_s && (*s == '\n' || *s == '\r'))
+          *o++ = *s++;
+        if (!backslash)
+          break;
       }
       // check for '#if 0 .. #endif'
       const char *ss = first_s + 1;
-      while (ss < s && isspace((unsigned char) *ss))
-    ss++;
-      if (ss < s - 5 && ss[0] == 'e' && ss[1] == 'n' && ss[2] == 'd'
-      && ss[3] == 'i' && ss[4] == 'f') {
-    if (if0_o_ptr)
-      while (if0_o_ptr < o)
-        *if0_o_ptr++ = ' ';
-    if0_o_ptr = 0;
-      } else if (ss < s - 3 && ss[0] == 'i' && ss[1] == 'f') {
-    for (ss += 2; ss < s && isspace((unsigned char) *ss); ss++) ;
-    if (ss < s && ss[0] == '0')
-      if0_o_ptr = o;
+      while (ss < s && isspace((unsigned char)*ss))
+        ss++;
+      if (ss < s - 5 && ss[0] == 'e' && ss[1] == 'n' && ss[2] == 'd' && ss[3] == 'i' && ss[4] == 'f')
+      {
+        if (if0_o_ptr)
+          while (if0_o_ptr < o)
+            *if0_o_ptr++ = ' ';
+        if0_o_ptr = 0;
+      }
+      else if (ss < s - 3 && ss[0] == 'i' && ss[1] == 'f')
+      {
+        for (ss += 2; ss < s && isspace((unsigned char)*ss); ss++)
+          ;
+        if (ss < s && ss[0] == '0')
+          if0_o_ptr = o;
       }
       continue;
     }
 
     // scan; stop at EOL, comment start, or literal start
-    while (s < end_s && *s != '\n' && *s != '\r') {
-
-      // copy chars
-      while (s < end_s && *s != '/' && *s != '\"' && *s != '\''
-         && *s != '\n' && *s != '\r')
-    *o++ = *s++;
-
-      if (s < end_s - 1 && *s == '/' && s[1] == '*') {
-    // slash-star comment
-    *o++ = ' ';
-    *o++ = ' ';
-    s += 2;
-    while (s < end_s && (*s != '*' || s >= end_s - 1 || s[1] != '/')) {
-      *o++ = (*s == '\n' || *s == '\r' ? *s : ' ');
-      s++;
-    }
-    if (s < end_s) {
-      *o++ = ' ';
-      *o++ = ' ';
-      s += 2;
-    }
-
-      } else if (s < end_s - 1 && *s == '/' && s[1] == '/') {
-    // slash-slash comment
-    *o++ = ' ';
-    *o++ = ' ';
-    s += 2;
     while (s < end_s && *s != '\n' && *s != '\r')
-      *o++ = ' ', s++;
-
-      } else if (*s == '\"' || *s == '\'') {
-    // literal
-    // XXX I am not sure why the closing quote,
-    // and any characters preceded by backslash, are turned into $.
-    char stopper = *s;
-    *o++ = ' ', s++;
-    while (s < end_s && *s != stopper) {
-      *o++ = ' ', s++;
-      if (s[-1] == '\\')
-        *o++ = '$', s++;
-    }
-    if (s < end_s)
-      *o++ = '$', s++;
+    {
 
-      } else if (*s != '\n' && *s != '\r')
-    // random other character, fine
-    *o++ = *s++;
+      // copy chars
+      while (s < end_s && *s != '/' && *s != '\"' && *s != '\'' && *s != '\n' && *s != '\r')
+        *o++ = *s++;
+
+      if (s < end_s - 1 && *s == '/' && s[1] == '*')
+      {
+        // slash-star comment
+        *o++ = ' ';
+        *o++ = ' ';
+        s += 2;
+        while (s < end_s && (*s != '*' || s >= end_s - 1 || s[1] != '/'))
+        {
+          *o++ = (*s == '\n' || *s == '\r' ? *s : ' ');
+          s++;
+        }
+        if (s < end_s)
+        {
+          *o++ = ' ';
+          *o++ = ' ';
+          s += 2;
+        }
+      }
+      else if (s < end_s - 1 && *s == '/' && s[1] == '/')
+      {
+        // slash-slash comment
+        *o++ = ' ';
+        *o++ = ' ';
+        s += 2;
+        while (s < end_s && *s != '\n' && *s != '\r')
+          *o++ = ' ', s++;
+      }
+      else if (*s == '\"' || *s == '\'')
+      {
+        // literal
+        // XXX I am not sure why the closing quote,
+        // and any characters preceded by backslash, are turned into $.
+        char stopper = *s;
+        *o++ = ' ', s++;
+        while (s < end_s && *s != stopper)
+        {
+          *o++ = ' ', s++;
+          if (s[-1] == '\\')
+            *o++ = '$', s++;
+        }
+        if (s < end_s)
+          *o++ = '$', s++;
+      }
+      else if (*s != '\n' && *s != '\r')
+        // random other character, fine
+        *o++ = *s++;
     }
 
     // copy EOL characters
@@ -767,12 +839,14 @@ skip_balanced_braces(const String &text, int p)
   const char *s = text.data();
   int len = text.length();
   int brace_level = 0;
-  while (p < len) {
+  while (p < len)
+  {
     if (s[p] == '{')
       brace_level++;
-    else if (s[p] == '}') {
+    else if (s[p] == '}')
+    {
       if (!--brace_level)
-    return p + 1;
+        return p + 1;
     }
     p++;
   }
@@ -785,12 +859,14 @@ skip_balanced_parens(const String &text, int p)
   const char *s = text.data();
   int len = text.length();
   int brace_level = 0;
-  while (p < len) {
+  while (p < len)
+  {
     if (s[p] == '(')
       brace_level++;
-    else if (s[p] == ')') {
+    else if (s[p] == ')')
+    {
       if (!--brace_level)
-    return p + 1;
+        return p + 1;
     }
     p++;
   }
@@ -800,31 +876,33 @@ skip_balanced_parens(const String &text, int p)
 /*
  * Parse functions but ignores declarations
  */
-int
-CxxInfo::parse_function_definition(const String &text, int fn_start_p,
-                   int paren_p, const String &original,
-                   CxxClass *cxx_class)
+int CxxInfo::parse_function_definition(const String &text, int fn_start_p,
+                                       int paren_p, const String &original,
+                                       CxxClass *cxx_class)
 {
   // find where we think open brace should be
   int p = skip_balanced_parens(text, paren_p);
   const char *s = text.data();
   int len = text.length();
-  while (p < len && isspace((unsigned char) s[p]))
+  while (p < len && isspace((unsigned char)s[p]))
     p++;
-  if (p < len - 5 && strncmp(s+p, "const", 5) == 0) {
-    for (p += 5; p < len && isspace((unsigned char) s[p]); p++)
+  if (p < len - 5 && strncmp(s + p, "const", 5) == 0)
+  {
+    for (p += 5; p < len && isspace((unsigned char)s[p]); p++)
       /* nada */;
   }
   // if open brace is not there, a function declaration or something similar;
   // return
-  if ( p < len + 5 && strncmp(s+p, "final", 5) == 0) {
-      p+= 5;
-     while (p < len && isspace((unsigned char) s[p]))
-        p++;
+  if (p < len + 5 && strncmp(s + p, "final", 5) == 0)
+  {
+    p += 5;
+    while (p < len && isspace((unsigned char)s[p]))
+      p++;
   }
-  if (p >= len || s[p] != '{') {
-      //click_chatter("No body :(");
-      return p;
+  if (p >= len || s[p] != '{')
+  {
+    //click_chatter("No body :(");
+    return p;
   }
 
   // save boundaries of function body
@@ -832,21 +910,22 @@ CxxInfo::parse_function_definition(const String &text, int fn_start_p,
   int close_brace_p = skip_balanced_braces(text, open_brace_p);
 
   // find arguments; cut space from end
-  for (p = open_brace_p - 1; p >= paren_p && isspace((unsigned char) s[p]); p--)
+  for (p = open_brace_p - 1; p >= paren_p && isspace((unsigned char)s[p]); p--)
     /* nada */;
   String args = original.substring(paren_p, p + 1 - paren_p);
 
   // find function name and class name
-  for (p = paren_p - 1; p > fn_start_p && isspace((unsigned char) s[p]); p--)
+  for (p = paren_p - 1; p > fn_start_p && isspace((unsigned char)s[p]); p--)
     /* nada */;
   int end_fn_name_p = p + 1;
-  while (p >= fn_start_p && (isalnum((unsigned char) s[p]) || s[p] == '_' || s[p] == '~'))
+  while (p >= fn_start_p && (isalnum((unsigned char)s[p]) || s[p] == '_' || s[p] == '~'))
     p--;
   String fn_name = original.substring(p + 1, end_fn_name_p - (p + 1));
   String class_name;
-  if (p >= fn_start_p + 2 && s[p] == ':' && s[p-1] == ':') {
+  if (p >= fn_start_p + 2 && s[p] == ':' && s[p - 1] == ':')
+  {
     int end_class_name_p = p - 1;
-    for (p -= 2; p >= fn_start_p && (isalnum((unsigned char) s[p]) || s[p] == '_' || s[p] == '~'); p--)
+    for (p -= 2; p >= fn_start_p && (isalnum((unsigned char)s[p]) || s[p] == '_' || s[p] == '~'); p--)
       /* nada */;
     if (p > fn_start_p && s[p] == ':') // nested class fns uninteresting
       return close_brace_p;
@@ -855,29 +934,30 @@ CxxInfo::parse_function_definition(const String &text, int fn_start_p,
   }
 
   // find return type; skip access control declarations, cut space from end
-  while (1) {
+  while (1)
+  {
     int access_p;
-    if (p >= fn_start_p + 6 && memcmp(s+fn_start_p, "public", 6) == 0)
+    if (p >= fn_start_p + 6 && memcmp(s + fn_start_p, "public", 6) == 0)
       access_p = fn_start_p + 6;
-    else if (p >= fn_start_p + 7 && memcmp(s+fn_start_p, "private", 7) == 0)
+    else if (p >= fn_start_p + 7 && memcmp(s + fn_start_p, "private", 7) == 0)
       access_p = fn_start_p + 7;
-    else if (p >= fn_start_p + 9 && memcmp(s+fn_start_p, "protected", 9) == 0)
+    else if (p >= fn_start_p + 9 && memcmp(s + fn_start_p, "protected", 9) == 0)
       access_p = fn_start_p + 9;
-    else if (p >= fn_start_p + 11 && memcmp(s+fn_start_p, "CLICK_DECLS", 11) == 0)
+    else if (p >= fn_start_p + 11 && memcmp(s + fn_start_p, "CLICK_DECLS", 11) == 0)
       access_p = fn_start_p + 11;
-    else if (p >= fn_start_p + 14 && memcmp(s+fn_start_p, "CLICK_ENDDECLS", 14) == 0)
+    else if (p >= fn_start_p + 14 && memcmp(s + fn_start_p, "CLICK_ENDDECLS", 14) == 0)
       access_p = fn_start_p + 14;
     else
       break;
-    while (access_p < p && isspace((unsigned char) s[access_p]))
+    while (access_p < p && isspace((unsigned char)s[access_p]))
       access_p++;
     if (access_p < p && s[access_p] == ':')
       access_p++;
-    for (; access_p < p && isspace((unsigned char) s[access_p]); access_p++)
+    for (; access_p < p && isspace((unsigned char)s[access_p]); access_p++)
       /* nada */;
     fn_start_p = access_p;
   }
-  while (p >= fn_start_p && isspace((unsigned char) s[p]))
+  while (p >= fn_start_p && isspace((unsigned char)s[p]))
     p--;
   String ret_type = original.substring(fn_start_p, p + 1 - fn_start_p);
 
@@ -889,72 +969,80 @@ CxxInfo::parse_function_definition(const String &text, int fn_start_p,
     relevant_class = cxx_class;
 
   // define function
-  if (relevant_class) {
+  if (relevant_class)
+  {
     int body_pos = open_brace_p + 1;
     int body_len = close_brace_p - 1 - body_pos;
-    relevant_class->defun
-      (CxxFunction(fn_name, !class_name, ret_type, args,
-           original.substring(body_pos, body_len),
-           text.substring(body_pos, body_len)));
-  } else {
-      //click_chatter("Not relevant:(");
+    relevant_class->defun(CxxFunction(fn_name, !class_name, ret_type, args,
+                                      original.substring(body_pos, body_len),
+                                      text.substring(body_pos, body_len)));
+  }
+  else
+  {
+    //click_chatter("Not relevant:(");
   }
   // done
   return close_brace_p;
 }
 
-int parse_reentrant(String s, int p, char b, char e, int len) {
-    int n = 0;
-    while(1) {
-        p++;
-        if (p == len) {
-            return p;
-        }
-        if (s[p] == b)
-            n++;
-        if (s[p] == e) {
-            n--;
-            if (n == 0) return p +1;
-        }
+int parse_reentrant(String s, int p, char b, char e, int len)
+{
+  int n = 0;
+  while (1)
+  {
+    p++;
+    if (p == len)
+    {
+      return p;
     }
-
+    if (s[p] == b)
+      n++;
+    if (s[p] == e)
+    {
+      n--;
+      if (n == 0)
+        return p + 1;
+    }
+  }
 }
-int
-CxxInfo::parse_class_definition(const String &text, int p,
-                const String &original, CxxClass * &cxxc)
+int CxxInfo::parse_class_definition(const String &text, int p,
+                                    const String &original, CxxClass *&cxxc)
 {
   // find class name
   const char *s = text.data();
   int len = text.length();
-  while (p < len && isspace((unsigned char) s[p]))
+  while (p < len && isspace((unsigned char)s[p]))
     p++;
   int name_start_p = p;
-  while (p < len && (isalnum((unsigned char) s[p]) || s[p] == '_'))
+  while (p < len && (isalnum((unsigned char)s[p]) || s[p] == '_'))
     p++;
   String class_name = original.substring(name_start_p, p - name_start_p);
   cxxc = make_class(class_name);
 
   // parse superclasses
-  while (p < len && s[p] != '{') {
-    while (p < len && s[p] != '{' && !isalnum((unsigned char) s[p]) && s[p] != '_')
+  while (p < len && s[p] != '{')
+  {
+    while (p < len && s[p] != '{' && !isalnum((unsigned char)s[p]) && s[p] != '_')
       p++;
     int p1 = p;
-    while (p < len && (isalnum((unsigned char) s[p]) || s[p] == '_'))
+    while (p < len && (isalnum((unsigned char)s[p]) || s[p] == '_'))
       p++;
-    if (p > p1 && (p != p1 + 6 || strncmp(s+p1, "public", 6) != 0)) {
+    if (p > p1 && (p != p1 + 6 || strncmp(s + p1, "public", 6) != 0))
+    {
       // XXX private or protected inheritance?
-     //click_chatter("Parent %s", original.substring(p1, p - p1).c_str());
+      //click_chatter("Parent %s", original.substring(p1, p - p1).c_str());
       CxxClass *parent = make_class(original.substring(p1, p - p1));
-    //Parse template parameters
+      //Parse template parameters
       String params = "";
-      if (s[p] == '<') {
-          int e = parse_reentrant(s, p -1 , '<','>', len);
-          if (e >= len)
-              click_chatter("Malformed template parameters!?");
-
-          params = original.substring(p + 1, e - p - 2 );
-          click_chatter("Template params '%s'", params.c_str());
-          p = e + 1;
+      if (s[p] == '<')
+      {
+        int e = parse_reentrant(s, p - 1, '<', '>', len);
+        if (e >= len)
+          click_chatter("Malformed template parameters!?");
+
+        params = original.substring(p + 1, e - p - 2);
+        click_chatter("Template params '%s'", params.c_str());
+        p = e + 1;
       }
 
       cxxc->add_parent(parent, params);
@@ -967,202 +1055,267 @@ CxxInfo::parse_class_definition(const String &text, int p,
   return c;
 }
 
-int
-CxxInfo::parse_class(const String &text, int p, const String &original,
-             CxxClass *cxx_class)
+int CxxInfo::parse_class(const String &text, int p, const String &original,
+                         CxxClass *cxx_class)
 {
   // parse clean_text
   const char *s = text.data();
   int len = text.length();
-//click_chatter("Parsing class at %d [%c]",p,text[p]);
-  while (1) {
+  //click_chatter("Parsing class at %d [%c]",p,text[p]);
+
+  while (1)
+  {
 
     // find first batch
-    while (p < len && isspace((unsigned char) s[p]))
+    while (p < len && isspace((unsigned char)s[p]))
       p++;
     int p1 = p;
     while (p < len && s[p] != ';' && s[p] != '(' && s[p] != '{' &&
-       s[p] != '}')
+           s[p] != '}')
       p++;
 
     //fprintf(stderr, "   %d %c\n", p, s[p]);
-    if (p >= len) {
+    if (p >= len)
+    {
       return len;
     }
-    else if (s[p] == ';') {
+    else if (s[p] == ';')
+    {
       // uninteresting
       p++;
       continue;
-    } else if (s[p] == '}') {
+    }
+    else if (s[p] == '}')
+    {
       //fprintf(stderr, " end of class at %d !!!!!!/\n",p+1);
       return p + 1;
-    } else if (s[p] == '{') {
+    }
+    else if (s[p] == '{')
+    {
 
-        CxxClass* child_cxx = 0;
-      if (p > p1 + 6 && !cxx_class
-      && (strncmp(s+p1, "class", 5) == 0
-          || strncmp(s+p1, "struct", 6) == 0)) {
+      CxxClass *child_cxx = 0;
+      if (p > p1 + 6 && !cxx_class && (strncmp(s + p1, "class", 5) == 0 || strncmp(s + p1, "struct", 6) == 0))
+      {
         // parse class definition
-            //click_chatter("Parsing definition at %d of %s", p1 + 6, text.substring(p1+6, 20).c_str());
+        //click_chatter("Parsing definition at %d of %s", p1 + 6, text.substring(p1+6, 20).c_str());
         p = parse_class_definition(text, p1 + 6, original, child_cxx);
-            //      click_chatter("Subclass %s", text.substring(p1+6,p-p1-6).c_str() );
-      } else if (p > p1 + 8 && !cxx_class
-      && (strncmp(s+p1, "template", 8) == 0)) {
+        //      click_chatter("Subclass %s", text.substring(p1+6,p-p1-6).c_str() );
+      }
+      else if (p > p1 + 8 && !cxx_class && (strncmp(s + p1, "template", 8) == 0))
+      {
 
         //click_chatter("Parsing template definition at %d of %s", p1, text.substring(p1, text.substring(p1).find_left('\n')).c_str());
-        int p2 = p1+8;
+        int p2 = p1 + 8;
 
-        while (p2 < len && isspace((unsigned char) s[p2])) p2++;
+        while (p2 < len && isspace((unsigned char)s[p2]))
+          p2++;
         p1 = p2;
-        p2 = parse_reentrant(s, p2 - 1 , '<','>', len);
-        String tmpl =  text.substring(p1 + 1, p2 - p1 -2);
+        p2 = parse_reentrant(s, p2 - 1, '<', '>', len);
+        String tmpl = text.substring(p1 + 1, p2 - p1 - 2);
         p1 = p2;
-        while (p1 < len && isspace((unsigned char) s[p1])) p1++;
+        while (p1 < len && isspace((unsigned char)s[p1]))
+          p1++;
         p = parse_class_definition(s, p1 + 6, original, child_cxx);
-        if (child_cxx) {
-            //click_chatter("Class %s had tmpl param %s", child_cxx->name().c_str(), tmpl.c_str());
-            child_cxx->set_template(tmpl);;
+        if (child_cxx)
+        {
+          //click_chatter("Class %s had tmpl param %s", child_cxx->name().c_str(), tmpl.c_str());
+          child_cxx->set_template(tmpl);
+          ;
         }
 
-            //      click_chatter("Subclass %s", text.substring(p1+6,p-p1-6).c_str() );
-      } else
+        //      click_chatter("Subclass %s", text.substring(p1+6,p-p1-6).c_str() );
+      }
+      else
 
-    p = skip_balanced_braces(text, p);
-    } else if (s[p] == '(') {
-        //click_chatter("Parse fun %s",text.substring(p1,p-p1).c_str());
+        p = skip_balanced_braces(text, p);
+    }
+    else if (s[p] == '(')
+    {
+      //click_chatter("Parse fun %s",text.substring(p1,p-p1).c_str());
       p = parse_function_definition(text, p1, p, original, cxx_class);
     }
+  }
+}
 
+bool has_meta(String meta, const char* s, int &p_in, int len) {
+  int p = p_in;
+  if (p < len && s[p] == '#') {
+    click_chatter("meta!");
+    p++;
+    while (p < len && isspace((unsigned char)s[p]))
+        p++;
+    if (p + meta.length() < len && memcmp(s + p, meta.data(), meta.length()) == 0 ) {
+      p_in = p + meta.length();
+      return true;
+    }
   }
+  return false;
 }
 
-void
-CxxInfo::parse_file(const String &original_text, bool header,
-            String *store_includes)
+void CxxInfo::parse_file(const String &original_text, bool header,
+                         String *store_includes)
 {
   String clean_text = remove_crap(original_text);
   CxxFunction::parsing_header_file = header;
   parse_class(clean_text, 0, original_text, 0);
+  //click_chatter("%s", original_text.c_str());
 
   // save initial comments and #defines and #includes for replication.
   // Also skip over 'CLICK_CXX_whatever', enum definitions, typedefs,
   // and 'extern "C" { }' blocks enclosing headers only.
   // XXX Should save up to an arbitrary comment or something
-  if (store_includes) {
+  if (store_includes)
+  {
     const char *s = clean_text.data();
+    const char *so = original_text.data();
     int p = 0;
     int len = clean_text.length();
-    while (1) {
-      while (p < len && isspace((unsigned char) s[p]))
-    p++;
-
-      if (p < len && s[p] == ';') {
-    // mop up stray semicolons
-    p++;
-
-      } else if (p + 7 < len && memcmp(s + p, "extern", 6) == 0
-         && isspace((unsigned char) s[p+6])) {
-    // include 'extern ["C"] { -HEADERS- }'
-    int p1 = p + 6;
-    while (p1 < len && (isspace((unsigned char) s[p1]) || s[p1] == '$'))
-      p1++;
-    if (p1 >= len || s[p1] != '{')
-      break;
-    for (p1++; p1 < len && isspace((unsigned char) s[p1]); p1++)
-      /* nada */;
-    if (p1 >= len || s[p1] != '}')
-      break;
-    p = p1 + 1;
-
-      } else if (p + 5 < len && memcmp(s + p, "enum", 4) == 0
-         && isspace((unsigned char) s[p+4])) {
-    // include 'enum [IDENTIFIER] { ... }'
-    int p1 = p + 5;
-    while (p1 < len && isspace((unsigned char) s[p1]))
-      p1++;
-    if (p1 < len && (isalnum((unsigned char) s[p1]) || s[p1] == '_')) {
-      while (p1 < len && (isalnum((unsigned char) s[p1]) || s[p1] == '_'))
-        p1++;
-      while (p1 < len && isspace((unsigned char) s[p1]))
-        p1++;
-    }
-    if (p1 >= len || s[p1] != '{')
-      break;
-    for (p1++; p1 < len && s[p1] != '}'; p1++)
-      /* nada */;
-    if (p1 >= len)
-      break;
-    p = p1 + 1;
-
-      } else if (p + 8 < len && memcmp(s + p, "typedef", 7) == 0
-         && isspace((unsigned char) s[p+7])) {
-    // include typedefs
-    for (p += 8; p < len && s[p] != ';'; p++)
-      /* nada */;
+    while (1)
+    {
+      while (p < len && isspace((unsigned char)so[p]))
+        p++;
 
-      } else if (p + 9 < len && memcmp(s + p, "CLICK_CXX", 9) == 0) {
-    // include 'CLICK_CXX' (used in <click/cxxprotect.h>)
-    for (p += 9; p < len && (isalnum((unsigned char) s[p]) || s[p] == '_'); p++)
-      /* nada */;
+      if (has_meta("if",so,p,len))
+      {
+        click_chatter("%d:if",p);
+        int count = 1;
+        while(p < len) {
+          if(has_meta("if",so,p,len)) {
+            count++;
+          } else if (has_meta("endif",so,p,len)) {
+            if (--count == 0)
+              break;
+          } else
+            p++;
+        }
+        continue;
+      } else {
+        if (p < len && isspace((unsigned char)s[p])) {
+          p++;
+          continue;
+        }
+      }
 
-      } else
-    break;
+      if (p < len && s[p] == ';')
+      {
+        // mop up stray semicolons
+        p++;
+      }
+      else if (p + 7 < len && memcmp(s + p, "extern", 6) == 0 && isspace((unsigned char)s[p + 6]))
+      {
+        // include 'extern ["C"] { -HEADERS- }'
+        int p1 = p + 6;
+        while (p1 < len && (isspace((unsigned char)s[p1]) || s[p1] == '$'))
+          p1++;
+        if (p1 >= len || s[p1] != '{')
+          break;
+        for (p1++; p1 < len && isspace((unsigned char)s[p1]); p1++)
+          /* nada */;
+        if (p1 >= len || s[p1] != '}')
+          break;
+        p = p1 + 1;
+      }
+      else if (p + 5 < len && memcmp(s + p, "enum", 4) == 0 && isspace((unsigned char)s[p + 4]))
+      {
+        // include 'enum [IDENTIFIER] { ... }'
+        int p1 = p + 5;
+        while (p1 < len && isspace((unsigned char)s[p1]))
+          p1++;
+        if (p1 < len && (isalnum((unsigned char)s[p1]) || s[p1] == '_'))
+        {
+          while (p1 < len && (isalnum((unsigned char)s[p1]) || s[p1] == '_'))
+            p1++;
+          while (p1 < len && isspace((unsigned char)s[p1]))
+            p1++;
+        }
+        if (p1 >= len || s[p1] != '{')
+          break;
+        for (p1++; p1 < len && s[p1] != '}'; p1++)
+          /* nada */;
+        if (p1 >= len)
+          break;
+        p = p1 + 1;
+      }
+      else if (p + 8 < len && memcmp(s + p, "typedef", 7) == 0 && isspace((unsigned char)s[p + 7]))
+      {
+        // include typedefs
+        for (p += 8; p < len && s[p] != ';'; p++)
+          /* nada */;
+      }
+      else if (p + 9 < len && memcmp(s + p, "CLICK_CXX", 9) == 0)
+      {
+        // include 'CLICK_CXX' (used in <click/cxxprotect.h>)
+        for (p += 9; p < len && (isalnum((unsigned char)s[p]) || s[p] == '_'); p++)
+          /* nada */;
+      }
+      else
+        break;
     }
     *store_includes = original_text.substring(0, p);
   }
 }
 
-void CxxClass::print_function_list() {
-      click_chatter("Function list:");
-      auto begin = _fn_map.begin();
-      auto end = _fn_map.end();
-      while (begin != end) {
-          click_chatter("FN[%s] %s", (*begin).first.c_str(), _functions[(*begin).second].name().c_str());
-          begin++;
-      }
-      for (int i = 0; i < _functions.size(); i++) {
-          click_chatter("FN[%d] %s", i, _functions[i].name().c_str());
-      }
+void CxxClass::print_function_list()
+{
+  click_chatter("Function list:");
+  auto begin = _fn_map.begin();
+  auto end = _fn_map.end();
+  while (begin != end)
+  {
+    click_chatter("FN[%s] %s", (*begin).first.c_str(), _functions[(*begin).second].name().c_str());
+    begin++;
+  }
+  for (int i = 0; i < _functions.size(); i++)
+  {
+    click_chatter("FN[%d] %s", i, _functions[i].name().c_str());
+  }
 }
 
-CxxFunction*
-CxxClass::find_in_parent(const String& name, const String& outer_class) {
-    for (int i = 0; i < nparents(); i++) {
-        /*
+CxxFunction *
+CxxClass::find_in_parent(const String &name, const String &outer_class)
+{
+  for (int i = 0; i < nparents(); i++)
+  {
+    /*
         click_chatter("Searching %s in %s", name.c_str(), parent(i)->name().c_str());
         parent(i)->print_function_list();
         */
-        CxxFunction* f = parent(i)->find(name);
-        if (!f)
-            f = parent(i)->find_in_parent(name, outer_class);
-        if (f) {
-            if (parent(i)->_template) {
-                String s = parent_tmpl(i);
-                if (!s) {
-                    click_chatter("Template parent without template parameters...");
-                    return 0;
-                }
-
-                Vector<String> args = parent(i)->_template.split(',');
-                Vector<String> vals = s.split(',');
-
-                CxxFunction* c = new CxxFunction(*f);
-                for (int i = 0; i < args.size(); i++) {
-                    String arg = args[i].trim();
-                    arg = arg.substring(arg.find_left(' ') + 1);
-                    String val = vals[i].trim();
-                    if (val == this->name()) {
-                        //click_chatter("CRTP detected, replacing with uttermost");
-                        val = outer_class;
-                    }
-                    //click_chatter("Replacing %s with %s",arg.c_str(),val.c_str());
-                    c->replace_expr(arg,val,true,true);
-                }
-                return c;
-            }
-
-            return f;
+    CxxFunction *f = parent(i)->find(name);
+    if (!f)
+      f = parent(i)->find_in_parent(name, outer_class);
+    if (f)
+    {
+      if (parent(i)->_template)
+      {
+        String s = parent_tmpl(i);
+        if (!s)
+        {
+          click_chatter("Template parent without template parameters...");
+          return 0;
+        }
+
+        Vector<String> args = parent(i)->_template.split(',');
+        Vector<String> vals = s.split(',');
+
+        CxxFunction *c = new CxxFunction(*f);
+        for (int i = 0; i < args.size(); i++)
+        {
+          String arg = args[i].trim();
+          arg = arg.substring(arg.find_left(' ') + 1);
+          String val = vals[i].trim();
+          if (val == this->name())
+          {
+            //click_chatter("CRTP detected, replacing with uttermost");
+            val = outer_class;
+          }
+          //click_chatter("Replacing %s with %s",arg.c_str(),val.c_str());
+          c->replace_expr(arg, val, true, true);
         }
+        return c;
+      }
+
+      return f;
     }
-    return 0;
+  }
+  return 0;
 }
diff --git a/tools/click-devirtualize/specializer.cc b/tools/click-devirtualize/specializer.cc
index 96b202b5fd..cf5109c9a0 100644
--- a/tools/click-devirtualize/specializer.cc
+++ b/tools/click-devirtualize/specializer.cc
@@ -34,8 +34,8 @@ Specializer::Specializer(RouterT *router, const ElementMap &em)
   : _router(router), _nelements(router->nelements()),
     _ninputs(router->nelements(), 0), _noutputs(router->nelements(), 0),
     _etinfo_map(0), _header_file_map(-1), _parsed_sources(-1), _do_inline(false),
-    _do_static(false), _do_unroll(false), _unroll_val(0), _do_switch(false), _verbose(false), _switch_burst(0),
-    _do_jmps(false), _jmp_burst(0), _do_align(0)
+    _do_static(false), _do_unroll(false), _unroll_val(0), _switch_burst(0),
+    _do_jmps(false), _jmp_burst(0), _do_align(0), _verbose(false)
 {
   _etinfo.push_back(ElementTypeInfo());
 
diff --git a/tools/click-devirtualize/specializer.hh b/tools/click-devirtualize/specializer.hh
index 9589a2e95d..5194843d16 100644
--- a/tools/click-devirtualize/specializer.hh
+++ b/tools/click-devirtualize/specializer.hh
@@ -86,14 +86,14 @@ class Specializer { public:
   bool _do_replace;
   bool _do_inline;
   bool _do_static;
-  bool _do_unroll;
   bool _do_switch;
-  bool _verbose;
+  bool _do_unroll;
   int _unroll_val;
   int _switch_burst;
   int _do_jmps;
   int _jmp_burst;
   int _do_align;
+  bool _verbose;
 
   CxxInfo _cxxinfo;
 
diff --git a/tools/click-mkmindriver/click-mkmindriver.cc b/tools/click-mkmindriver/click-mkmindriver.cc
index 1a19108744..7d4a07b02d 100644
--- a/tools/click-mkmindriver/click-mkmindriver.cc
+++ b/tools/click-mkmindriver/click-mkmindriver.cc
@@ -655,6 +655,9 @@ particular purpose.\n");
 	md.require("Error", &lerrh);
 	md.require("PortInfo", &lerrh);
 	md.require("ScheduleInfo", &lerrh);
+#if HAVE_CTX
+    md.require("FlowContextDispatcher", &lerrh);
+#endif
 	if (driver == Driver::USERLEVEL)
 	    md.require("ControlSocket", &lerrh);
     }
diff --git a/userlevel/.gitignore b/userlevel/.gitignore
index 33e3a8c7a7..9cdcf50a77 100644
--- a/userlevel/.gitignore
+++ b/userlevel/.gitignore
@@ -2,6 +2,7 @@
 *.o
 Makefile
 click
+packetmill
 elements.cc
 elements.conf
 elements.csmk
diff --git a/userlevel/Makefile.in b/userlevel/Makefile.in
index 100d241f56..91bbcfb696 100644
--- a/userlevel/Makefile.in
+++ b/userlevel/Makefile.in
@@ -13,7 +13,7 @@ bindir = @bindir@
 sbindir = @sbindir@
 libdir = @libdir@
 
-VPATH = .:$(top_srcdir)/lib:$(top_srcdir)/vendor/tinyexpr:$(top_srcdir)/vendor/nicscheduler:$(top_srcdir)/lib/metron:$(top_srcdir)/$(subdir):$(top_srcdir)/elements/standard
+VPATH = .:$(top_srcdir)/lib:$(top_srcdir)/vendor/tinyexpr:$(top_srcdir)/vendor/tinymt32:$(top_srcdir)/vendor/swifsymbol:$(top_srcdir)/vendor/nicscheduler:$(top_srcdir)/lib/metron:$(top_srcdir)/$(subdir):$(top_srcdir)/elements/standard
 
 CC = @CC@
 CPP = @CPP@
@@ -78,6 +78,7 @@ GENERIC_OBJS = string.o straccum.o nameinfo.o \
 	integers.o md5.o crc32.o in_cksum.o iptable.o \
 	archive.o userutils.o driver.o \
 	tinyexpr.o \
+	tinymt32.o swifsymbol.o \
 	$(EXTRA_DRIVER_OBJS) $(LLVM_OBJS)
 
 USE_FLOW = @USE_FLOW@
diff --git a/vendor/swifsymbol/swifsymbol.c b/vendor/swifsymbol/swifsymbol.c
new file mode 100644
index 0000000000..2432679b53
--- /dev/null
+++ b/vendor/swifsymbol/swifsymbol.c
@@ -0,0 +1,62 @@
+#include <stdbool.h>
+#include "swifsymbol.h"
+
+uint8_t gf256_mul(uint8_t a, uint8_t b, uint8_t *mul) { 
+    return mul[a * 256 + b];
+}
+
+
+uint8_t gf256_mul_formula(uint8_t a, uint8_t b)
+{
+    uint8_t p = 0;
+    for (int i = 0 ; i < 8 ; i++) {
+        if ((b % 2) == 1) p ^= a;
+        b >>= 1;
+        bool carry = (a & 0x80) != 0;
+        a <<= 1;
+        if (carry) {
+            a ^= 0x1d;
+        }
+    }
+    return p;
+}
+
+
+/**
+ * @brief Take a symbol and add another symbol multiplied by a 
+ *        coefficient, e.g. performs the equivalent of: p1 += coef * p2
+ * @param[in,out] p1     First symbol (to which coef*p2 will be added)
+ * @param[in]     coef  Coefficient by which the second packet is multiplied
+ * @param[in]     p2     Second symbol
+ */
+void symbol_add_scaled
+(void *symbol1, uint8_t coef, const void *symbol2, uint32_t symbol_size, uint8_t *mul)
+{
+    uint8_t *data1 = (uint8_t *) symbol1;
+    uint8_t *data2 = (uint8_t *) symbol2; 
+    for (uint32_t i=0; i<symbol_size; i++) {
+        data1[i] ^= gf256_mul(coef, data2[i], mul);
+    }
+}
+
+bool symbol_is_zero(void *symbol, uint32_t symbol_size) {
+    uint8_t *data8 = (uint8_t *) symbol;
+    uint64_t *data64 = (uint64_t *) symbol;
+    for (int i = 0 ; i < symbol_size/8 ; i++) {
+        if (data64[i] != 0) return false;
+    }
+    for (int i = (symbol_size/8)*8 ; i < symbol_size ; i++) {
+        if (data8[i] != 0) return false;
+    }
+    return true;
+}
+
+void symbol_mul(uint8_t *symbol1, uint8_t coef, uint32_t symbol_size, uint8_t *mul) {
+    for (uint32_t i=0; i<symbol_size; i++) {
+        symbol1[i] = gf256_mul(coef, symbol1[i], mul);
+    }
+}
+
+void assign_inv(uint8_t *array) {
+        array[0] = 0; array[1] = 1; array[2] = 142; array[3] = 244; array[4] = 71; array[5] = 167; array[6] = 122; array[7] = 186; array[8] = 173; array[9] = 157; array[10] = 221; array[11] = 152; array[12] = 61; array[13] = 170; array[14] = 93; array[15] = 150; array[16] = 216; array[17] = 114; array[18] = 192; array[19] = 88; array[20] = 224; array[21] = 62; array[22] = 76; array[23] = 102; array[24] = 144; array[25] = 222; array[26] = 85; array[27] = 128; array[28] = 160; array[29] = 131; array[30] = 75; array[31] = 42; array[32] = 108; array[33] = 237; array[34] = 57; array[35] = 81; array[36] = 96; array[37] = 86; array[38] = 44; array[39] = 138; array[40] = 112; array[41] = 208; array[42] = 31; array[43] = 74; array[44] = 38; array[45] = 139; array[46] = 51; array[47] = 110; array[48] = 72; array[49] = 137; array[50] = 111; array[51] = 46; array[52] = 164; array[53] = 195; array[54] = 64; array[55] = 94; array[56] = 80; array[57] = 34; array[58] = 207; array[59] = 169; array[60] = 171; array[61] = 12; array[62] = 21; array[63] = 225; array[64] = 54; array[65] = 95; array[66] = 248; array[67] = 213; array[68] = 146; array[69] = 78; array[70] = 166; array[71] = 4; array[72] = 48; array[73] = 136; array[74] = 43; array[75] = 30; array[76] = 22; array[77] = 103; array[78] = 69; array[79] = 147; array[80] = 56; array[81] = 35; array[82] = 104; array[83] = 140; array[84] = 129; array[85] = 26; array[86] = 37; array[87] = 97; array[88] = 19; array[89] = 193; array[90] = 203; array[91] = 99; array[92] = 151; array[93] = 14; array[94] = 55; array[95] = 65; array[96] = 36; array[97] = 87; array[98] = 202; array[99] = 91; array[100] = 185; array[101] = 196; array[102] = 23; array[103] = 77; array[104] = 82; array[105] = 141; array[106] = 239; array[107] = 179; array[108] = 32; array[109] = 236; array[110] = 47; array[111] = 50; array[112] = 40; array[113] = 209; array[114] = 17; array[115] = 217; array[116] = 233; array[117] = 251; array[118] = 218; array[119] = 121; array[120] = 219; array[121] = 119; array[122] = 6; array[123] = 187; array[124] = 132; array[125] = 205; array[126] = 254; array[127] = 252; array[128] = 27; array[129] = 84; array[130] = 161; array[131] = 29; array[132] = 124; array[133] = 204; array[134] = 228; array[135] = 176; array[136] = 73; array[137] = 49; array[138] = 39; array[139] = 45; array[140] = 83; array[141] = 105; array[142] = 2; array[143] = 245; array[144] = 24; array[145] = 223; array[146] = 68; array[147] = 79; array[148] = 155; array[149] = 188; array[150] = 15; array[151] = 92; array[152] = 11; array[153] = 220; array[154] = 189; array[155] = 148; array[156] = 172; array[157] = 9; array[158] = 199; array[159] = 162; array[160] = 28; array[161] = 130; array[162] = 159; array[163] = 198; array[164] = 52; array[165] = 194; array[166] = 70; array[167] = 5; array[168] = 206; array[169] = 59; array[170] = 13; array[171] = 60; array[172] = 156; array[173] = 8; array[174] = 190; array[175] = 183; array[176] = 135; array[177] = 229; array[178] = 238; array[179] = 107; array[180] = 235; array[181] = 242; array[182] = 191; array[183] = 175; array[184] = 197; array[185] = 100; array[186] = 7; array[187] = 123; array[188] = 149; array[189] = 154; array[190] = 174; array[191] = 182; array[192] = 18; array[193] = 89; array[194] = 165; array[195] = 53; array[196] = 101; array[197] = 184; array[198] = 163; array[199] = 158; array[200] = 210; array[201] = 247; array[202] = 98; array[203] = 90; array[204] = 133; array[205] = 125; array[206] = 168; array[207] = 58; array[208] = 41; array[209] = 113; array[210] = 200; array[211] = 246; array[212] = 249; array[213] = 67; array[214] = 215; array[215] = 214; array[216] = 16; array[217] = 115; array[218] = 118; array[219] = 120; array[220] = 153; array[221] = 10; array[222] = 25; array[223] = 145; array[224] = 20; array[225] = 63; array[226] = 230; array[227] = 240; array[228] = 134; array[229] = 177; array[230] = 226; array[231] = 241; array[232] = 250; array[233] = 116; array[234] = 243; array[235] = 180; array[236] = 109; array[237] = 33; array[238] = 178; array[239] = 106; array[240] = 227; array[241] = 231; array[242] = 181; array[243] = 234; array[244] = 3; array[245] = 143; array[246] = 211; array[247] = 201; array[248] = 66; array[249] = 212; array[250] = 232; array[251] = 117; array[252] = 127; array[253] = 255; array[254] = 126; array[255] = 253;
+}
\ No newline at end of file
diff --git a/vendor/swifsymbol/swifsymbol.h b/vendor/swifsymbol/swifsymbol.h
new file mode 100644
index 0000000000..7bc3a0752b
--- /dev/null
+++ b/vendor/swifsymbol/swifsymbol.h
@@ -0,0 +1,47 @@
+/**
+ * SWiF Codec: an open-source sliding window FEC codec in C
+ * https://github.com/irtf-nwcrg/swif-codec
+ */
+
+#ifndef SWIF_SYMBOL_H
+#define SWIF_SYMBOL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#define symbol_sub_scaled symbol_add_scaled
+#define gf256_add(a, b) (a^b)
+#define gf256_sub gf256_add
+    
+/*---------------------------------------------------------------------------*/
+
+/**
+ * @brief Take a symbol and add another symbol multiplied by a 
+ *        coefficient, e.g. performs the equivalent of: p1 += coef * p2
+ * @param[in,out] p1     First symbol (to which coef*p2 will be added)
+ * @param[in]     coef2  Coefficient by which the second packet is multiplied
+ * @param[in]     p2     Second symbol
+ */
+void symbol_add_scaled
+(void *symbol1, uint8_t coef, const void *symbol2, uint32_t symbol_size, uint8_t *mul);
+
+bool symbol_is_zero(void *symbol, uint32_t symbol_size);
+
+void symbol_mul
+(uint8_t *symbol1, uint8_t coef, uint32_t symbol_size, uint8_t *mul);
+
+uint8_t gf256_mul(uint8_t a, uint8_t b, uint8_t *mul);
+
+uint8_t gf256_mul_formula(uint8_t a, uint8_t b);
+
+void assign_inv(uint8_t *array);
+
+/*---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SWIF_SYMBOL_H */
\ No newline at end of file
diff --git a/vendor/tinymt32/LICENSE b/vendor/tinymt32/LICENSE
new file mode 100644
index 0000000000..792335125b
--- /dev/null
+++ b/vendor/tinymt32/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2011, 2013 Mutsuo Saito, Makoto Matsumoto,
+Hiroshima University and The University of Tokyo.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of the Hiroshima University nor the names of
+      its contributors may be used to endorse or promote products
+      derived from this software without specific prior written
+      permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/vendor/tinymt32/tinymt32.c b/vendor/tinymt32/tinymt32.c
new file mode 100644
index 0000000000..ede1052820
--- /dev/null
+++ b/vendor/tinymt32/tinymt32.c
@@ -0,0 +1,145 @@
+/**
+ * @file tinymt32.c
+ *
+ * @brief Tiny Mersenne Twister only 127 bit internal state
+ *
+ * @author Mutsuo Saito (Hiroshima University)
+ * @author Makoto Matsumoto (The University of Tokyo)
+ *
+ * Copyright (C) 2011 Mutsuo Saito, Makoto Matsumoto,
+ * Hiroshima University and The University of Tokyo.
+ * All rights reserved.
+ *
+ * The 3-clause BSD License is applied to this software, see
+ * LICENSE.txt
+ */
+#include "tinymt32.h"
+#define MIN_LOOP 8
+#define PRE_LOOP 8
+
+/**
+ * This function represents a function used in the initialization
+ * by init_by_array
+ * @param x 32-bit integer
+ * @return 32-bit integer
+ */
+static uint32_t ini_func1(uint32_t x) {
+    return (x ^ (x >> 27)) * UINT32_C(1664525);
+}
+
+/**
+ * This function represents a function used in the initialization
+ * by init_by_array
+ * @param x 32-bit integer
+ * @return 32-bit integer
+ */
+static uint32_t ini_func2(uint32_t x) {
+    return (x ^ (x >> 27)) * UINT32_C(1566083941);
+}
+
+/**
+ * This function certificate the period of 2^127-1.
+ * @param random tinymt state vector.
+ */
+static void period_certification(tinymt32_t * random) {
+    if ((random->status[0] & TINYMT32_MASK) == 0 &&
+        random->status[1] == 0 &&
+        random->status[2] == 0 &&
+        random->status[3] == 0) {
+        random->status[0] = 'T';
+        random->status[1] = 'I';
+        random->status[2] = 'N';
+        random->status[3] = 'Y';
+    }
+}
+
+/**
+ * This function initializes the internal state array with a 32-bit
+ * unsigned integer seed.
+ * @param random tinymt state vector.
+ * @param seed a 32-bit unsigned integer used as a seed.
+ */
+void tinymt32_init(tinymt32_t * random, uint32_t seed) {
+    random->status[0] = seed;
+    random->status[1] = random->mat1;
+    random->status[2] = random->mat2;
+    random->status[3] = random->tmat;
+    for (unsigned int i = 1; i < MIN_LOOP; i++) {
+        random->status[i & 3] ^= i + UINT32_C(1812433253)
+            * (random->status[(i - 1) & 3]
+               ^ (random->status[(i - 1) & 3] >> 30));
+    }
+    period_certification(random);
+    for (unsigned int i = 0; i < PRE_LOOP; i++) {
+        tinymt32_next_state(random);
+    }
+}
+
+/**
+ * This function initializes the internal state array,
+ * with an array of 32-bit unsigned integers used as seeds
+ * @param random tinymt state vector.
+ * @param init_key the array of 32-bit integers, used as a seed.
+ * @param key_length the length of init_key.
+ */
+void tinymt32_init_by_array(tinymt32_t * random, uint32_t init_key[],
+                            int key_length) {
+    const unsigned int lag = 1;
+    const unsigned int mid = 1;
+    const unsigned int size = 4;
+    unsigned int i, j;
+    unsigned int count;
+    uint32_t r;
+    uint32_t * st = &random->status[0];
+
+    st[0] = 0;
+    st[1] = random->mat1;
+    st[2] = random->mat2;
+    st[3] = random->tmat;
+    if (key_length + 1 > MIN_LOOP) {
+        count = (unsigned int)key_length + 1;
+    } else {
+        count = MIN_LOOP;
+    }
+    r = ini_func1(st[0] ^ st[mid % size]
+                  ^ st[(size - 1) % size]);
+    st[mid % size] += r;
+    r += (unsigned int)key_length;
+    st[(mid + lag) % size] += r;
+    st[0] = r;
+    count--;
+    for (i = 1, j = 0; (j < count) && (j < (unsigned int)key_length); j++) {
+        r = ini_func1(st[i % size]
+                      ^ st[(i + mid) % size]
+                      ^ st[(i + size - 1) % size]);
+        st[(i + mid) % size] += r;
+        r += init_key[j] + i;
+        st[(i + mid + lag) % size] += r;
+        st[i % size] = r;
+        i = (i + 1) % size;
+    }
+    for (; j < count; j++) {
+        r = ini_func1(st[i % size]
+                      ^ st[(i + mid) % size]
+                      ^ st[(i + size - 1) % size]);
+        st[(i + mid) % size] += r;
+        r += i;
+        st[(i + mid + lag) % size] += r;
+        st[i % size] = r;
+        i = (i + 1) % size;
+    }
+    for (j = 0; j < size; j++) {
+        r = ini_func2(st[i % size]
+                      + st[(i + mid) % size]
+                      + st[(i + size - 1) % size]);
+        st[(i + mid) % size] ^= r;
+        r -= i;
+        st[(i + mid + lag) % size] ^= r;
+        st[i % size] = r;
+        i = (i + 1) % size;
+    }
+    period_certification(random);
+    for (i = 0; i < PRE_LOOP; i++) {
+        tinymt32_next_state(random);
+    }
+}
\ No newline at end of file
diff --git a/vendor/tinymt32/tinymt32.h b/vendor/tinymt32/tinymt32.h
new file mode 100644
index 0000000000..e26b6c49a1
--- /dev/null
+++ b/vendor/tinymt32/tinymt32.h
@@ -0,0 +1,257 @@
+#ifndef TINYMT32_H
+#define TINYMT32_H
+/**
+ * @file tinymt32.h
+ *
+ * @brief Tiny Mersenne Twister only 127 bit internal state
+ *
+ * @author Mutsuo Saito (Hiroshima University)
+ * @author Makoto Matsumoto (University of Tokyo)
+ *
+ * Copyright (C) 2011 Mutsuo Saito, Makoto Matsumoto,
+ * Hiroshima University and The University of Tokyo.
+ * All rights reserved.
+ *
+ * The 3-clause BSD License is applied to this software, see
+ * LICENSE.txt
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#define TINYMT32_MEXP 127
+#define TINYMT32_SH0 1
+#define TINYMT32_SH1 10
+#define TINYMT32_SH8 8
+#define TINYMT32_MASK UINT32_C(0x7fffffff)
+#define TINYMT32_MUL (1.0f / 16777216.0f)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/**
+ * tinymt32 internal state vector and parameters
+ */
+struct TINYMT32_T {
+    uint32_t status[4];
+    uint32_t mat1;
+    uint32_t mat2;
+    uint32_t tmat;
+};
+
+typedef struct TINYMT32_T tinymt32_t;
+
+void tinymt32_init(tinymt32_t * random, uint32_t seed);
+void tinymt32_init_by_array(tinymt32_t * random, uint32_t init_key[],
+                            int key_length);
+
+#if defined(__GNUC__)
+/**
+ * This function always returns 127
+ * @param random not used
+ * @return always 127
+ */
+inline static int tinymt32_get_mexp(
+    tinymt32_t * random  __attribute__((unused))) {
+    return TINYMT32_MEXP;
+}
+#else
+inline static int tinymt32_get_mexp(tinymt32_t * random) {
+    return TINYMT32_MEXP;
+}
+#endif
+
+/**
+ * This function changes internal state of tinymt32.
+ * Users should not call this function directly.
+ * @param random tinymt internal status
+ */
+inline static void tinymt32_next_state(tinymt32_t * random) {
+    uint32_t x;
+    uint32_t y;
+
+    y = random->status[3];
+    x = (random->status[0] & TINYMT32_MASK)
+        ^ random->status[1]
+        ^ random->status[2];
+    x ^= (x << TINYMT32_SH0);
+    y ^= (y >> TINYMT32_SH0) ^ x;
+    random->status[0] = random->status[1];
+    random->status[1] = random->status[2];
+    random->status[2] = x ^ (y << TINYMT32_SH1);
+    random->status[3] = y;
+    int32_t const a = -((int32_t)(y & 1)) & (int32_t)random->mat1;
+    int32_t const b = -((int32_t)(y & 1)) & (int32_t)random->mat2;
+    random->status[1] ^= (uint32_t)a;
+    random->status[2] ^= (uint32_t)b;
+}
+
+/**
+ * This function outputs 32-bit unsigned integer from internal state.
+ * Users should not call this function directly.
+ * @param random tinymt internal status
+ * @return 32-bit unsigned pseudorandom number
+ */
+inline static uint32_t tinymt32_temper(tinymt32_t * random) {
+    uint32_t t0, t1;
+    t0 = random->status[3];
+#if defined(LINEARITY_CHECK)
+    t1 = random->status[0]
+        ^ (random->status[2] >> TINYMT32_SH8);
+#else
+    t1 = random->status[0]
+        + (random->status[2] >> TINYMT32_SH8);
+#endif
+    t0 ^= t1;
+    if ((t1 & 1) != 0) {
+        t0 ^= random->tmat;
+    }
+    return t0;
+}
+
+/**
+ * This function outputs floating point number from internal state.
+ * Users should not call this function directly.
+ * @param random tinymt internal status
+ * @return floating point number r (1.0 <= r < 2.0)
+ */
+inline static float tinymt32_temper_conv(tinymt32_t * random) {
+    uint32_t t0, t1;
+    union {
+        uint32_t u;
+        float f;
+    } conv;
+
+    t0 = random->status[3];
+#if defined(LINEARITY_CHECK)
+    t1 = random->status[0]
+        ^ (random->status[2] >> TINYMT32_SH8);
+#else
+    t1 = random->status[0]
+        + (random->status[2] >> TINYMT32_SH8);
+#endif
+    t0 ^= t1;
+    if ((t1 & 1) != 0) {
+        conv.u  = ((t0 ^ random->tmat) >> 9) | UINT32_C(0x3f800000);
+    } else {
+        conv.u  = (t0 >> 9) | UINT32_C(0x3f800000);
+    }
+    return conv.f;
+}
+
+/**
+ * This function outputs floating point number from internal state.
+ * Users should not call this function directly.
+ * @param random tinymt internal status
+ * @return floating point number r (1.0 < r < 2.0)
+ */
+inline static float tinymt32_temper_conv_open(tinymt32_t * random) {
+    uint32_t t0, t1;
+    union {
+        uint32_t u;
+        float f;
+    } conv;
+
+    t0 = random->status[3];
+#if defined(LINEARITY_CHECK)
+    t1 = random->status[0]
+        ^ (random->status[2] >> TINYMT32_SH8);
+#else
+    t1 = random->status[0]
+        + (random->status[2] >> TINYMT32_SH8);
+#endif
+    t0 ^= t1;
+        if ((t1 & 1) != 0) {
+        conv.u  = ((t0 ^ random->tmat) >> 9) | UINT32_C(0x3f800001);
+    } else {
+        conv.u  = (t0 >> 9) | UINT32_C(0x3f800001);
+    }
+    return conv.f;
+}
+
+/**
+ * This function outputs 32-bit unsigned integer from internal state.
+ * @param random tinymt internal status
+ * @return 32-bit unsigned integer r (0 <= r < 2^32)
+ */
+inline static uint32_t tinymt32_generate_uint32(tinymt32_t * random) {
+    tinymt32_next_state(random);
+    return tinymt32_temper(random);
+}
+
+/**
+ * This function outputs floating point number from internal state.
+ * This function is implemented using multiplying by (1 / 2^24).
+ * floating point multiplication is faster than using union trick in
+ * my Intel CPU.
+ * @param random tinymt internal status
+ * @return floating point number r (0.0 <= r < 1.0)
+ */
+inline static float tinymt32_generate_float(tinymt32_t * random) {
+    tinymt32_next_state(random);
+    return (float)(tinymt32_temper(random) >> 8) * TINYMT32_MUL;
+}
+
+/**
+ * This function outputs floating point number from internal state.
+ * This function is implemented using union trick.
+ * @param random tinymt internal status
+ * @return floating point number r (1.0 <= r < 2.0)
+ */
+inline static float tinymt32_generate_float12(tinymt32_t * random) {
+    tinymt32_next_state(random);
+    return tinymt32_temper_conv(random);
+}
+
+/**
+ * This function outputs floating point number from internal state.
+ * This function is implemented using union trick.
+ * @param random tinymt internal status
+ * @return floating point number r (0.0 <= r < 1.0)
+ */
+inline static float tinymt32_generate_float01(tinymt32_t * random) {
+    tinymt32_next_state(random);
+    return tinymt32_temper_conv(random) - 1.0f;
+}
+
+/**
+ * This function outputs floating point number from internal state.
+ * This function may return 1.0 and never returns 0.0.
+ * @param random tinymt internal status
+ * @return floating point number r (0.0 < r <= 1.0)
+ */
+inline static float tinymt32_generate_floatOC(tinymt32_t * random) {
+    tinymt32_next_state(random);
+    return 1.0f - tinymt32_generate_float(random);
+}
+
+/**
+ * This function outputs floating point number from internal state.
+ * This function returns neither 0.0 nor 1.0.
+ * @param random tinymt internal status
+ * @return floating point number r (0.0 < r < 1.0)
+ */
+inline static float tinymt32_generate_floatOO(tinymt32_t * random) {
+    tinymt32_next_state(random);
+    return tinymt32_temper_conv_open(random) - 1.0f;
+}
+
+/**
+ * This function outputs double precision floating point number from
+ * internal state. The returned value has 32-bit precision.
+ * In other words, this function makes one double precision floating point
+ * number from one 32-bit unsigned integer.
+ * @param random tinymt internal status
+ * @return floating point number r (0.0 <= r < 1.0)
+ */
+inline static double tinymt32_generate_32double(tinymt32_t * random) {
+    tinymt32_next_state(random);
+    return tinymt32_temper(random) * (1.0 / 4294967296.0);
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
\ No newline at end of file