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 programs are constructed from three kinds of compilation units or modules:
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.
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:
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)
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.
A packet module's structure consists of:
extern
keyword are flagged for load-time dynamic linking to the appropriate shared module.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:
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.extern
keyword indicate that the function should appear as an entry point for an associated shared module.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:
const
keyword are initialized once at application start and cannot be modified during the execution of the packetC program.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
// andtrailing 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;
}
A shared module's structure consists of:
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:
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.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;
…
}
}
A library module's structure consists of:
Library modules interact with the three possible scopes as follows.
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;
}
…
}
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.
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.
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:
To represent this data, packetC defines three predefined types: $PACKET, $PIB, and $SYS. These datatypes and parameters are reserved words within packetC.
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
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.
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) { … }
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.)
3.147.79.84