4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 // Stefan Bund <g0dil@berlios.de>
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 /** \mainpage libPPI : The Packet Processing Infrastructure
25 The PPI provides an infrastructure to create packet oriented network processing applications. A
26 PPI application is built by combining processing modules in a very flexible manner.
28 \image html scenario.png Target Scenario
30 The PPI concept is built around some key concepts
32 \li The PPI is based on processing \ref ppi_packets. It does not handle stream oriented
34 \li The PPI is built around reusable \ref ppi_modules. Each module is completely independent.
35 \li Each module has an arbitrary number of \ref ppi_connectors, inputs and outputs.
36 \li The modules are connected to each other using flexible \ref ppi_connections.
37 \li Data flow throughout the network is governed via flexible automatic or manual \ref
38 ppi_throttling (throttle notifications).
39 \li Modules may register additional external \ref ppi_events (file descriptor events or timers).
41 The PPI thereby builds on the facilities provided by the other components of the SENF
42 framework. The target scenario above depicts a diffserv capable UDLR/ULE router including
43 performance optimizations for TCP traffic (PEP). This router is built by combining several
46 \see \ref ppi_overview \n
47 <a href="../../../Examples/RateStuffer/doc/html/index.html">PPI Example Application:
49 \ref senf::ppi::module "Modules" \n
50 \ref senf::ppi::connector "Connectors" \n
54 /** \page ppi_overview PPI Overview and Concepts
59 <li>\ref ppi_design</li>
60 <li>\ref ppi_packets</li>
61 <li>\ref ppi_modules</li>
62 <li>\ref ppi_connectors</li>
63 <li>\ref ppi_connections</li>
64 <li>\ref ppi_throttling</li>
65 <li>\ref ppi_events</li>
67 <li>\ref ppi_flows</li>
71 \section ppi_design Design considerations
73 The PPI interface is designed to be as simple as possible. It provides sane defaults for all
74 configurable parameters to simplify getting started. It also automates all resource
75 management. The throttling infrastructure handles blocking conditions (like input exhaustion)
78 \section ppi_packets Packets
80 The PPI processes packets and uses the <a href="@TOPDIR@/Packets/doc/html/index.html">Packet
81 library</a> to handle them. All packets are internally passed around as generic \ref
82 senf::Packet references, however connectors may optionally be defined as sending or receiving
83 packets of a specific type only.
85 \section ppi_modules Modules
87 A module is represented by a class derived from senf::ppi::module::Module. Each module has
90 \li It may have any number of \ref ppi_connectors (inputs and outputs)
91 \li Each module declares flow information which details the route packets take within the
92 module. This information does not define how the information is processed, it only tells,
93 where data arriving on some input will be directed at (\ref
94 senf::ppi::module::Module::route())
95 \li The module might take additional parameters.
96 \li The module might also register additional \ref ppi_events.
98 Generally, modules are divided into several categories:
100 \li \ref io_modules receive external data or forward packets out of the PPI
101 \li \ref routing_modules forward packets within the network
102 \li \ref adapter_modules are used to connect incompatible connectors to each other
103 \li Application modules are modules implemented to perform an applications function
105 Of these modules, normally only the application modules need to be implemented since the library
106 provides an extensive set of reusable modules.
108 The following example module declares three \ref ppi_connectors "Connectors": \c payload,
109 \c stuffing and \c output. These connectors are defined as \e public data members so they
110 can be accessed from the outside. This is important as we will see below.
114 : public senf::ppi::module::Module
116 SENF_PPI_MODULE(RateStuffer);
118 senf::ppi::IntervalTimer timer_;
121 senf::ppi::connector::ActiveInput<> payload;
122 senf::ppi::connector::ActiveInput<> stuffing;
123 senf::ppi::connector::ActiveOutput<> output;
125 RateStuffer(unsigned packetsPerSecond)
126 : timer_(1000u, packetsPerSecond)
128 route(payload, output);
129 route(stuffing, output);
131 registerEvent( timer_, &RateStuffer::tick );
145 The constructor will declare flow information using senf::ppi::module::Module::route(). Then the
146 module registers an interval timer which will fire <tt>packetsPerSecond</tt> times every
147 <tt>1000</tt> milliseconds.
149 The module processing is very simple: Whenever a timer tick arrives a packet is sent. If the \c
150 payload input is ready (see \ref ppi_throttling), a payload packet is sent, otherwise a stuffing
151 packet is sent. The module will therefore provide a constant stream of packets at a fixed rate
152 on \c output (see the
153 <a href="@TOPDIR@/Examples/RateStuffer/doc/html/index.html">RateStuffer</a> example application
154 for a slightly different approach)
156 An example module to generate the stuffing packets could be
159 class CopyPacketGenerator
160 : public senf::ppi::module::Module
162 SENF_PPI_MODULE(CopyPacketGenerator);
164 senf::ppi::connector::PassiveOutput<> output;
166 CopyPacketGenerator(Packet template)
167 : template_ (template)
170 output.onRequest(&CopyPacketGenerator::makePacket);
178 output(template_.clone());
183 This module just produces a copy of a given packet whenever output is requested.
185 \see senf::ppi::module::Module
187 \section ppi_connectors Connectors
189 The input and output attachment points of a module are called connectors. Each connector may be
190 active or passive. This gives us 4 types of connectors:
192 \li senf::ppi::connector::ActiveInput
193 \li senf::ppi::connector::ActiveOutput
194 \li senf::ppi::connector::PassiveInput
195 \li senf::ppi::connector::PassiveOutput
197 An \e active connector (input or output) is <em>activated by the module</em> to send data or to
198 poll for available packets. This means, the modules processing routine will call the connector
199 without being signaled by the framework to read the connector. It just actively fetches a
202 A \e passive connector is <em>signaled by the framework</em> to fetch data from the module or to
203 pass data into the module. The module must register a callback which will be called, whenever a
204 packet is requested from the module or whenever a new packet is made available for the module to
207 To send or receive a packet (either actively or passively) the module just calls the
208 connector. It is permissible to generate or process multiple packets in one iteration. However,
209 you must ensure yourself that enough packets are available to be read if more than one packet
210 shall be read. It is also permissible to not handle a packet at all even if signaled to do
211 so. The packet will automatically be queued.
213 To provide this flexibility, all input connectors incorporate a packet queue. This queue is
214 exposed to the module and allows the module to optionally process packets in batches.
216 Connectors take an optional template argument which allows to specify the type of packet this
217 connector sends or received. This template arguments defaults to \ref senf::Packet.
219 \see \ref senf::ppi::connector
221 \section ppi_connections Connections
223 \image html ratestuffer.png Simple RateStuffer
225 To make use of the modules, they have to be instantiated and connections have to be created
226 between its connectors. It is possible to connect any pair of input/output connectors as long as
227 one of them is active and the other is passive.
229 It is possible to connect two active or passive connectors with each other using a special
230 adaptor module (senf::ppi::module::PassiveQueue or senf::ppi::module::ActiveFeeder
233 To complete our simplified example: Lets connet senf::ppi::module::ActiveSocketReader and
234 senf::ppi::module::PassiveSocketWriter to our example module:
237 RateStuffer rateStuffer (10);
239 senf::Packet stuffingPacket = senf::DataPacket::create(...);
240 CopyPacketGenerator generator (stuffingPacket);
242 senf::UDPv4ClientSocketHandle inputSocket (1111);
243 senf::ppi::module::ActiveSocketReader udpInput (inputSocket);
245 senf::UDPv4ClientSocketHandle outputSocket ("2.3.4.5:2222");
246 senf::ppi::module::PassiveSocketWriter udpOutput (outputSocket);
248 senf::ppi::module::PassiveQueue adaptor;
250 senf::ppi::connect(udpInput, adaptor);
251 senf::ppi::connect(adaptor, rateStuffer.payload);
252 adaptor.qdisc(ThresholdQueueing(10,5));
253 senf::ppi::connect(generator, rateStuffer.stuffing);
254 senf::ppi::connect(rateStuffer, udpOutput);
259 This application will read udp-packets coming in on port 1111 and will forward
260 them to port 2222 on host 2.3.4.5 with a fixed rate of 10 packets / second.
262 We start out by instantiating the necessary modules. Then the connections between these modules
263 are set up by successively connecting each output connector to an input connector. As can be
264 seen, the name of the connector can be left of if it is named \c output or \c input
267 The buffering on the udpInput <-> rateStuffer adaptor is changed so the queue will begin to
268 throttle only if more than 10 packets are in the queue. The connection will be unthrottled as
269 soon as there are no more than 5 packets left in the queue (see \ref ppi_throttling).
271 \section ppi_throttling Throttling
273 Throttling and throttle notifications at it's base is about handling blocking conditions. The
274 most straight forward blocking condition is that of a file descriptor not being available for
275 reading resp. writing. Other blocking conditions can arise for example when a queue fills up or
276 if a module for some application specific reason does not want to handle packets for a period of
279 All this is handled using throttle notifications. We need throttle notifications so a passive
280 connector can tell it's connected peer that it cannot service further requests until an
281 unthrottle notification is sent. This tells us, that from the view of someone implementing a
282 module, throttle notifications will always be received on active connectors and be sent on
285 This tells us, that the direction of control flow (the throttle notifications) is from passive
286 to active connectors and does \e not depend on the direction of data flow (which flows from
287 output to input connector). Thinking about this, this makes sense: The module with the active
288 connector is the one initiating the data processing (after all, it is the \e active part) and
289 needs to be told not to request or send packets on it's connector since the connected passive
290 peer cannot handle the request.
292 So if a passive connector cannot handle requests, the connector must be \e throttled. Throttling
293 the connector will forward a throttle notification to its peer. The peer then handles the
294 throttling notification.
296 There are two ways, throttle notifications can be handled: By automatic throttling or by
297 registering callbacks. The default is <em>automatic throttling</em>.
299 <em>Automatic throttling</em> is based on the routing information available to the module. Every
300 notification received is forwarded within the module along all known routes from active to
301 passive connectors (routes which connect to active or passive connectors are absolutely valid,
302 they just are not \e forwarding routes, they are ignored by the throttle
303 notifications). Together with automatic event throttling (see \ref ppi_events), this is all that
304 is normally needed to handle throttle notifications: By forwarding the notifications we ensure,
305 that a module's passive connectors will only be signaled when it's corresponding active
306 connectors are not throttled (as defined by the routing information). The module is therefore
307 not called until the connector(s) are untrhottled.
309 <em>Throttle callbacks</em> can optionaly be registerd (with automatic throttling enabled or
310 disabled, see \ref senf::ppi::connector::ActiveConnector) to be called when a throttle
311 notification is received. The callback may then handle the notification however it sees fit, for
312 example by manually throttling some passive connector (see \ref
313 senf::ppi::connector::PassiveConnector).
315 To enable/disable automatic throttling, the \ref senf::ppi::module::Module::route() command
316 returns a reference to a \ref senf::ppi::Route instance. If this route is \e forwarding route,
317 (that is, of the connectors is passive and the other is active), the return value will be
318 derived from \ref senf::ppi::ForwardingRoute which provides members to control the throttle
319 notification forwarding.
322 senf::ppi::module::Module \n
325 \section ppi_events Events
327 Modules may register additional events. These external events are very important since they
328 drive the PPI framework. Events are like external calls into the module network which are sent
329 whenever some event happens. Some possible events are
330 \li timer events (senf::ppi::IntervalTimer)
331 \li read or write events on some file descriptor (senf::ppi::IOEvent)
332 \li internal events (senf::ppi::IdleEvent)
334 The PPI really is not concerned, how the events are called and what information is needed to
335 perform the call. This is handled by the <a
336 href="@TOPDIR@/Scheduler/doc/html/index.html">Scheduler</a>, which is wrapped by the event
339 All events are derived from senf::ppi::EventDescriptor. The base class allows to enable and
340 disable the event. Each type of event will take descriptor specific constructor arguments to
341 describe the event to be generated. Events are declared as (private) data members of the
342 module and are then registered using senf::ppi::module::Module::registerEvent().
344 Each event when signaled is described by an instance of the descriptor specific \e
345 descriptorType \c ::Event class. This instance will hold the event specific information (like
346 scheduled time of the event, file handle state and so on). This information is passed to the
349 Additionaly, events are valid routing targets. This feature allows events to be disabled and
350 enabled by throtling notifications. For the sake of routing, an event may be used like an active
351 input or output. Iit is \e active from the PPI's point of view since it is signaled from the
352 outside and not by some module. It may be either input or output depending on the operation the
355 If we take into account event routing, we can extend the \c RateStuffer constructor accordingly:
358 RateStuffer(unsigned packetsPerSecond)
359 : timer_(1000u, packetsPerSecond)
361 route(payload, output);
362 route(stuffing, output);
363 route(timer_, output); // (*)
365 registerEvent( timer_, &RateStuffer::tick );
369 We have added the marked route call. This way, the \c timer_ will receive throttling
370 notifications from the output: Whenever the output is throttled, the event will be disabled
371 until the output is unthrottled again.
373 \see senf::ppi::EventDescriptor
375 \section ppi_run Running the network
377 After the network has been set up, senf::ppi::run() is called to execute it. This call will only
378 return after all data has been processed. The PPI knows this, when no events are enabled any
379 more. Without events, nothing will happen any more since it is the events which drive the
380 PPI. Therefore the PPI surmises, that all data has been processed and returns from
383 This works very well with automatic throttling. When no data is available to be processed any
384 more and no more data can be expected to arrive (for Example since data has been read from a
385 file which is now exhausted) all events will be disabled automatically via trhottle
386 notifications and so signal that any processing should stop.
388 \section ppi_flows Information Flow
390 The above description conceptually introduces three different flow levels:
392 \li The <em>data flow</em> is, where the packets are flowing. This flow always goes from output
394 \li The <em>execution flow</em> describes the flow of execution from one module to another. This
395 flow always proceeds from active to passive connector.
396 \li The <em>control flow</em> is the flow of throttling notifications. This flow always proceeds
397 \e opposite to the execution flow, from passive to active connector.
399 This is the outside view, from without any module. These flows are set up using
400 senf::ppi::connect() statements.
402 Within a module, the different flow levels are defined differently depending on the type of
405 \li The <em>data flow</em> is defined by how data is processed. The different event and
406 connector callbacks will pass packets around and thereby define the data flow
407 \li Likewise, the <em>execution flow</em> is defined parallel to the data flow (however possible
408 in opposite direction) by how the handler of one connector calls other connectors.
409 \li The <em>control flow</em> is set up using senf::ppi::Module::route statements (as long as
410 automatic throttling is used. Manual throttling defines the control flow within the
411 respective callbacks).
413 In nearly all cases, these flows will be parallel. Therefore it makes sense to define the \c
414 route statement as defining the 'conceptual data flow' since this is also how control messages
415 should flow (sans the direction, which is defined by the connectors active/passive property).
417 \see \ref ppi_implementation
420 /** \page ppi_implementation Implementation Notes
422 \section processing Data Processing
424 The processing in the PPI is driven by events. Without events <em>nothing will happen</em>. When
425 an event is generated, the called module will probably call one of it's active connectors.
427 Calling an active connector will directly call the handler registered at the connected passive
428 connector. This way the call and data are handed across the connections until an I/O module will
429 finally handle the request (by not calling any other connectors).
431 Throttling is handled in the same way: Throttling a passive connector will call a corresponding
432 (internal) method of the connected active connector. This method will call registered handlers
433 and will analyze the routing information of the module for other (passive) connectors to call
434 and throttle. This will again create a call chain which terminates at the I/O modules. An event
435 which is called to be throttled will disable the event temporarily. Unthrottling works in the
438 This simple structure is complicated by the existence of the input queues. This affects both
439 data forwarding and throttling:
440 \li A data request will only be forwarded, if no data is available in the queue
441 \li The connection will only be throttled when the queue is empty
442 \li Handlers of passive input connectors must be called repeatedly until either the queue is
443 empty or the handler does not take any packets from the queue
446 \section ppi_logistics Managing the Data Structures
448 The PPI itself is a singleton. This simplifies many of the interfaces (We do not need to pass
449 the PPI instance). Should it be necessary to have several PPI systems working in parallel
450 (either by registering all events with the same event handler or by utilizing multiple threads),
451 we can still extend the API by adding an optional PPI instance argument.
453 Every module manages a collection of all it's connectors and every connector has a reference to
454 it's containing module. In addition, every connector maintains a collection of all it's routing
457 All this data is initialized via the routing statements. This is, why \e every connector must
458 appear in at least one routing statement: These statements will as a side effect initialize the
459 connector with it's containing module.
461 Since all access to the PPI via the module is via it's base class, unbound member function
462 pointers can be provided as handler arguments: They will automatically be bound to the current
463 instance. This simplifies the PPI usage considerably. The same is true for the connectors: Since
464 they know the containing module, they can explicitly bind unbound member function pointers to
467 \section ppi_random_notes Random implementation notes
469 Generation of throttle notifications: Backward throttling notifications are automatically
470 generated (if this is not disabled) whenever the input queue is non-empty \e after the event
471 handler has finished processing. Forward throttling notifications are not generated
472 automatically within the connector. However, the Passive-Passive adaptor will generate
473 Forward-throttling notifications whenever the input queue is empty.
475 \section ppi_classdiagram Class Diagram
477 \image html classes.png
484 // c-file-style: "senf"
485 // indent-tabs-mode: nil
486 // ispell-local-dictionary: "american"