// Copyright (C) 2007 // Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS) // Kompetenzzentrum fuer Satelitenkommunikation (SatCom) // Stefan Bund // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /** \mainpage libPPI : The Packet Processing Infrastructure The PPI provides an infrastructure to create packet oriented network processin applications. A PPI application is built by combining processing modules in a very flexible manner. \image html scenario.png Target Scenario The PPI concept is built around some key concepts \li The PPI is based on processing \e packets. It does not handle stream oriented channels. \li The PPI is built around reusable \e modules. Each module is completely independent. \li Each module has an arbitrary number of \e connectors, inputs and outputs. \li The modules are connected to each other using flexible \e connections. \li Data flow throughout the network is governed via flexible automatic or manual \e throttling. \li Modules may register additional external \e events (file descriptor events or timers). The PPI thereby builds on the facilities provided by the other components of the SENF framework. Modules are divided roughly in to two categories: I/O modules provide packet sources and sinks (network connection, writing packets to disk, generating new packets) whereas processing modules process packets internally. The target scenario above depicts a diffserv capable UDLR/ULE router including performance optimizations for TCP traffic (PEP). This router is built by combining several modules. In this scenario, TAP, ASI Out, Raw Socket and in a limited way Generator are I/O modules whereas PEP, DiffServ, DVB Enc, GRE/UDLR, TCP Filter and Stufferare processing modules. ASI/MPEG and Net are external I/O ports which are integrated via the TAP, ASI Out and Raw Sock modules using external events. \section packets Packets The PPI processes packets and uses the Packet library to handle them. All packets are passed around as generic Packet::ptr's, the PPI does not enforce any packet type restrictions. \section modules Modules A module is represented by a class type. Each module has several components: \li It may have any number of connectors (inputs and outputs) \li Each module declares flow information which details the route packets take within the module. This information does not define how the information is processed, it only tells, where data arriving on some input will be directed at. \li The module might take additional parameters. \li The module might also register additional events. \code class RateStuffer : public senf::ppi::Module { public: ActiveInput payload; ActiveInput stuffing; ActiveOutput output; RateStuffer(unsigned packetsPerSecond) { route(payload, output); route(stuffing, output); registerEvent(&RateStuffer::tick, IntervalTimer(1000u, packetsPerSecond)); } private: void tick() { if (payload) output(payload()); else output(stuffing()); } }; \endcode This module declares three I/O connectors (see below): payload, stuffing and output. These connectors are defined as public data members so they can be accessed from the outside. This is important as we will see below. On module instantiation, it will declare it's flow information with route (which is inherited from senf::ppi::Module). Then the module registers an interval timer which will fire packetsPerSecond times every 1000 milliseconds. The processing of the module is very simple: Whenever a timer tick arrives a packet is sent. If the payload input is ready (see throttling below), a payload packet is sent, otherwise a stuffing packet is sent. The module will therefore provide a constant stream of packets at a fixed rate on output An example module to generate the stuffing packets could be \code class CopyPacketGenerator : public senf::ppi::Module { public: PassiveOutput output; CopyPacketGenerator(Packet::ptr template) : template_ (template) { noroute(output); output.onRequest(&CopyPacketGenerator::makePacket); } private: Packet::ptr template_; void makePacket() { output(template_.clone()); } }; \endcode This module just produces a copy of a given packet whenever output is requested. \subsection connectors Connectors Inputs and Outputs can be active and passive. An \e active I/O is activated by the module to send data or to poll for available packets. A \e passive I/O is signaled by the framework to fetch data from the module or to pass data into the module. To send or receive a packet (either actively or after packet reception has been signaled), the module just calls the connector. This allows to generate or process multiple packets in one iteration. However, reading will only succeed, as long as packets are available from the connection. A module might want to queue incoming packets within a passive input or outgoing packets within an active output. This is possible by either not reading any packet even though a new packet has been scheduled on the input or by writing to the output while it is still throttled. To facilitate this use, the connectors provide accessors to access the attached connection and it's queue. This allows to analyze all packets available in the queue and act accordingly. \section connections Connections To make use of the modules, they have to be instantiated and connections have to be created between the I/O connectors. It is possible to connect any pair of input/output connectors as long as at least one of them is active Every connection contains an internal packet queue. Under normal operating conditions (without throttling) the queue will mostly be empty since packets will be processed directly. If a connection is throttled, it can still receive new packets on it's input which will then be queued. This is necessary even though the throttling will be propagated backwards (so no new packets should arrive) since a module may produce more then one result packet from a single incoming packet. To complete our simplified example: Lets say we have a UdpInput module and a UdpOutput module. We can then use our RateStuffer module to build an application which will create a fixed-rate UDP stream: \code RateStuffer rateStuffer (10); CopyPacketGenerator generator (some_packet_ptr); senf::UDPv4ClientSocketHandle inputSocket (1111); senf::ppi::SocketInput udpInput (inputSocket); senf::UDPv4ClientSocketHandle outputSocket ("2.3.4.5:2222"); senf::ppi::SocketOutput udpOutput (outputSocket); senf::ppi::connect(udpInput.output, rateStuffer.payload) .bufferHighThresh(10) .bufferLowThresh(5); senf::ppi::connect(generator.output, rateStuffer.stuffing); senf::ppi::connect(rateStuffer.output, udpOutput.input); senf::ppi::run(); \endcode First all necessary modules are created. Then the connections between these modules are set up. The buffering of the udpInput <-> rateStuffer connection is changed so the queue will begin to throttle only if more than 10 packets are in the queue. The connection will be unthrottled as soon as there are no more than 5 packets left in the queue. This application will read udp-packts coming in on port 1111 and will forward them to port 2222 on host 2.3.4.5 with a fixed rate of 10 packets / second. \section throttling Throttling If a connection cannot pass packets in it's current state, the connection is \e throttled. In simple cases, throttling is handled automatically by \li the connection if the queue is exceeds the buffering threshold \li I/O modules whenever the external source or sink of the module is not ready Throttling is handled separately in each direction: \li Forward throttling will throttle in the direction of the data flow. Example: No new packets are available from an input. This will activate forward throttling until new data arrives \li Backward throttling will throttle in the direction to the data source. Example: an output device (e.g. serial interface) has no more room for data. This event will activate backward throttling so no new data will arrive until the device can send data again The throttling state is managed within the Connection. Automatic throttling utilizes the routing information (provided in the modules constructor) to forward throttling events across modules. However, automatic throttling can be disabled for each connector. Modules may also register event handlers whenever a throttling event occurs. Whenever a connection is throttled (in the corresponding direction), passive connectors will \e not be called by the framework. This may lead to packets being accumulated in the connection queue. These packets will be sent as soon as the connection is unthrottled. The unthrottle event will hoewever only be forwarded when the queue is empty (or has reached it's lower buffering threshold). \code passiveConnector.autoForwardThrottling(false); passiveConnector.autoBackwardThrotttling(true); passiveConnector.onForwardThrottle(...); passiveConnector.onBackwardUnthrottle(...); \endcode Throttling is not enforced: especially a throttled output may still be called, the excessive packets will be queued in the connection queue. \section events Events Modules may register additional events. These external events are very important since the drive the PPI framework. Possible event sources are \li time based events \li file descriptors. */ // Local Variables: // mode: c++ // fill-column: 100 // c-file-style: "senf" // indent-tabs-mode: nil // ispell-local-dictionary: "american" // mode: flyspell // mode: auto-fill // End: