Example introducing the Packet Processing Infrastructure

This example application implements a simple PPI application: It will read UDP packets from an input port and will forward them to another port at a fixed packet rate. If the input stream does not provide enough packets, empty UDP packets will be sent instead.

Running the example

Running the example is a little bit more complicated since we need to provide example UDP
packets so we can see the application at work. We do this using \c netcat. We open several shell
windows and run the following commands, each in it's own window

The first command listens for incoming UDP packets on port 44345:

nc -u -l -p 44345

 
The next command starts the \c ratestuffer

cd .../Examples/RateStuffer

scons -u

./ratestuffer

 
We should now see '<idle>' messages arriving in the first window once per second. We now can
start another \c netcat to send packets to the input port.

nc -u localhost 44344

< type any text here >
Whenever we send out a packet with CR in the last window we should see it appear in the first
one. If we send out packets faster than 1 packet per second, they will start to be discarded if
more than two packets are in flight.

\image html screenshot.png

Module setup

Above image depicts the module setup implementing the rate stuffer. A
senf::ppi::module::ActiveSocketSource reads the incoming UDP packets and sends them into a
senf::ppi::module::PassiveQueue (via a senf::ppi::module::ThrottleBarrier).

The \a queue feeds the packets into a senf::ppi::module::PriorityJoin. The CloneSource
\a generator is fed as second input into the \a join to provide the stuffing packets.

The RateFilter \a rateFilter reads packets from it's input at a fixed rate and writes them into
the senf::ppi::module::PassiveSocketSink \a udpSink. The senf::ppi::module::PriorityJoin
ensures that read requests of the RateStuffer's input are always serviced, either from the \a
queue or, if the \a queue output is throttled, from the \a generator.

The \a barrier is not strictly necessary. However, it makes the behavior of the RateStuffer
predictable in the case where packets need to be dropped. Without the
senf::ppi::module::ThrottleBarrier, the packets will be left in the kernel socket queue. Packets
will only start to be dropped when that queue fills up. The size of this queue cannot be easily
manipulated and it's initial size is immense. So to stop the kernel queue from filling up with
increasingly out-of-date packets, we add the \a barrier which will explicitly read and drop
excess packets.

Example code

\dontinclude ratestuffer.cc

The code starts out including the necessary header files

\skip Custom
\skip #include
\until PPI

We also define some namespace aliases

\skip namespace
\until senf::ppi;

The RateStuffer application is based on one additional application module.

The RateFilter module

The RateFilter module simply forwards packets at a fixed rate.

\skip class
\until };

Both connectors of the RateFilter module are active. The module is driven by a
senf::ppi::IntervalTimer.

\until }

The event is initialized to fire every \a interval nanoseconds.  The traffic is routed 'across'
the timer which controls the traffic. This routing lets the module automatically handle
throttling events. The timer is registered to call RateFilter::timeout().

\until }

The event handler is quite simple: Every \a interval nanoseconds a packet is read from \a input
and forwarded to \a output.

This is all there is to the RateFilter module. Due to the routing setup, the timer will
automatically be disabled should either \a input or \a output become throttled. However, in this
specific case this should never happen: The \a input is connected (via the \a join) to the
senf::ppi::module::CloneSource, which will never throttle. The \a output is connected to a UDP
socket which also never throttles.

The RateStuffer subnet

We decide to implement the RateStuffer as a subnet or collection. This is a simple struct or
class which contains all the modules necessary for a specific functionality. The modules are
initialized and connected in the class's constructor. External connectors are exported as
references to the corresponding module connectors:

\skip class
\until rateFilter

First the needed modules are declared. We have
- the \a barrier to discard incoming packets sent to fast
- the \a queue to receive incoming packets and create throttling notifications
- the \a generator to create the stuffing packets
- the \a join to combine the input stream from the \a queue with the stuffing packet stream
- the \a rateFilter to generate the fixed rate output stream

\until output

Here we declare the external connectors. The subnetwork exports a single input and output
connector. The external connectors are declared as \e references.

\until output

The constructor now initializes all the local objects. We pass the template \a packet to the \a
generator and set the timing \a interval of the \a rateFilter.

The \a input and \a output connector references are bound to the corresponding connectors we
want to expose: \a input to the \a barrier's \a input and \a output to the \a rateFilter's \a
output.

\until };

The constructor body sets up the connections within the subnetwork. Finally, we set the queueing
discipline of the \a queue. This Completes the RateStuffer. This subnetwork can now be used like
a module.

Application setup

The applications main() method starts out by initializing the socket handles

\skip main
\until 44345

The \a inputSocket is listening on port 44344 while the \a outputSocket will send packets to
port 44345 on localhost. The \a outputSocket uses the senf::ConnectedUDPv4SocketProtocol which
is compatible with the senf::ppi::module::PassiveSocketSink module.

\until udpSink

Here we allocate the components:

- \a udpSource to read the packets from \a inputSocket
- \a stuffer for the real work and
- \a udpSink to send the packets to \a outputSocket

\until udpSink

The \ref senf::ppi::connect() calls setup the necessary connections.

The module setup is complete, \ref senf::ppi::run() is called to enter the event loop.

\until }