C H A P T E R  4

image

Construction of a packetC Program

packetC and Parallelism

packetC is designed to be used with parallel-processing runtime environments, although an implementation may be compliant without providing this. The language was designed for environments in which a packetC program will typically be executing with large numbers of simultaneous contexts. In packetC, similar to interrupt service routines, the arrival of a packet is an event that triggers processing by a context. The packetC program is considered re-entrant and is designed such that more than one copy is executing at any point in time. Parallel processing is based upon each context running the entire application from receipt of a packet to completion of processing for the assigned packet. The packetC language specification does not prescribe how actual or apparent parallelism must be implemented. Hence, each copy of a packet module or shared module is termed a context, since the terms process, task, or thread imply how parallel execution is to be implemented and imply characteristics that may vary from one operating system to another.

The packetC language is designed to hide the complexities of parallel programming from the user. Although a few explicit parallel constructs are available, such as lock() and unlock() operators, to use as semaphore controls on global variables, the packetC operating environment provides most parallel-processing functionality without programmer direction. Accordingly, the user does not have to explicitly replicate the program, allocate parallel resources, or orchestrate multiple executables. Furthermore, the coordination of the receipt of packets into memory buffers, allocation, and de-allocation of context to packets, or strict separation of memory between contexts is presumed to be controlled by the underlying system. Some fundamental aspects of the packetC language, such as how it handles identifier scopes, do reflect the fact that the language will usually be executed in a parallel environment.

packetC Modules: Three Kinds of Compilation Units

packetC programs are constructed from three kinds of compilation units or modules:

  • packet module
    • Contains the main body of an application and it alone is able to constitute an application; it may have subordinate functions and declare links to the other kinds of modules that will be resolved at link time.
  • shared module
    • A collection of functions that can be called by different types of packet modules during a single execution episode. Declaration syntax indicates whether a given function can be called by a packet module. Shared modules execute in their own memory space and are linked in dynamically when loading occurs on a system.
  • library module
    • A collection of routines that can be called by functions in a packet module or another library module. This construct supports separate compilation. Library modules are linked at compile time into an application produced by a packet module. In contrast to a shared module, each packet module linking a library module will have its own copy of code and data as part of the executable space of the application.

Only one kind of compilation unit can appear in a given source code file. The subsections below describe each kind of compilation unit.

Example of a packet module:

packet module mainApp;
#include <cloudshield.ph>         // Platform standard include file
#include “someLib.ph”             // Header file with function prototypes for library module

extern int sharedCall( $PACKET sharedPacket );  
// Function prototype for shared library function

int     countPacketPerSecond_;             // Global scope variable

// Local scope function
int myLocalFunc(int functionCounter) {
         int xLocal;                       // Block scope variable

   xLocal = sharedCall( pkt );             // Shared function call, visibility to the packet.
   functionCounter += 1;

   return xLocal;
}

/* Main entry point, system passes the current          *
 * packet ($PACKET type), current system variables      *
 * ($SYS type), and the packet info block ($PIB type).  *
 * All packet modules must have main declared.          */
void main( $PACKET pkt, $PIB pib, $SYS sys )
{
   int     mainFunctionCounter;                 // Packet scope variables
   int     xMain;

   // Call to local function
   xMain = myLocalFunc(@mainFunctionCounter);   // Packet scope var wo stack (inline)

   someLib_Func();                              // Call to Library function

   pib.action = FORWARD_PACKET;
}

Like C, and unlike some languages such as FORTRAN and Python, the indentation is for human consumption and readability. Although indentation is not a requirement of packetC, style guidelines to promote more auditable and potentially secure code are presented in the final chapter of this book.

Three Kinds of Scope

In C, blocks determine the scope of data between that which is global to all segments of the code and local scope data, such as within a function. Due to packetC's implicit parallelism, scope, and visibility requirements are different from C's basic scoping. Specifically, there is a need to indicate that some variables associated with a packet module are global in the sense of being shared by multiple contexts of the application running in parallel. This introduces additional constraints on their use. In addition, specific data is unique to each context. The input parameters to main() represent these unique data elements. The packet associated with the context, as well as system- and packet-related information are considered packet scope and available to code processing within the context. These unique values are considered to be part of a packet scope. In packetC, three scopes, as depicted in Figure 4-1, are defined and are central to parallel processing:

  • global scope
    • The variable is visible to every construct that follows its declaration within the module, reading in top-to-bottom fashion. In a packet module, the variable is visible to other contexts of that same packet module (application). Actions on global scope variables are atomic.  However, cooperative locking through the use of a semaphore is required for coherent multi-step transactions, because in packetC global scope differs from C global scope in that it is visible to multiple contexts.
  • block scope
    • The variable is visible solely within the enclosing block from the point of its declaration to the end of the enclosing block similar to local scope in C. Block scope variables are often those defined within functions such as loop counters.
  • packet scope
    • Packet scope variables are those defined within the body of a packet module's main(). In addition, the parameters passed as input to main, namely pkt, sys and pib are considered packet scope. Functions in the packet module automatically have visibility to pkt, sys, and pib. However, user variables defined in packet scope must be passed as arguments to functions. Packet scope is unique to packetC.
images

Figure 4-1. Global, block and packet scope

For reliability reasons, packetC forbids the use of conflicting identifiers, whether they name similar constructs or not. Conflicting use occurs if any source code location exists where both declarations could be visible; this assumes that a declaration of an identifier in an inner scope does not hide the identifier's occurrence in an outer scope.

int myFunc(int x) {
int i, j;
i = x;

for ( j = 1; j < 5; ++j ) {
   int i = j + 1;      // ERROR in packetC (redundant 'I'), legal in C
   …
   }
return i + 50;
}

int fn1() {int k;…}
int fn2() {int k;…}    // no conflicting visibility

// ERROR
int aName;
typedef int aName;     // ERROR: dissimilar uses still are conflicts
// (Also, for style reasons, types should use UpperCamelCase)

Module Structure and Scopes

The scoping rules applicable to each of the three types of source code module—packet module, shared module, and library module—are summarized in Figure 4-2 and treated separately below.

images

Figure 4-2. Three kinds of packetC compilation units

Packet Module

A packet module's structure consists of:

  • The packet module declaration at the top of the file.
  • An optional section of global definitions (types, variables, functions, function prototypes, pragmas). Function prototypes not included in the packet module are flagged for library module link-time resolution, while function prototypes preceded by the extern keyword are flagged for load-time dynamic linking to the appropriate shared module.
  • The packet application main. This body begins with the keywords void main followed by a parenthesized set of input arguments that establishes the origin of the current packet ($PACKET pkt), the Packet Information Block ($PIB pib), and the System Information Block ($SYS sys), described below. After an open curly bracket, the main body begins. The main body contains type declarations, variable declarations, and statements in any order following standard C rules. Naturally, the packet main is concluded by a closing curly bracket.

Each packet module must contain exactly one main() component. A packet module without a definition for main() or with more than one found across all included source code files will fail to compile.

Developers must consider several special attributes of global, packet, and block scope within packet modules.

Global scope: In global scope type declarations, global variable declarations, database and searchset declarations, function prototypes, functional declarations, and pragma clauses can appear here. The following is true for packet modules:

  • Global variables are shared among all contexts of an executing packet module.
  • While operators on global variables are atomic, complex interaction with global variables across instances must employ cooperative multi-tasking. The lock and unlock operators provide an atomic test-and-set allowing any global to be used as a semaphore. Global variables used as a semaphore cannot be used to store any other data.
  • Function prototypes without a subsequent function definition indicate that the function should appear at link time in a library module.
  • Function prototypes preceded by the extern keyword indicate that the function should appear as an entry point for an associated shared module.
  • An initialization value for a variable in global scope is set only once, at the loading of an application.

Packet scope: Only packet modules contain a packet scope which corresponds to the packet body within main(), enclosed by curly brackets. The following are true for packet scope:

  • A variable initialized within packet scope receives any initialization value declared for it each time that the enclosing packet application is triggered to process a new packet or event.
  • Similar to C, variables with the const keyword are initialized once at application start and cannot be modified during the execution of the packetC program.
  • The packet (pkt), packet information block (pib), or system information (sys) are each unique to the packet module context being processed and are accessible throughout all packet module scopes.
  • Variables declared within packet scope are visible from the point of their declaration to the end of main().

Block scope: Block scopes are defined by function bodies and compound statements just like C. Type declarations, variable declarations, and pragma clauses may appear within them, but not function prototypes or function declarations. An initialization value for a variable with block scope is set each time the block is entered.

packet module mainApp;
#include <cloudshield.ph>
#include <protocols.ph>
#include “someLib.ph”               // Header file with function prototypes for library module

extern int sharedCall( $PACKET pkt );  // Function prototype for shared library function

int     countPacketPerSecond_;     // Global scope variable(identify by lower camelCase
                                   //  andtrailing underscore)

// Block scope function
int myLocalFunc(int functionCounter)
{
   int  xLocal;                   // Block scope variable (identify by lower camelCase)

   xLocal =  sharedCall( pkt );   // Shared function providing visibility to the packet.
   functionCounter += 1;

   return xLocal;
}

/*
 * Main entry point, virtual machine passes the current packet in $PACKET pkt,
 * current system variables in $SYS sys, and the packet info block in $PIB pib.
 * All packet modules must have main() declared.  Unlike C, main() is always
 * declared as void in packetC.
 */
void main( $PACKET pkt, $PIB pib, $SYS sys ) {
   int   mainFunctionCounter;     // Packet scope variables
   int   xMain;

   // Call to local function
   xMain =  myLocalFunc(@mainFunctionCounter);
   // Provide packet scope variable without stack (inline)

   someLib_Func();                // Call to Library function

   sys.action = FORWARD_PACKET;
  }

Shared Module

A shared module's structure consists of:

  • The module declaration,
  • Global definitions, which may include function declarations preceded by the entry keyword, which serves as entry points for packet module callers. Function prototypes cannot be preceded by the entry keyword.

Shared modules interact with the three possible scopes as follows:

  • Global scope: Type declarations, global variable declarations, function prototypes, functional declarations, and pragma clauses may appear here. A shared module:
    • Has function declarations preceded by the entry keyword to indicate that the function may be called from (multiple kinds of) packet modules. If the entry point function needs the caller's current packet, the function's parameter list should include the packet.
    • Defines global variables that are visible only to functions within the shared module.
    • Has no visibility to global scope variables within the calling packet or library module.
  • Packet scope: A shared module has neither a packet scope nor a main() body. Any packet scope data from a packet module must be explicitly passed by value to a shared library if it is intended to be exposed.
  • Block scope: Block scopes are defined within shared module function bodies, either by the function bodies themselves, or by compound statements inside them. Type declarations, variable declarations, and pragma clauses may appear within them, but not function prototypes or function declarations. An initialization value for a variable with block scope is set each time the block is entered.
    shared module sharedApp;
    #include <cloudshield.ph>
    // Shared Modules provide shared functions to apps at run time

    // Library function prototypes - Note: Separate copy from mainApp
    #include "someLib.ph"

    // Global scope – Within Shared Library Only
    int sharedAppGlobal_;

    //  Local function in Shared Lib
    void myLocalFunc()
    {
       // Call to a Library function
       someLibFuncB();
       …
    }
    // Externally shared function
    entry void sharedCall($PACKET pkt)
    {
       int  xLocal; // Block Scope
       xLocal = (int)pkt[46];
       …   
       myLocalFunc();
    }

    // Externally shared function
    entry void sharedCall1()
    {
       int y;  // Block Scope
       myLocalFunc();

       // Call to Library function
       someLibFuncA(y);

       // Block scope variable
       int i;
       for ( i = 1; i < 5; ++i )
       {
          // Block scope variable
          int x;
          …
       }
    }

Library Module

A library module's structure consists of:

  • The module declaration,
  • global definitions, which may include type, variable and function declarations, as well as function prototypes and pragma clauses.

Library modules interact with the three possible scopes as follows.

  • Global scope: Type declarations, global variable declarations, and function prototypes are visible only within the library module. Functions declared within the library module can be called by packet modules that declare matching function prototypes in their own global sections.
  • Packet scope: A library module cannot define packet scope variables nor a main() body. Packet scope variables must be passed to a library module as parameters of function calls with the exception of pkt, sys, and pib which are available to functions in the library module bounded by the executing context.
  • Block scope: Block scopes are defined within library module function bodies, either by the function bodies themselves, or by compound statements inside them. Type declarations, variable declarations, and pragma clauses may appear within them, but not function prototypes or function declarations. An initialization value for a variable with block scope is set each time the block is entered.
library module someLib;
// Library modules provide funcs to applications at link time

// Library function prototypes
#include "someLib.ph"

// Global scope
int libraryGlobal_;

// Library function define
void someLibFuncA(int x)
{
  …
}

void someLibFuncB()
{
  …
}

void someLibFuncC()
{
  …
}

void someLibFuncD(int x)
{
   int myVal; // Block Scope
   // pkt, pib, sys visible
   if ( pkt[46] == 0xff )
     {
     // Block scope variable
      int y;  // Block Scope
      myVal = pib.payload_offset;
     }
   …
}

Graphical Representation of Scope Linkage

Figure 4-3 contains the same contents as Figure 4-2, except that graphical linkages are applied in the figure to show the scope relationships of some select elements that cross or are replicated across module boundaries.

images

Figure 4-3. Relationships of external content across packetC compilation units

In the modules above, three elements are highlighted to point out some of the packetC scope dynamics between modules. In both the packet module and shared module, the function myLocalFunc() is defined. The scope of myLocalFunc() is within each module and visibility is not shared. There are no conflicts to having the same function in both modules as it is not externalized and references remain local within the modules. When a function is desired to be shared from a shared module to a packet module, the entry and extern keywords are used to provide visibility and linkage as shown with sharedCall(). Library modules are linked in at compile time and standard function prototypes (included within someLib.ph) are utilized to properly define before referencing. The functions someLibFuncA() and someLibFuncB() represent examples of a library function reference by a packet module and a shared module.

Run time Environment Data and Predefined Types

packetC does not specify requirements for particular hardware at program execution time. However, it does require a packetC implementation to provide several predefined types that support common packet processing activities, such as managing packet contents, network protocols, ports, and messages.

Thus, a packetC implementation shall provide certain system data for each instance of a packet main() when the context first begins execution:

  • The current packet (for any executing instance of a packet module), declared as datatype $PACKET and passed to the main() as pkt.
  • The Packet Information Block (PIB) that includes information about the packet such as layer offsets and decoded protocol information, declared as datatype $PIB and passed to the main() as pib.
  • A System Information Block (SYS) that includes information about the system in which the packetC application is executing, including objects such as time and interface port information, declared as datatype $SYS and passed to the main() as sys.

To represent this data, packetC defines three predefined types: $PACKET, $PIB, and $SYS. These datatypes and parameters are reserved words within packetC.

  • $PACKET is the type of the current packet (acts as an array of bytes), always referenced as pkt.
  • $PIB is the structure definition for a Packet Information Block (PIB), always referenced as pib.
  • $SYS is the structure definition for a System Information Block (SYS), always referenced as sys.

A packet module's main shall have only one parameter of each of these types, as defined in the packet main section's declaration. These values are unique to each executing instance of the packet module and on input indicate the result of processing of the current packet prior to start of main and upon exit of main may dictate actions for the system to perform upon the packet subsequent to processing in the instance. The pkt, pib and sys are available throughout all scopes of a packet module and are not passed as parameters to any functions within a packet or library module. These may be placed on a parameter list for a shared module's entry function prototype to indicate and permit scope visibility.

packet module mainApp;
...
void main( $PACKET pkt, $PIB pib, $SYS sys )
{
  . . .
}

In addition, a packetC implementation shall implement

  • $MSG_TYPE to hold system messages that are used to interact with the control plane (see section on Control Plane Environment).

Packet ($PACKET pkt)

Before an instance of a packet module is executed, a current packet is made available to the program. The packet is made accessible to user code by declaring it as an input argument to a packet module's main() function.

As a variable of the pre-defined user type $PACKET, the current packet acts as an array of bytes. Portions of the current packet that comprise standard protocols are accessible through a special kind of structure, termed descriptors. (See the section on the descriptor data type below). In addition, the current packet can be treated as a byte array.

void main ( $PACKET pkt, $PIB pib, $SYS sys)
   {
      byte myArr[64];
      …
      myArr[0:31] = pkt[0:31];

Several packetC operators are defined for this data type (see Packet Operators described later in this book). A packetC implementation may handle the varying size of the current packet in a variety of ways, including specifying a maximum size for the packet.

Packet Information Block ($PIB pib)

A Packet Information Block (PIB) shall be present and shall contain information about the current packet that is derived from the packet, going beyond what is contained within the packet. This descriptive data includes information about the presence of various protocols (or headers) and their locations within the packet. For example, the PIB structure contains information such as the decoded layer 2 type, offsets to layer 3 and layer 4.

The PIB is accessible as a variable of the predefined type $PIB, specified as an argument to the packet module main. The structure definition for $PIB is provided in the cloudshield.ph #include file. (For a complete definition of the PIB see Chapter 18.)

void main ( $PACKET pkt, $PIB pib, $SYS sys) { … }

System Information ($SYS type)

The System Information (SYS) type is a structure that contains attributes about the system the packetC application is executing that may be useful for processing a packet. This includes, for example, arrival time, visibility to parallel processing contexts or instances, the interface port through which the packet was received, and information about system thresholds, such as buffer utilization.

System Information is accessible to the user in a variable of the predefined type $SYS, specified as an argument to the packet module main.

 void main ( $PACKET pkt, $PIB pib, $SYS sys) { … }

The structure definition for $SYS may vary by system vendor and is declared in a vendor #include file such as cloudshield.ph or, alternatively, predefined by a packetC compiler implementation. (SYS contents are described in Chapter 18.)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.79.84