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