2 // Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
3 // Kompetenzzentrum fuer Satelitenkommunikation (SatCom)
4 // Stefan Bund <g0dil@berlios.de>
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the
18 // Free Software Foundation, Inc.,
19 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 /** \mainpage libPPI : The Packet Processing Infrastructure
23 The PPI provides an infrastructure to create packet oriented network processin
24 applications. A PPI application is built by combining processing modules in a very flexible
27 \image html scenario.png Target Scenario
29 The PPI concept is built around some key concepts
31 \li The PPI is based on processing \ref packets. It does not handle stream oriented channels.
32 \li The PPI is built around reusable \ref modules. Each module is completely independent.
33 \li Each module has an arbitrary number of \ref connectors, inputs and outputs.
34 \li The modules are connected to each other using flexible \ref connections.
35 \li Data flow throughout the network is governed via flexible automatic or manual \ref throttling.
36 \li Modules may register additional external \ref events (file descriptor events or timers).
38 The PPI thereby builds on the facilities provided by the other components of the SENF
39 framework. The target scenario above depicts a diffserv capable UDLR/ULE router including
40 performance optimizations for TCP traffic (PEP). This router is built by combining several
43 \section design Design considerations
45 The PPI interface is designed to be as simple as possible. It provides sane defaults for all
46 configurable parameters to simplify getting started. It also automates all resource
47 management. Especially to simplify resource management, the PPI will take many configuration
48 objects by value. Even though this is not as efficient, it frees the user from most resource
49 management chores. This decision does not affect the runtime performance since it only affects
50 the configuration step.
52 \section packets Packets
54 The PPI processes packets and uses the <a href="@TOPDIR@/Packets/doc/html/index.html">Packet
55 library</a> to handle them. All packets are passed around as generic Packet::ptr's, the PPI
56 does not enforce any packet type restrictions.
58 \section modules Modules
60 A module is represented by a class type. Each module has several components:
62 \li It may have any number of connectors (inputs and outputs)
63 \li Each module declares flow information which details the route packets take within the
64 module. This information does not define how the information is processed, it only tells,
65 where data arriving on some input will be directed at.
66 \li The module might take additional parameters.
67 \li The module might also register additional events.
69 Modules are divided roughly in to two categories: I/O modules provide packet sources and sinks
70 (network connection, writing packets to disk, generating new packets) whereas processing modules
71 process packets internally. In the target scenario, <em>TAP</em>, <em>ASI Out</em>, <em>Raw
72 Socket</em> and in a limited way <em>Generator</em> are I/O modules whereas <em>PEP</em>,
73 <em>DiffServ</em>, <em>DVB Enc</em>, <em>GRE/UDLR</em>, <em>TCP Filter</em> and <em>Stuffer</em>
74 are processing modules. <em>ASI/MPEG</em> and <em>Net</em> are external I/O ports which are
75 integrated via the <em>TAP</em>, <em>ASI Out</em> and <em>Raw Sock</em> modules using external
78 The following example module declares three I/O connectors (see below): <tt>payload</tt>,
79 <tt>stuffing</tt> and <tt>output</tt>. These connectors are defined as <em>public</em> data
80 members so they can be accessed from the outside. This is important as we will see below.
84 : public senf::ppi::Module
86 senf::ppi::IntervalTimer timer_;
89 senf::ppi::ActiveInput payload;
90 senf::ppi::ActiveInput stuffing;
91 senf::ppi::ActiveOutput output;
93 RateStuffer(unsigned packetsPerSecond)
94 : timer_(1000u, packetsPerSecond)
96 route(payload, output);
97 route(stuffing, output);
99 registerEvent(&RateStuffer::tick, timer_);
113 On module instantiation, it will declare it's flow information with <tt>route</tt> (which
114 is inherited from <tt>senf::ppi::Module</tt>). Then the module registers an interval timer which
115 will fire <tt>packetsPerSecond</tt> times every <tt>1000</tt> milliseconds.
117 The processing of the module is very simple: Whenever a timer tick arrives a packet is sent. If
118 the <tt>payload</tt> input is ready (see throttling below), a payload packet is sent, otherwise
119 a stuffing packet is sent. The module will therefore provide a constant stream of packets at a
120 fixed rate on <tt>output</tt>
122 An example module to generate the stuffing packets could be
125 class CopyPacketGenerator
126 : public senf::ppi::Module
129 senf::ppi::PassiveOutput output;
131 CopyPacketGenerator(Packet::ptr template)
132 : template_ (template)
135 output.onRequest(&CopyPacketGenerator::makePacket);
139 Packet::ptr template_;
143 output(template_.clone());
148 This module just produces a copy of a given packet whenever output is requested.
150 \section connectors Connectors
152 Inputs and Outputs can be active and passive. An \e active I/O is <em>activated by the
153 module</em> to send data or to poll for available packets. A \e passive I/O is <em>signaled by
154 the framework</em> to fetch data from the module or to pass data into the module.
156 To send or receive a packet (either actively or after packet reception has been signaled), the
157 module just calls the connector. This allows to generate or process multiple packets in one
158 iteration. However, reading will only succeed, as long as packets are available from the
161 Since a module is free to generate more than a single packet on incoming packet requests, all
162 input connectors incorporate a packet queue. This queue is exposed to the module and allows the
163 module to process packets in batches.
165 \section connections Connections
167 To make use of the modules, they have to be instantiated and connections have to be created
168 between the I/O connectors. It is possible to connect any pair of input/output connectors as
169 long as one of them is active and the other is passive.
171 It is possible to connect two active connectors with each other using a special adaptor
172 module. This Module has a passive input and a passive output. It will queue any incoming packets
173 and automatically handle throttling events (see below). This adaptor is automatically added by
174 the connect method if needed.
176 To complete our simplified example: Lets say we have an <tt>ActiveSocketInput</tt> and a
177 <tt>PassiveUdpOutput</tt> module. We can then use our <tt>RateStuffer</tt> module to build an
178 application which will create a fixed-rate UDP stream:
181 RateStuffer rateStuffer (10);
183 senf::Packet::ptr stuffingPacket = senf::Packet::create<...>(...);
184 CopyPacketGenerator generator (stuffingPacket);
186 senf::UDPv4ClientSocketHandle inputSocket (1111);
187 senf::ppi::ActiveSocketInput udpInput (inputSocket);
189 senf::UDPv4ClientSocketHandle outputSocket ("2.3.4.5:2222");
190 senf::ppi::PassiveSocketOutput udpOutput (outputSocket);
192 senf::ppi::connect(udpInput.output, rateStuffer.payload,
193 dynamicModule<PassiveQueue>()
194 -> qdisc(ThresholdQueueing(10,5)) );
195 senf::ppi::connect(generator.output, rateStuffer.stuffing);
196 senf::ppi::connect(rateStuffer.output, udpOutput.input);
201 First all necessary modules are created. Then the connections between these modules are set
202 up. The buffering on the udpInput <-> rateStuffer adaptor is changed so the queue will begin to
203 throttle only if more than 10 packets are in the queue. The connection will be unthrottled as
204 soon as there are no more than 5 packets left in the queue. This application will read
205 udp-packts coming in on port 1111 and will forward them to port 2222 on host 2.3.4.5 with a
206 fixed rate of 10 packets / second.
208 \section throttling Throttling
210 If a passive connector cannot handle incoming requests, this connector may be \e
211 throttled. Throttling a request will forward a throttle notification to the module connected
212 to that connector. The module then must handle this throttle notification. If automatic
213 throttling is enabled for the module (which is the default), the notification will automatically
214 be forwarded to all dependent connectors as taken from the flow information. For there it will
215 be forwarded to further modules and so on.
217 A throttle notification reaching an I/O module will normally disable the input/output by
218 disabling any external I/O events registered by the module. When the passive connector which
219 originated the notification becomes active again, it creates an unthrottle notification which
220 will be forwarded in the same way. This notification will re-enable any registered I/O events.
222 The above discussion shows, that throttle events are always generated on passive connectors and
223 received on active connectors. To differentiate further, the throttling originating from a
224 passive input is called <em>backward throttling</em> since it is forwarded in the direction \e
225 opposite to the data flow. Backward throttling notifications are sent towards the input
226 modules. On the other hand, the throttling originating from a passive output is called
227 <em>forward throttling</em> since it is forwarded along the \e same direction the data
228 is. Forward throttling notifications are therefore sent towards the output modules.
230 Since throttling a passive input may not disable all further packet delivery immediately, any
231 passive input contains an input queue. In it's default configuration, the queue will send out
232 throttle notifications when it becomes non-empty and unthrottle notifications when it becomes
233 empty again. This automatic behavior may however be disabled. This allows a module to collect
234 incoming packets in it's input queue before processing a bunch of them in one go.
236 \section events Events
238 Modules may register additional events. These external events are very important since the drive
239 the PPI framework. Possible event sources are
240 \li time based events
241 \li file descriptors.
243 Here some example code implementing the ActiveSocketInput Module:
246 class ActiveSocketInput
247 : public senf::ppi::Module
249 typedef senf::ClientSocketHandle<
250 senf::MakeSocketPolicy< senf::ReadablePolicy,
251 senf::DatagramFramingPolicy > > Socket;
252 static PacketParser<senf::DataPacket> defaultParser_;
255 DataParser const & parser_;
256 senf::ppi:IOSignaler event_;
259 senf::ppi::ActiveOutput output;
261 // I hestitate taking parser by const & since a const & can be bound to
262 // a temporary even though a const & is all we need. The real implementation
263 // will probably make this a template arg. This simplifies the memory management
264 // from the users pov.
265 ActiveSocketInput(Socket socket, DataParser & parser = SocketInput::defaultParser_)
268 event_ (socket, senf::ppi::IOSignaler::Read)
270 registerEvent( &ActiveSocketInput::data, event_ );
271 route(event_, output);
280 output(parser_(data));
285 First we declare our own socket handle type which allows us to read packets. The constructor
286 then takes two arguments: A compatible socket and a parser object. This parser object gets
287 passed the packet data as read from the socket (an \c std::string) and returns a
288 senf::Packet::ptr. The \c PacketParser is a simple parser which interprets the data as specified
289 by the template argument.
291 We register an IOSignaler event. This event will be signaled whenever the socket is
292 readable. This event is routet to the output. This routing automates throttling for the socket:
293 Whenever the output receives a throttle notifications, the event will be temporarily disabled.
295 Processing arriving packets happens in the \c data() member: This member simple reads a packet
296 from the socket. It passes this packet to the \c parser_ and sends the generated packet out.
299 \li Exception handling. It would be great to have a sane default exception handling freeing us
300 from most manual work. However, I don't think this is feasible.
302 \see \ref ppi_implementation \n
303 <a href="http://openfacts.berlios.de/index-en.phtml?title=SENF:_Packet_Processing_Infrastructure">Implementation plan</a>
306 /** \page ppi_implementation Implementation Overview
308 \section processing Data Processing
310 The processing in the PPI is driven by external events. Without external events <em>nothing will
311 happen</em>. When an external event is generated, the module called will probably either send or
312 receive data from an active connector.
314 Calling an active connector will directly call the handler registered at the connected passive
315 connector. This way the call and data are handed across the connections until an I/O module will
316 finally handle the request (by not calling any other connectors).
318 Throttling is handled in the same way: Throttling a passive connector will call a corresponding
319 (internal) method of the connector active connector. This method will call registered handlers
320 and will analyze the routing information of the module for other (passive) connectors to call
321 and throttle. This will again create a call chain which terminates at the I/O modules. An event
322 which is called to be throttled will disable the event temporarily. Unthrottling works in the
325 This simple structure is complicated by the existence of the input queues. This affects both
326 data forwarding and throttling:
327 \li A data request will only be forwarded, if no data is available in the queue
328 \li The connection will only be throttled when the queue is empty
329 \li Handlers of passive input connectors must be called repeatedly until either the queue is
330 empty or the handler does not take any packets from the queue
333 \section logistics Managing the Data Structures
335 The PPI itself is a singleton. This simplifies many of the interfaces (We do not need to pass
336 the PPI instance). Should it be necessary to have several PPI systems working in parallel
337 (either by registering all events with the same event handler or by utilizing multiple threads),
338 we can still extend the API by adding an optional PPI instance argument.
340 Every module manages a collection of all it's connectors and every connector has a reference to
341 it's containing module. In addition, every connector maintains a collection of all it's routing
344 All this data is initialized via the routing statements. This is, why \e every connector must
345 appear in at least one routing statement: These statements will as a side effect initialize the
346 connector with it's containing module.
348 Since all access to the PPI via the module is via it's base class, unbound member function
349 pointers can be provided as handler arguments: They will automatically be bound to the current
350 instance. This simplifies the PPI usage considerably. The same is true for the connectors: Since
351 they know the containing module, they can explicitly bind unbound member function pointers to
355 \section random_notes Random implementation notes
357 Generation of throttle notifications: Backward throttling notifications are automatically
358 generated (if this is not disabled) whenever the input queue is non-empty \e after the event
359 handler has finished processing. Forward throttling notifications are not generated
360 automatically within the connector. However, the Passive-Passive adaptor will generate
361 Forward-throttling notifications whenever the input queue is empty.
368 // c-file-style: "senf"
369 // indent-tabs-mode: nil
370 // ispell-local-dictionary: "american"