NDK API Guide

Table of Contents

1 Introduction

This chapter serves as an introduction to the programming API reference for the NDK software.

1.1 What This Document Covers

This Reference for the NDK is mainly a programming API reference guide. It is intended to aid in the development of network applications and describes the various API functions provided by the stack libraries.

Although this guide is the central reference document used when programming the stack, you should first see the TI Network Developer’s Kit (NDK) User’s Guide (SPRU523) to familiarize yourself with the stack libraries and with using the stack.

1.2 Introduction

The Network Developer’s Kit (NDK) is a platform for development and demonstration of network enabled applications on TI embedded processors. The code included in this NDK release is generic C code, which runs on a variety of TI devices.

Within the SimpleLink SDK, the Network Services SlNetSock module configures the NDK as the network stack for wired Ethernet communications.

The NDK stack serves as a rapid prototyping platform for the development of network and packet processing applications. It can be used to add network connectivity to existing applications for communications, configuration, and control. Using the components provided in the NDK, developers can quickly move from development concepts to working implementations attached to the network.

Figure 1-1 Components of an NDK-Enabled Application

As Figure 1-1 shows, a user application can make calls using the standard BSD sockets APIs, or it can directly call into the SlNetSock layer to communicate with a network connection. The SlNetSock layer is a stack-independent layer between the user application and the service-specific stacks.

Within the SimpleLink SDK, the Network Services SlNetSock module configures the NDK as the network stack for wired Ethernet communications. SlNetIfNDK is the implementation of the SlNetSock interface for the NDK.

The NDK stack’s settings may be configured at run time by making calls to the NDK’s Cfg() functions.

The NDK is designed to provide a platform-independent, device-independent, and RTOS-independent API interface for use by the application. Many of the API modules described in this document and in the TI Network Developer’s Kit (NDK) API Reference Guide (SPRU524) are rarely used by applications. Instead they are available to those who are writing device drivers and network stacks.

A user application typically uses the following APIs provided by the NDK:

1.3 Supplemental API Information

The following information appears as appendices to this document. These sections contain optional information that may be useful in understanding the low-level application interface, but is not required when developing traditional network applications.

2 Operating System Abstraction API

The NDK OS Abstraction Layer (OSAL) uses a combination of native OS and POSIX APIs as a means of OS abstraction. In general, application writers should not use the OSAL APIs, with a few exceptions. One is the TaskCreate() function, which may be used as a convenience function for creating an NDK user thread that will make socket calls. Another is TaskSelf(), which can be used as an argument to fdOpenSession().

To keep the stack system portable, it was coded to a very compact operating system abstraction. The stack can execute in any operating environment by porting the functions described here. Most of these functions will map directly to their native OS counterpart.

If you program to this API, your applications will execute on any system to which this abstraction is ported, but more importantly, because all the NDK functions are written to this layer, the behavior of the NDK can be altered by altering the implementation of this layer. This allows the stack to be tuned in how it interfaces to the native operating system.

2.1 Operating System Configuration

If you are using the Configuration Manager API to configure your application, the configuration options that regulate OS behavior are stored in a data structure. The types of properties defined in the structure are those that would typically be macros, but using a data structure allows the values to be changed without rebuilding the libraries. The structure is described here for completeness, but applications should use the configuration system to make alterations to these values.

If you are using XGCONF to configure your application, you can configure several aspects of the OS behavior in the Scheduling tab of the Global NDK module property page. When you build the *.cfg configuration file, the data structure described in the following section is generated internally and linked into your application. See the TI Network Developer’s Kit (NDK) User’s Guide (SPRU523) and the context-sensitive help for details.

Configuration methods are described in Section 4.

2.1.1 Configuration Structure

This section describes a data structure that is generated automatically by the XGCONF configuration. If you are using XGCONF for configuration, you can ignore the structure described here. If you are using the Configuration Manager API to configure your application, you will use this structure.

The stack internal configuration structure is _oscfg. Any element in this structure may be modified before the system is booted. System initialization is covered later in this document.

The _oscfg structure is of type OSENVCFG, which is defined as follows:

// Configuration Structure
typedef struct _osenvcfg {
    uint32_t DbgPrintLevel; // Debug message print threshold
    uint32_t DbgAbortLevel; // Debug message sys abort threshold
    int TaskPriLow;         // Lowest priority for stack task
    int TaskPriNorm;        // Normal priority for stack task
    int TaskPriHigh;        // High priority for stack task
    int TaskPriKern;        // Kernel-level priority (highest)
    int TaskStkLow;         // Minimum stack size
    int TaskStkNorm;        // Normal stack size
    int TaskStkHigh;        // Stack size for high volume tasks
    int TaskStkBoot;        // Stack size for NS_BootTask
} OSENVCFG;

The structure entries are defined as follows:

_oscfg.DbgPrintLevel Debug message print threshold

Default Value: DBG_INFO

Description: This is the lowest severity level of a system debug message (call to DbgPrintf() function) that will be recorded into the debug log. The threshold may be raised. The legal values for this variable are: DBG_INFO, DBG_WARN, DBG_ERROR, and DBG_None.

_oscfg.DbgAbortLevel Debug message abort threshold

Default Value: DBG_ERROR

Description: This is the lowest severity level of a system debug message (call to DbgPrintf() function) that will result in a system shutdown (call to NC_NetStop()). The threshold may be raised. The legal values for this variable are: DBG_INFO, DBG_WARN, DBG_ERROR, and DBG_None.

_oscfg.TaskPriLow Priority Level for Low Priority Stack Task

Default Value: 3

Description: This is the priority at which low priority stack task threads are set. Setting a thread to a lower priority than this will not disrupt the system, but no system or service supplied in this package will attempt it.

_oscfg.TaskPriNorm Priority Level for Normal Priority for Stack Task

Default Value: 5

Description: This is the priority at which most stack task threads are set. Task threads that are created by the system or services will usually run at this level.

_oscfg.TaskPriHigh Priority Level for High Priority for Stack Task

Default Value: 7

Description: This is the priority at which high priority stack task threads are set. Setting a thread at a higher priority than this may disrupt the system and cause unpredictable behavior if the thread calls any stack related functions. High priority tasks (like interrupts) can execute at higher priority levels, but should signal lower priority tasks to perform any required stack functions.

_oscfg.TaskPriKern Priority Level of High Priority Kernel Tasks

Default Value: 9

Description: This is the priority that task threads execute at when they are inside the kernel. Setting tasks to this priority level ensures that they will not be disrupted by another task calling stack functions. Note that this priority should be 2 higher than _oscfg.TaskPriHigh, to allow the scheduler thread to occupy a priority in between. The proper method of entering the kernel is to call llEnter() and llExit(). These functions are discussed in the appendices, as they are not required for applications programming.

_oscfg.TaskStkLow Minimum Task Stack Size

Default Value: 3072

Description: This is the stack size used for network task that do very little network processing, or do not use TCP.

_oscfg.TaskStkNorm Normal Task Stack Size

Default Value: 4096

Description: This is the stack size used for a network task with an average network bandwidth using TCP. It is used for the majority of network tasks in the network tools library that use TCP.

_oscfg.TaskStkHigh High Volume Task Stack Size

Default Value: 5120

Description: This is the stack size used to network tasks that require a high network bandwidth using TCP.

_oscfg.TaskStkBoot Boot Task Stack Size

Default Value: 2048

Description: This is the stack size used for the boot task (NS_BootTask).

2.2 Task Support

The task object provides a method of manipulating task threads using a generic task handle.

Internally, task threads are implemented using native threads of the RTOS the app is built to use. Therefore, the rules governing scheduling and stacks are determined by the underlying RTOS. The documentation for the RTOS being used should be consulted for more info.

2.2.1 Function Overview

The Task Object access functions (in functional order) are as follows:

Function Description
TaskCreate() Create new task thread
TaskDestroy() Destroy a task thread
TaskSelf() Get handle to current task thread
TaskExit() Exit (terminate) current task thread
TaskYield() Yield to another task thread at the same priority
TaskSleep() Block a task thread for a period of time
TaskSetPri() Set task thread priority level
TaskGetPri() Get task thread priority level
TaskSetEnv() Assign one of three private environment handles to task thread
TaskGetEnv() Retrieve one of three private environment handles

2.2.2 Task API Functions

TaskCreate – Create a Task Thread

void *TaskCreate( void(*pFun)(),
                  char      *Name,
                  int       Priority,
                  uint32_t  StackSize,
                  uintptr_t  Arg1,
                  uintptr_t  Arg2,
                  uintptr_t  argReserved);

Parameters

Return Value Returns a Task Handle on success or NULL on memory failure.

Description This function creates a task thread and automatically initializes and closes the NDK file descriptor table for this thread. If successful, TaskCreate() returns a handle to the newly created task.

This API automatically calls the NDK APIs fdOpenSession() and fdCloseSession() to initialize and close the file descriptor table, respectively, for this thread. When using TaskCreate() to create a thread for sockets programming, it is not necessary to call fdOpenSession() and fdCloseSession() in the function pFun, as this will be done automatically.

The task name supplied in Name is used for informational purposes only, and does not need to be unique.

The task priority specified in Priority determines the task thread’s priority relative to other tasks in the system. The priority should not be higher than the configured value for the NDK’s Global.highTaskPriLevel property (the priority for high priority NDK tasks), which is 7 by default. 0 is the lowest priority and should be reserved for an idle task. If the specified priority is negative, the task is blocked.

The task stack size specified by StackSize is not examined or adjusted by the create function. The size should be made compatible with the native environment (a multiple of 4 bytes should be sufficient).

Arg1 and Arg2 are optional arguments that can be passed to the calling function (they are always pushed onto the stack, but the task function need not reference them). argReserved is reserved for future use.

There is no limit to the number of tasks that can be installed in the system.

If the priority level of the new task is higher than the priority level of the current task, the entry-point function pFun is executed immediately (before TaskCreate() returns to the caller).

Calling this function may cause a task switch.

TaskDestroy – Destroy a Task Thread

void TaskDestroy(void *hTask);

Parameters

Return Value None.

Description Terminates execution of the task object specified by the supplied handle hTask, and frees task object from system memory. Note that memory allocated by the task thread is not associated with the task thread and must be freed manually.

TaskExit – Exit a Task Thread

void TaskExit();

Parameters None

Return Value Does not return.

Description This function exits a task thread. It should always be called immediately before the task entry-point function is about to return, but it may be called from anywhere.

TaskGetEnv – Get Task Environment Handle

void *TaskGetEnv(void  *hTask,
                 int   Slot);

Parameters

Return Value Private environment handle or NULL.

Description Returns a private environment handle for the supplied task handle hTask that was previously stored with the TaskSetEnv() function. The slot specified in Slot specifies the address (1-3) of the environment handle. There are actually four slots, but slot 0 is reserved.

NOTE: This function returns without setting or getting an environment variable if the “slot” parameter is non-zero. All internal stack functions use slot zero.

TI-RTOS Kernel Users Note: The OS adaptation layer (OS.LIB) implements this function for slot 0 only. The reserved slot 0 is the only slot required by the NDK. Slots 1 to 3 are not implemented. You should use the standard TI-RTOS Kernel functions Task_setEnv() and Task_getEnv() for private environment pointer storage and retrieval.

TaskGetPri – Get Task Priority

int TaskGetPri(void *hTask);

Parameters

Return Value Task priority level.

Description Returns the priority of the target task. See TaskSetPri() for more information on priority.

TaskSelf

Get the Handle to the Currently Executing Task Thread

void *TaskSelf();

Parameters None.

Return Value Handle to currently executing thread, or NULL on error.

Description Returns the task handle of the currently executing task thread. This function is used mainly in other task object calls where the caller wishes to operate on the current thread, but does not know the current thread’s handle.

If called on an illegal (system) thread, this function returns NULL. Only certain implementations of the OS even have a system thread, and no user code should ever be executed on it. A NULL may also result if Task functions are called before the operating system is initialized.

TaskSetEnv – Set Task Environment Handle

void TaskSetEnv(void  *hTask,
                int   Slot,
                void  *hEnv);

Parameters

Return Value None.

Description Sets and stores a private environment handle for the supplied task handle hTask. This handle can be later retrieved by TaskGetEnv(). The slot specified in Slot assigns an address (1-3) to the environment handle. There are actually four slots, but slot 0 is reserved.

NOTE: This function returns without setting or getting an environment variable if the “slot” parameter is non-zero. All internal stack functions use slot zero.

TI-RTOS Kernel Users Note: The OS adaptation layer (OS.LIB) implements this function for slot 0 only. The reserved slot 0 is the only slot required by the NDK. Slots 1 to 3 are not implemented. Application programmers should use the standard TI-RTOS Kernel functions Task_setEnv() and Task_getEnv() for private environment pointer storage and retrieval.

TaskSetPri – Set Task Priority

int TaskSetPri(void *hTask,
               int  Priority);

Parameters

Return Value Previous task priority level.

Description Sets the priority of the target task to the specified value. The priority should not be higher than the configured value for the NDK’s Global.highTaskPriLevel property (the priority for high priority NDK tasks), which is 7 by default. 0 is the lowest priority and should be reserved for an idle task. If the specified priority is negative, the task is blocked.

Calling this function may cause a task switch.

TaskSleep – Sleep Task for Period of Time

void TaskSleep(uint32_t Delay);

Parameters

Return Value None.

Description Sleeps the calling task for a period of time as supplied in Delay. The sleep time cannot be zero.

Calling this function may cause a task switch.

TaskYield – Yield Execution to Another Task Thread

void TaskYield();

Parameters None.

Return Value None.

Description This function yields execution to another thread by causing a round-robin task switch among ready task threads executing at the same priority level.

This function always causes a task switch; however, the original calling task may be the next to execute.

2.3 Semaphore Support

The semaphore object provides a method of manipulating counting semaphores using a generic handle. Semaphores can be used for both task synchronization and mutual exclusion.

2.3.1 Function Overview

The Semaphore Object access functions (in functional order) are as follows:

Function Description
SemCreate() Create new semaphore
SemDelete() Delete semaphore
SemPend() Wait on semaphore, optionally for a period of time
SemCount() Get the current semaphore count
SemPost() Release semaphore - increment count
SemReset() Reset semaphore and set new count

2.3.2 Semaphore API Functions

SemCreate – Create New Semaphore

void *SemCreate(int Count);

Parameters

Return Value Handle to semaphore or NULL on error.

Description Creates a new semaphore object with an initial count.

SemCount – Get Current Semaphore Count

int SemCount(void *hSem);

Parameters

Return Value Current semaphore count

Description Returns the current count of the semaphore object.

SemDelete – Delete Semaphore

void SemDelete(void *hSem);

Parameters

Return Value None.

Description Deletes the semaphore object and frees related memory.

Any task currently waiting on this semaphore is blocked forever - even if it originally specified a timeout to SemPend(). With a little care in programming, this will not occur.

SemPend – Wait for a Semaphore

int SemPend(void     *hSem,
            uint32_t Timeout);

Parameters

Return Value The function returns 1 if the semaphore was obtained, and 0 if not.

Description This function waits on a semaphore.

If the semaphore count is greater than 0, the semaphore count is decrement and this function immediately returns.

If the semaphore count is zero, the task is placed on a waiting list for the semaphore and blocked. If the semaphore becomes available in the time period specified in Timeout, the function returns. However, the function returns regardless once the timeout has expired. A timeout value of 0 always returns without blocking or yielding. A timeout value of SemaphoreP_WAIT_FOREVER causes the caller to wait on the semaphore without time out.

The waiting list is first in, first out, without regard to priority. Thus, semaphores can be used to round-robin task threads at different priority levels.

Calling this function may cause a task switch (unless called with Timeout set to 0).

SemPost – Signal a Semaphore

void SemPost(void *hSem);

Parameters

Return Value None.

Description If the semaphore count is greater than 0 (or is equal to 0, but without any pending task threads), the semaphore count is incremented and this function immediately returns.

If the semaphore count is zero and there are tasks threads pending on it, the count remains at zero, and the first thread in the pending list is unblocked.

Calling this function may cause a task switch.

SemReset – Reset Semaphore

void SemReset(void *hSem,
              int  Count);

Parameters

Return Value None.

Description This function resets the semaphore, first setting an initial semaphore count, and then unblocking all tasks that are pending on the semaphore.

This function should be used with care. Tasks that are pending on the semaphore may exhibit unexpected behavior because all tasks pending on the semaphore will return from their respective SemPend() calls regardless of requested timeout. The return value for the respective SemPend() calls will always be correct because one or more tasks may get the semaphore (depending on the value of Count), but tasks that called SemPend() without a timeout may assume they have obtained the semaphore without checking the SemPend() return value.

Calling this function may cause a task switch.

2.4 Memory Allocation Support

As part of normal stack operation, memory will be allocated and freed on a regular basis. It is therefore recommended that a memory support system have the ability to allocate and free small memory blocks in a variety of sizes, without memory fragmentation. The functions described here work on a memory bucket system of predefined fixed sizes. Although it allocates more memory than requested, when the memory is released, it can be reused without fragmentation.

2.4.1 Function Overview

The Memory Allocation access functions (in functional order) are as follows:

Function Description
mmAlloc() Allocate Small Memory Block
mmFree() Free mmAlloc() Memory Block
mmBulkAlloc() Allocate Unrestricted Memory Block
mmBulkFree() Free mmBulkAlloc() Memory Block
mmCopy() Copy a Memory Block
mmZeroInit() Initialize a Memory Block to Zero

2.4.2 Memory Allocation API Functions

mmAlloc – Allocate Memory Block

void *mmAlloc(uint32_t size);

Return Value Pointer to allocated memory or NULL on error.

Description Allocates a memory block of at least size bytes in length. The function should return a pointer to the new memory block, or NULL if memory is not available. The size of the allocation cannot be more than 3068 bytes.

mmFree – Free Memory Block

int mmFree(void *pv);

Return Value If a memory tracking error occurs, this function returns 0; otherwise, it returns 1.

Description Frees a previously allocated memory block by supplying the pointer that mmAlloc() originally returned.

mmBulkAlloc – Allocate Bulk Memory Block

void *mmBulkAlloc(int32_t Size);

Return Value Pointer to allocated memory or NULL on error.

Description Allocates a memory block of at least size bytes in length. The function returns a pointer to the new memory block, or NULL if memory is not available. The size of the allocation is not restricted.

mmBulkFree – Free Bulk Memory Block

void mmBulkFree(void *pv);

Return Value None.

Description Frees a previously allocated memory block by supplying the pointer that mmBulkAlloc() originally returned.

mmCopy – Copy Memory

void mmCopy(void      *pDst,
            void      *pSrc,
            uint32_t  size);

Return Value None.

Description Called to copy size bytes of data memory from the data buffer pSrc to the data buffer pDst.

mmZeroInit – Zero Memory

void mmZeroInit(void      *pDst,
                uint32_t  size);

Return Value None.

Description Called to initialize size bytes of data memory in the data buffer pDst to NULL.

The stack provides an output function called DbgPrintf(). This function currently builds upon XDC’s very configurable System_printf() to log messages. Where these messages go is controlled by the user’s configuration of XDC’s System Module. For more information, including how to configure the System Module, refer to XDC’s documentation.

Note that the severity threshold at which the debug message is recorded can be configured by the user (see _oscfg.DbgPrintLevel), as can the point at which the error causes a system shutdown (see _oscfg.DbgAbortLevel).

Also note that printf-style-formatting limitations (e.g. whether floating point formatting is supported) is a function of how the user configures XDC’s System Module.

FreeRTOS Users Note: As DbgPrint() is built upon XDC’s System_printf(), the logging features of DbgPrintf() are not provided on FreeRTOS. The severity threshold, which may cause the stack to shut down in the most extreme cases, is supported, but no logging is provided.

2.5.1 Standard API Functions

A set of printf-like functions is supported:

int NDK_sprintf(char *s, const char *format, ...);
int NDK_vprintf(const char *format, va_list arg);
int NDK_vsprintf(char *s, const char *format, va_list arg);

2.5.2 Debug API Functions

DbgPrintf – Print a Debug Message to the Debug Log

void DbgPrintf(int ErrLevel,
               char *Format,
               ?);

Parameters

Return Value None.

Description This function prints a debug message to the global debug log buffer. The log buffer is defined as follows:

#define LL_DEBUG_LOG_MAX   1024
extern char DebugLog[LL_DEBUG_LOG_MAX]; // DebugLog Buffer
extern int  DebugLogSize;               // Bytes of data currently in DebugLog

The buffer behaves like one large NULL terminated string. The contents are cleared by setting DebugLogSize to 0.

The value of ErrLevel determines if the message is printed and additionally, if the message results in a system shutdown. Both of these thresholds (printing and shutdown) are set through the OS configuration. The definition of the severity levels are as follows:

#define DBG_INFO  1
#define DBG_WARN  2
#define DBG_ERROR 3
#define DBG_None  4

NOTE: The DbgPrintf() function is not supported for applications that use FreeRTOS.

3 Sockets and Stream IO API

This chapter describes the socket and file API functions.

3.1 File Descriptor Environment

In most embedded operating system environments, support for file descriptors varies greatly. In most cases, only the bare minimum functionality is provided, and trimmed down support functions are provided using the common reserved names (read(), write(), close(), etc.).

As this stack supports the standard sockets interface functions, and these functions require file descriptor support, the stack provides its own small file system. This section describes the basic mechanics of the file system.

3.1.1 Organization

The basic building block of the stack code internally is an object handle. Internally to the stack, both sockets and pipes are addressed by object handles. However, at the application level, sockets and pipes are treated as file descriptors. The file descriptor contains additional state information allowing tasks to be blocked and unblocked based on socket activity.

The stack API supports the use of file descriptors by adding a file descriptor layer of abstraction to the native operating environment. This layer implements the standard sockets and file IO functions. The stack works by associating a file descriptor session with each caller’s thread (or in this terminology, task). In this system, each task has its own file descriptor session. The file descriptor session is used when the task needs to block pending network activity.

Note that although file descriptors can be used in classic functions like select(), in this implementation, they are still handles, not integers. For compatibility, network applications must use the NDK header files, and use INVALID_SOCKET for an error condition (not -1), and refrain from comparing sockets as <0 when checking for validity.

3.1.2 Initializing the File System Environment

To use the file system and socket functions provided by the stack, a task must first allocate a file descriptor table (called a file descriptor session). This is accomplished at the application layer by calling the file descriptor function fdOpenSession().

When the task is finished using the file descriptor API, or when it is about to terminate, the function fdCloseSession() is called.

3.1.2.1 When to Initialize the File Descriptor Environment

For correct stack operation, a task thread must open a file descriptor session before calling any file descriptor related functions, and then close it when it is done.

The simplest way to handle the file descriptor session is to call TaskCreate(), which handles opening and closing the file descriptor session internally.

Another way to handle the file descriptor session is for the task to open a file session when it starts, and close the session when it completes. For example:

Socket Task:

void socket_task(int IPAddr, int TcpPort)
{
    SOCKET s;

    // Open the file session
    fdOpenSession(TaskSelf());

    < socket application code >

    // Close the file session
    fdCloseSession(TaskSelf());
}

Another method is for the task that creates the socket task thread to open the file descriptor session for the child thread. Note that the parent task must guarantee that the child task’s file session is open before the child task executes. This is done via task priority or semaphore, but can complicate task creation. Therefore, it is not the ideal approach.

It is also possible to allow a child task to open its own file session, but allow the parent task to monitor its children and eventually destroy them. Here, the parent task must close the file session of the child task threads it destroys. The child task then blocks when finished instead of terminating its own thread. The following example illustrates this concept:

Child Socket Task:

void child_socket_task(int IPAddr, int TcpPort)
{
    SOCKET s;

    // Open the file session
    fdOpenSession(TaskSelf());

    < socket application code >

    // We are done, but our parent thread will close
    // our file session and destroy this task, so here
    // we just yield.
    TaskYield();
}

The parent task functions would look as follows:

Parent Task Functions:

void create_child_task()
{
    // Create System Tasks

    // Create a child task
    hChildTask = TaskCreate(&child_socket_task, ?);
}

void destroy_child_task()
{
    // First close the child's file session
    // (This will close all open files)
    fdSessionClose(hChildTask);

    // Then destroy the task
    TaskDestroy(hChildTask);
}

3.1.2.2 Auto-Initializing the File Descriptor Environment

For TI-RTOS Kernel users who configure their application using XGCONF or a *.cfg configuration file, the calls to fdOpenSession and fdCloseSession can be configured to be called automatically. This is achieved by setting the following configuration parameter:

var Global = xdc.useModule('ti.ndk.config.Global.xdc');
Global.autoOpenCloseFD = true;

Setting this parameter to true causes calls to fdOpenSession and fdCloseSession to be made automatically in the TI-RTOS Kernel Task module’s create hook function and exit hook function, respectively.

Note that the Global.autoOpenCloseFD parameter is only supported for dynamically-created Tasks created from within a Task context (that is, from within another running Task function). Tasks created statically in the configuration or dynamically in main() or a Hwi or Swi thread do not support this feature.

3.2 File Descriptor Programming Interface

The purpose of supporting a file system is to support the sockets API. Unfortunately, the sockets API is not a complete IO API, as it was originally designed to integrate into the Unix file system. Thus, several file descriptor functions that are important for application programming are not really socket calls at all. The stack library supports a handful of what are normally considered file functions, so that sockets applications can be programmed in a more traditional sense. So that these functions will not conflict with any other file functions in the system, their names have been altered slightly from the standard definitions.

3.2.1 Function Overview

The stream IO object can take two forms. In the vast majority of cases, it will be in the form of a local file descriptor. The following functions can operate on file descriptors:

Function Description
fdOpenSession() Open file descriptor support session
fdCloseSession() Close file descriptor support session
fdClose() Flush stream and close file descriptor (same as standard close())
fdError() Return last error value (same as standard error)
fdPoll() Wait on a list of file descriptor events (same as standard poll())
fdSelect() Wait on one or more file events (same as standard select())
fdSelectAbort() Aborts calls to fdSelect() and fdPoll() with forced timeout condition
fdStatus() Get the current status of a file descriptor (similar to ioctl/FIONREAD)
fdShare() Add a reference count to a file descriptor

The fdSelect() function uses file descriptor sets to specify which file descriptors are being checked for activity and which have activity detected. There is a small set of MACRO functions for manipulating file descriptor sets. These include the following:

Function Description
FD_SET() Add a file descriptor to a file descriptor set.
FD_CLR() Remove a file descriptor from a file descriptor set.
FD_ISSET() Test to see if a file descriptor is included in a file descriptor set.
FD_COPY() Copy a file descriptor set.
FD_ZERO() Clear (initialize) a file descriptor set.

3.2.2 File Descriptor API Functions

fdOpenSession – Open File Descriptor Session

int fdOpenSession(void *hTask);

Parameters

Return Value 1 on success or 0 on error. An error return indicates that a session is already open for the specified task, or that a memory allocation error has occurred.

Description This function opens a file descriptor session on a task thread so that the task can begin using file descriptor and other stream IO functions.

A task thread normally calls fdOpenSession() when it is first created, and fdCloseSession() before it exits. Use of these functions was described in more detail in the previous section.

fdCloseSession – Close File Descriptor Session

void fdCloseSession(void *hTask);

Parameters

Return Value None.

Description This function closes a file descriptor session that was previously opened with fdOpenSession(). When called, any remaining open file descriptors are closed.

A task thread normally calls fdOpenSession() when it is first created, and fdCloseSession() before it exits. Use of these functions was described in more detail in the previous section.

fdClose – Close File Descriptor

int fdClose(void *fd);

Parameters

Return Value 0 on success or -1 on error. When an error occurs, the error type can be obtained by calling fdError() (error is also equal to this function).

Description This function closes the indicated file descriptor.

fdError – Get the Last File Error

int fdError();

Description This function returns the last file error that occurred on the current task. In the SERRNO.H header file, error is equal to this function.

NOTE: The error code returned via fdError() is stored in the file descriptor session associated with a task. If a task calls a file or socket function before it opens a file descriptor session, an error condition results. However, no error code can be stored for retrieval by fdError() because the file descriptor session does not exist to hold it.

fdPoll – Wait on a List of File Descriptor Events

int fdPoll(FDPOLLITEM items,
           uint32_t   itemcnt,
           int32_t    timeout);

Parameters

Return Value

Returns the number of file descriptors in the items list for which the eventsDetected field is non-zero.

Returns SOCKET_ERROR if the caller has not opened a file descriptor session (with fdOpenSession()).

Returns zero (0) under any of the following conditions:

Description The fdPoll() function is a more efficient alternative to the fdSelect() function. It polls the supplied list of sockets, with a timeout specified in milliseconds (or POLLINFTIM for infinite timeout). It has the advantage over fdSelect() because the original list of file descriptors (or sockets) to be examined is not overwritten by the results, and thus can be used multiple times without reconstruction.

The list of file descriptors to check is provided in the items array. The array is of type FDPOLLITEM, which is defined as follows:

typedef struct _fdpollitem {
        void *fd;
        uint16_t   eventsRequested;
        uint16_t   eventsDetected;
} FDPOLLITEM;

The FDPOLLITEM entry contains a file descriptor (or socket) to check, a set of flags for requested events that is initialized by the application, and a set of resulting flags for a detected event that is initialized by the fdPoll() function.

The entry fd is the file descriptor to check. If fd is set to INVALID_SOCKET, or the eventsRequested field is NULL, the item entry is ignored. However, the eventsDetected field is still reset to zero.

The same file descriptor should not appear twice in the list, instead the event flags should be combined on a single entry. (Duplicate descriptors will not cause an error, but will increase system load.)

Valid flags for eventsRequested are one or more of the following:

Valid flags for eventsDetected are the same as above, where all detected conditions are indicated. (Note that POLLNVAL can be set whether or not it was requested in eventsRequested.)

fdSelect – Wait on one or multiple File Events

int fdSelect(int            maxfd,
             fd_set         *readset,
             fd_set         *writeset,
             fd_set         *exceptset,
             struct timeval *timeout);

Parameters

Return Value Returns a positive count of ready descriptors (combined from all three possible sets), 0 on timeout, or -1 on error. When an error occurs, the error type can be obtained by calling fdError().

Description This function allows the task to instruct the stack to wait for any one of multiple events to occur and to wake up the process only when one of more of these events occurs or when a specified amount of time has passed.

The definition of the timeval structure is:

struct timeval {
    int32_t   tv_sec;
    int32_t   tv_usec;
};

Passing in a NULL pointer for timeout specifies an infinite wait period. Passing a valid pointer to a timeval structure with both tv_sec and tv_usec set to zero specifies that the function should not block.

NOTE: This function is less efficient than fpPoll(). In fact, the fdSelect() function calls fdPoll() after rearranging the descriptor sets into an fdPoll() descriptor list.

fdSelectAbort – Terminate a Previous Call to fdSelect() or fdPoll()

void fdSelectAbort(void *hTask);

Parameters

Return Value None.

Description This function aborts a call to fdSelect() or fdPoll() on the specified target thread by simulating a timeout condition (even when no timeout was originally specified). It can be used to wake a thread using a different method than socket or pipe activity. It is useful in callback functions where the handle to the target task thread is known, but where socket calls cannot be easily used.

The return value from the fdSelect() or fdPoll() function called on the target thread is still valid. In other words, if there is pending file descriptor activity, it will still be returned to the caller. However, if the target task thread is blocked in fdSelect() or fdPoll() at the time of the call, the most likely return value is zero for no activity.

If the target thread is not currently pending on a call to fdSelect() or fdPoll(), any subsequent call will be affected. Thus, the target thread is guaranteed to see the abort (although it may be accompanied by actual socket activity). So there is no race condition on calling fdSelectAbort() immediately prior to the target task thread calling fdSelect() or fdPoll().

fdStatus – Get the Current Status of a File Descriptor

int fdStatus(void *fd,
             int  request,
             int  *results);

Parameters

Return Value 0 on success or -1 on error. When an error occurs, the error type can be obtained by calling fdError() (errno is also equal to this function).

Description This function reads current status information about the file descriptor. The descriptor can be either a socket or a pipe object. The following describes the value written to results for the various request types and descriptor types:

fdShare – Add a Reference Count to a File Descriptor

int fdShare(void *fd);

Parameters

Return Value Returns zero on success or -1 on error.

Description This is an optional function for applications that use descriptor sharing. It increments a reference count on the target descriptor, which is then decremented when the application calls fdClose(). It allows the descriptor to be shared among multiple tasks, each calling fdClose() when they are done, and the file descriptor is only closed by the final call. (Note that file descriptors are created with a reference call of 1, meaning that the first call to fdClose() will close the descriptor.)

For example, fdShare() is useful in a case where Task A opens a session and calls NDK_recv() in a loop on a socket. Task B has a loop that calls NDK_send() on the same socket. The call to NDK_send() from Task B will fail and then fdError() will return -1 if you do not call fdOpenSession() and then fdShare() from the second Task after the first Task has opened the socket.

For an example that calls fdShare(), see the contest.c file in the /ti/ndk/tools/console directory.

3.2.3 File Descriptor Set (fd_set) Macros

FD_SET – Add a File Descriptor to a File Descriptor Set

void FD_SET( void   *fd,
             fd_set *pFdSet);

Parameters

Return Value Should be treated as a void function. The true return value is dependent on the implementation of the macro.

Description This function adds a file descriptor to a file descriptor set, typically before using the set in a call to fdSelect(). Note that after declaring an fd_set data type, it should be initialized using FD_ZERO() before attempting to set individual file descriptors.

FD_CLR – Remove a File Descriptor From a File Descriptor Set

void FD_CLR(void   *fd,
            fd_set *pFdSet);

Parameters

Return Value Should be treated as a void function. The true return value is dependent on the implementation of the macro.

Description This function removes a file descriptor from a file descriptor set, typically after the file descriptor has been processed in a loop that continuously checks a file descriptor set.

FD_ISSET – Test to See if a File Descriptor is Included in a File Descriptor Set

int FD_ISSET(void   *fd,
             fd_set *pFdSet);

Parameters

Return Value Returns an int value that should be treated as a TRUE/FALSE condition.

Description This function returns TRUE if the supplied file descriptor is contained in the indicated file descriptor set. This function is typically called after a call to fdSelect() to determine on what file descriptors select has detected activity.

FD_COPY – Copy a File Descriptor Set

void FD_COPY(fd_set *pFdSetSRC,
             fd_set *pFdSetDST);

Parameters

Return Value None.

Description This function is called to make a copy of a file descriptor set. This is typically done if a set needs to be modified, but this original information needs to be maintained.

FD_ZERO – Clear (Initialize) a File Descriptor Set

void FD_ZERO(fd_set *pFdSet);

Parameters

Return Value None.

Description This function is called to clear all bits in a file descriptor set. This should be the first call made on a newly declared fd_set variable.

3.3 Sockets Programming Interface

The socket function API supported by the stack library is not consistent with the standard Berkeley sockets API. For the BSD-compliant socket interface, see Appendix F and the SlNetSock documentation, which is provided with the SimpleLink SDK installation.

This section covers only the IPv4 (AF_INET) family Sockets. For details on IPv6 sockets, see Appendix G of this document. Similarly, for details on Raw Ethernet Sockets, see Section 3.4 of this document.

3.3.1 Enhanced No-Copy Socket Operation

Any performance of any data stream operation suffers when data copies are performed. Although the stack software is designed to use a minimum number of data copies, memory efficiency and API compatibility sometimes require the use of data copy operations.

By default, neither UDP nor RAW sockets use send or receive buffers. However, the sockets API functions NDK_recv() and NDK_recvfrom() require a data buffer copy because of how the calling parameters to the functions are defined. In the stack library, two alternative functions – NDK_recvnc() and NDK_recvncfrom() – are provided to allow an application to get received data buffers directly without a copy operation. When the application is finished with these buffers, it returns them to the system via a call to NDK_recvncfree().

By default, TCP uses both a send and receive buffer. The send buffer is used because the TCP protocol can require reshaping or retransmission of data due to window sizes, lost packets, etc. On receive, the standard TCP socket also has a receive buffer. This coalesces TCP data received from packet buffers. Coalescing data is important for protocols that transmit data in very small bursts (like a telnet session).

For TCP applications that get data in large bursts (and tend not to use flags like MSG_WAITALL on receive), the receive buffer can be eliminated by specifying an alternate TCP stream type of SOCK_STREAMNC (see NDK_socket() ). Without the receive buffer, there is at least one less data copy because TCP will queue up the actual network packets containing receive data instead of copying it into a receive buffer.

Care needs to be taken when eliminating the TCP receive buffer. Here large amounts of packet buffers can be tied up for a small amount of data. Also, because packet buffers come from the HAL, there may be a limited supply available. If the MSG_WAITALL flag is used on an NDK_recv() or NDK_recvfrom() call, it is possible for all packet buffers to be consumed before the specified amount of payload data is received. This would cause a deadlock situation if no socket timeout is specified.

Although TCP sockets that use the SOCK_STREAMNC stream type are 100% compatible with the standard TCP socket type, they can also be used with the NDK_recvnc() and NDK_recvncfrom() functions that UDP and RAW sockets use to eliminate the final data copy from the stack to the sockets application. Using the no copy functions with SOCK_STREAMNC eliminates two data copies from the standard TCP socket. Note that when NDK_recvnc() and NDK_recvncfrom() are used with TCP, out of band data is not supported. If the SO_OOBINLINE socket option is set, the out of band data is retained, but the out of band data mark is discarded. If not using the inline socket option, the out of band data is discarded.

3.3.2 Function Overview

The standard socket access functions are as follows:

Function Description
NDK_accept() Accept a connection on a socket
NDK_bind() Bind a name to a socket
NDK_connect() Initiate a connection on a socket
NDK_getpeername() Return name (address) of connected peer
NDK_getsockname() Return the local name (address) of the socket
NDK_getsockopt() Get the value of a socket option
NDK_listen() Listen for connection requests on a socket
NDK_recv() Receive data from a socket
NDK_recvfrom() Receive data from a socket with the senders name (address)
NDK_send() Send data to a connected socket
NDK_sendto() Send data to a specified destination on an unconnected socket
NDK_setsockopt() Set the value of a socket option
NDK_shutdown() Close one half of a socket connection
NDK_socket() Create a socket
NDK_socketpair() Create socket pair. Redundant; see Section 3.5, Full Duplex Pipes Programming Interface.

The enhanced socket functions are as follows:

Function Description
NDK_recvnc() Receive no-copy data from a socket
NDK_recvncfree() Free buffer obtained from NDK_recvnc() or NDK_recvncfrom()
NDK_recvncfrom() Receive no-copy data from a socket with the senders name (address)

3.3.3 Sockets API Functions

NDK_accept – Accept a Connection on a Socket

SOCKET NDK_accept(SOCKET          s,
                  struct sockaddr *pName,
                  int             *plen);

Parameters

Return Value If it succeeds, the function returns a non-negative integer that is a descriptor for the accepted socket. Otherwise, a value of INVALID_SOCKET is returned and the function fdError() can be called to determine the error:

Description The argument s is a socket that has been created with the NDK_socket() function, bound to an address with NDK_bind(), and is listening for connections after NDK_listen(). The NDK_accept() function extracts the first connection request on the queue of pending connections, creates a new socket with the same properties of socket s and allocates a new file descriptor for the socket. If no pending connections are present on the queue, and the socket is not marked as non-blocking, NDK_accept() blocks the caller until a connection is present. If the socket is marked non-blocking and no pending connections are present on the queue, NDK_accept() returns an error as described above.

The accepted socket may not be used to accept more connections. The original socket s remains open.

The argument pName is a result parameter that is filled in with the address of the connecting entity as known to the communications layer. The domain in which the communication is occurring determines the exact format of the pName parameter. The plen is a value-result parameter; it should initially contain at least sizeof(struct sockaddr), the amount of space pointed to by pName; on return it will contain the actual length (in bytes) of the address returned.

This call is used with connection-based socket types, currently with SOCK_STREAM.

It is possible to select (fdSelect()) a socket for the purposes of doing an accept by selecting it for read.

NDK_bind – Bind a Name (Address) to a Socket

int NDK_bind(SOCKET          s,
             struct sockaddr *pName,
             int             len);

Parameters

Return Value If it succeeds, the function returns 0. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_bind() function assigns a name to an unnamed socket. When a socket is created with NDK_socket() it exists in a name space (address family) but has no name assigned. The NDK_bind() function requests that name be assigned to the socket.

The argument s is a socket that has been created with the NDK_socket() function. The argument pName is a structure of type sockaddr that contains the desired local address. The len parameter contains the size of pName, which is sizeof(struct sockaddr).

NDK_connect – Initiate a Connection on a Socket

int NDK_connect(SOCKET          s,
                struct sockaddr *pName,
                int             len);

Parameters

Return Value If it succeeds, the function returns 0. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_connect() function establishes a logical (and potentially physical) connection from the socket specified by s to the foreign name (address) specified by pName.

If sock is of type SOCK_DGRAM, this call specifies the peer address with which the socket is to be associated; this address is that to which datagrams are to be sent, and the only address from which datagrams are to be received. If the socket is of type SOCK_STREAM, the function attempts to make a connection to another socket.

The argument s is a socket that has been created with the NDK_socket() function. The argument pName is a structure of type sockaddr that contains the desired foreign address. The len parameter contains the size of pName, which is sizeof(struct sockaddr).

Stream sockets may connect only once; while datagram sockets may re-connect multiple times to change their association. The connection may be dissolved by attempting to connect to an illegal address (for example, NULL IP address and Port). Datagram sockets that require multiple connections may consider using the NDK_recvfrom() and NDK_sendto() functions instead of NDK_connect().

It is possible to select (fdSelect()) a socket for the purposes of doing an NDK_connect() by selecting it for writing.

NDK_getpeername – Get Name (Address) of Connected Peer

int NDK_getpeername(SOCKET          s,
                    struct sockaddr *pName,
                    int             *plen);

Parameters

Return Value If it succeeds, the function returns 0. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_getpeername() function returns the name (address) of the connected peer.

The argument pName is a result parameter that is filled in with the address of the connecting entity as known to the communications layer. The domain in which the communication is occurring determines the exact format of the pName parameter. The plen is a value-result parameter; it should initially contain at least sizeof(struct sockaddr), the amount of space pointed to by pName; on return it will contain the actual length (in bytes) of the address returned.

NDK_getsockname – Get the Local Name (Address) of the Socket

int NDK_getsockname(SOCKET          s,
                    struct sockaddr *pName,
                    int             *plen);

Parameters

Return Value If it succeeds, the function returns 0. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_getsockname() function returns the local name (address) of the socket.

The argument pName is a result parameter that is filled in with the address of the connecting entity as known to the communications layer. The domain in which the communication is occurring determines the exact format of the pName parameter. The plen is a value-result parameter; it should initially contain at least sizeof(struct sockaddr), the amount of space pointed to by pName; on return it will contain the actual length (in bytes) of the address returned.

NDK_getsockopt – Get the Value of a Socket Option Parameter

int NDK_getsockopt(SOCKET s,
                   int   level,
                   int   op,
                   void  *pbuf,
                   int   *pbufsize);

Parameters

Return Value If it succeeds, the function returns 0. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_getsockopt() function returns the options associated with a socket. Options may exist at multiple protocol levels; they are always present at the uppermost socket level.

When manipulating socket options, the level at which the option resides and the name of the option must be specified. To manipulate options at the socket level, level is specified as SOL_SOCKET. To manipulate options at any other level, the protocol number of the appropriate protocol controlling the option is supplied. In this implementation, only SOL_SOCKET, IPPROTO_IP, and IPPROTO_TCP are supported.

The parameters pbuf and pbufsize identify a buffer in which the value for the requested option(s) are to be returned. pbufsize is a value-result parameter, initially containing the size of the buffer pointed to by pbuf, and modified on return to indicate the actual size of the value returned.

Most socket-level options utilize an int parameter for pbuf. SO_LINGER uses a struct linger parameter, which specifies the desired state of the option and the linger interval (see below). SO_SNDTIMEO and SO_RCVTIMEO use a struct timeval parameter.

The following options are recognized at the socket level:

struct linger lingerConfig;

socklen_t optlen = sizeof(struct linger);
memset(&lingerConfig, 0, optlen);

getsockopt(lSocket, SOL_SOCKET, SO_LINGER , &lingerConfig, &optlen);

optlen = sizeof(struct linger);
memset(&lingerConfig, 0, optlen);
lingerConfig.l_onoff = 1;
lingerConfig.l_linger = 100;

setsockopt(lSocket, SOL_SOCKET, SO_LINGER , &lingerConfig, optlen);

Options that are not Berkeley standard:

The following options are recognized at the IPPROTO_IP level:

The following options are recognized at the IPPROTO_TCP level:

Options that are not Berkeley standard:

/*
 * The settings of TCP_SACK_REXMIT_TIMER_RATIO, TCP_SACK_REXMIT_SEGMENT,
 * and TCP_SACK_TICKS_REXMT definitions allows tuning of retransmission
 * characteristics of SACK.
 */
#define TCP_SACK_REXMIT_TIMER_RATIO   4  /* SACK Retransmission Timer Ratio */
                                         /* t_trtx / TCP_SACK_REXMIT_TIMER_RATIO */
#define TCP_SACK_REXMIT_SEGMENT       1  /* SACK Retransmission Congestion  */
                                         /* Window Segment Count (in MSS unit) */
#define TCP_SACK_TICKS_REXMIT         2  /* If a SACK option is received,  */
                                         /* the current traditional retransmit */
                                         /* timeout tick value is incremented */
                                         /* by TCP_SACK_TICKS_REXMIT. */

NDK_listen – Listen for Connection Requests on Socket

int NDK_listen(SOCKET s,
               int    maxcon);

Parameters

Return Value If it succeeds, the function returns 0. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_listen() function listens for connection requests on a socket.

To accept connections, a socket is first created with NDK_socket(). The NDK_listen() function is called to specify a willingness to accept incoming connections and a queue limit for incoming connections. New connections are accepted by calling the NDK_accept() function. The NDK_listen() function applies only to sockets of type SOCK_STREAM.

The maxcon parameter defines the maximum length to which the queue of pending connections may grow. If a connection request arrives with the queue full, the client receives an error with an indication of ECONNREFUSED.

NDK_recv – Receive Data from a Socket

int NDK_recv(SOCKET s,
             void   *pbuf,
             int    size,
             int    flags);

Parameters

Return Value If it succeeds, the function returns the number of bytes received. Returns 0 on connection oriented sockets where the connection has been closed by the peer (or socket shutdown for read). Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_recv() function attempts to receive data from a socket. It is normally used on a connected socket (see NDK_connect() ). The data is placed into the buffer specified by pbuf, up to a maximum length specified by size. The options in flags can be used to change the default behavior of the operation.

The function returns the length of the message on successful completion.

For a datagram type socket, the receive operation always copies one packet’s worth of data. If the buffer is too short to hold the entire packet, the data is truncated and lost.

If no messages are available at the socket, it waits for a message to arrive, unless the socket is non-blocking. The function normally returns any data available, up to the requested amount, rather than waiting for receipt of the full amount requested; this behavior is affected by the options specified in flags as well as the socket-level options SO_RCVLOWAT and SO_RCVTIMEO described in NDK_getsockopt() .

The select call (fdSelect()) may be used to determine when more data arrives.

The flags argument to an NDK_recv() call is formed by combining one or more of the following flags:

NDK_recvfrom – Receive Data from a Socket with the Sender’s Name (Address)

int NDK_recvfrom(SOCKET s,
                 void            *pbuf,
                 int             size,
                 int             flags,
                 struct sockaddr *pName,
                 int             *plen);

Parameters

Return Value If it succeeds, the function returns the number of bytes received. Returns 0 on connection oriented sockets where the connection has been closed by the peer (or socket shutdown for read). Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_recvfrom() function attempts to receive data from a socket. It is normally called with unconnected, non-connection oriented sockets. The data is placed into the buffer specified by pbuf, up to a maximum length specified by size. The options in flags can be used to change the default behavior of the operation. The name (address) of the sender is written to pName.

The argument pName is a result parameter that is filled in with the address of the sending entity as known to the communications layer. The domain in which the communication is occurring determines the exact format of the pName parameter. The plen is a value-result parameter; it should initially contain at least sizeof(struct sockaddr), the amount of space pointed to by pName; on return it will contain the actual length (in bytes) of the address returned.

The function returns the length of the message on successful completion.

For a datagram type socket, the receive operation always copies one packet’s worth of data. If the buffer is too short to hold the entire packet, the data is truncated and lost.

If no messages are available at the socket, it waits for a message to arrive, unless the socket is non-blocking. The function normally returns any data available, up to the requested amount, rather than waiting for receipt of the full amount requested; this behavior is affected by the options specified in flags as well as the socket-level options SO_RCVLOWAT and SO_RCVTIMEO described in NDK_getsockopt() .

The select call (fdSelect()) may be used to determine when more data arrives.

The flags argument to an NDK_recv() call is formed by combining one or more of the following flags:

NDK_recvnc – Receive Data from a Socket without Buffer Copy

int NDK_recvnc(SOCKET s,
               void   **ppbuf,
               int    flags,
               void   **phBuffer);

Parameters

Return Value If it succeeds, the function returns the number of bytes received. Returns 0 on connection oriented sockets where the connection has been closed by the peer (or socket shutdown for read). Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_recvnc() function attempts to receive a data buffer from a socket. It is normally used on a connected socket (see NDK_connect() ). A pointer to the data buffer is returned in ppbuf. A system handle used to free the buffer is returned in phBuffer. Both of these pointers must be valid. The options in flags can be used to change the default behavior of the operation.

The function returns the length of the message on successful completion.

The receive operation always returns one packet buffer. The caller has no control over the size of the data returned in this buffer.

If no messages are available at the socket, this call waits for a message to arrive, unless the socket is non-blocking. The function returns the data buffer available.

When the caller no longer needs the data buffer, it is returned to the system by calling NDK_recvncfree(). Repeated failure to free buffers will eventually cause the stack to stop receiving data.

This function cannot be used with sockets of type SOCK_STREAM. When used with sockets of type SOCK_STREAMNC, out of band data marks are cleared.

The select call (fdSelect()) may be used to determine when more data arrives.

The flags argument to an NDK_recv() call can be one of the following flags:

NDK_recvncfree – Return a Data Buffer Obtained from a No-Copy Receive Operation

void NDK_recvncfree(void *hBuffer);

Parameters

Return Value None.

Description The NDK_recvncfree() function frees a data buffer obtained from calling either NDK_recvnc() or NDK_recvncfrom(). The calling parameter hBuffer is the handle of the buffer to free (not the pointer to the buffer).

NDK_recvncfrom – Receive Data and the Sender’s Name From a Socket Without Buffer Copy

int NDK_recvncfrom(SOCKET s,
                   void            **ppbuf,
                   int             flags,
                   struct sockaddr *pName,
                   int             *plen,
                   void            **phBuffer);

Parameters

Return Value If it succeeds, the function returns the number of bytes received. Returns 0 on connection oriented sockets where the connection has been closed by the peer (or socket shutdown for read). Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_recvncfrom() function attempts to receive a data buffer from a socket. It is normally called with unconnected, non-connection oriented sockets. A pointer to the data buffer is returned in ppbuf. A system handle used to free the buffer is returned in phBuffer. Both of these pointers must be valid. The options in flags can be used to change the default behavior of the operation. The name (address) of the sender is written to pName.

The argument pName is a result parameter that is filled in with the address of the sending entity as known to the communications layer. The domain in which the communication is occurring determines the exact format of the pName parameter. The plen is a value-result parameter; it should initially contain at least sizeof(struct sockaddr), the amount of space pointed to by pName; on return it will contain the actual length (in bytes) of the address returned.

The function returns the length of the message on successful completion.

The receive operation always returns one packet buffer. The caller has no control over the size of the data returned in this buffer.

If no messages are available at the socket, this call waits for a message to arrive, unless the socket is non-blocking. The function returns the data buffer available.

When the caller no longer needs the data buffer, it is returned to the system by calling NDK_recvncfree(). Repeated failure to free buffers will eventually cause the stack to stop receiving data.

This function cannot be used with sockets of type SOCK_STREAM. When used with sockets of type SOCK_STREAMNC, out of band data marks are cleared.

The select call (fdSelect()) may be used to determine when more data arrives.

The flags argument to an NDK_recv() call can be one of the following flags:

NDK_send – Transmit Data to a Socket

int NDK_send(SOCKET s,
             void   *pbuf,
             int    size,
             int    flags);

Parameters

Return Value If it succeeds, the function returns the number of bytes sent. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_send() function attempts to send data on a socket. It is used on connected sockets only (see NDK_connect() ). The data to send is contained in the buffer specified by pbuf, with a length specified by size. The options in flags can be used to change the default behavior of the operation.

The function returns the length of the data transmitted on successful completion.

If a thread does not contain calls to fdOpenSession() and fdCloseSession(), the NDK_send() function returns a value of -1.

For a datagram type socket, the send operation always copies one packet’s worth of data. If the buffer size is too large to be transmitted in a single packet, an error code of EMSGSIZE is returned.

If there is not transmit buffer space available on a stream type socket, the function waits for space to become available, unless the socket is non-blocking. The function normally transmits all the specified data.

The select call (fdSelect()) may be used to determine when the socket is able to write.

The flags argument to an NDK_send() call is formed by combining one or more of the following flags:

NDK_sendto – Transmit Data on a Socket to Designated Destination

int NDK_sendto(SOCKET          s,
               void            *pbuf,
               int             size,
               int             flags,
               struct sockaddr *pName,
               int             len);

Parameters

Return Value If it succeeds, the function returns the number of bytes sent. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_sendto() function attempts to send data on a socket to a specified destination. It is used on unconnected, non-connection oriented sockets only (see NDK_connect() ). The data to send is contained in the buffer specified by pbuf, with a length specified by size. The options in flags can be used to change the default behavior of the operation.

The argument pName is a pointer to the address of the destination entity as known to the communications layer. The domain in which the communication is occurring determines the exact format of the pName parameter. The len parameter should contain the size of name, which is sizeof(struct sockaddr).

The function returns the length of the data transmitted on successful completion.

For a datagram type socket, the send operation always copies one packet’s worth of data. If the buffer size is too large to be transmitted in a single packet, an error code of EMSGSIZE is returned.

The select call (fdSelect()) may be used to determine when the socket is able to write.

The flags argument to an NDK_sendto() call is formed by combining one or more of the following flags:

NOTE: The native operation of the socket NDK_sendto operation is to connect, sendto, and disconnect. Sockets that are not bound to a local IP or local port would have an ephemeral port selected every time in order to override this behavior and to ensure that the ephemeral port is not selected every time it is recommended that customers do an NDK_bind() to port 0. This selects the first free port not in use and all subsequent communication uses the same port.

NDK_setsockopt – Set the Value of a Socket Option Parameter

int NDK_setsockopt(SOCKET s,
                   int    level,
                   int    op,
                   void   *pbuf,
                   int    bufsize);

Parameters

Return Value If it succeeds, the function returns 0. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_setsockopt() function sets option values associated with a socket. Options may exist at multiple protocol levels; they are always present at the uppermost socket level.

When manipulating socket options, the level at which the option resides and the name of the option must be specified. To manipulate options at the socket level, level is specified as SOL_SOCKET. To manipulate options at any other level, the protocol number of the appropriate protocol controlling the option is supplied. In this implementation, only SOL_SOCKET, IPPROTO_IP, and IPPROTO_TCP are supported.

The parameters pbuf and bufsize identify a buffer that holds the value for the specified option.

Most socket-level options utilize an int parameter for pbuf. SO_LINGER uses a struct linger parameter, which specifies the desired state of the option and the linger interval; see the example provided for NDK_getsockopt(). SO_SNDTIMEO and SO_RCVTIMEO use a struct timeval parameter. The IP-level options IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP accept a struct ip_mreq parameter.

The socket options supported for NDK_setsockopt() are the same as defined for NDK_getsockopt(), with the exception of SO_TYPE and SO_ERROR, which cannot be set.

Please see the description of NDK_getsockopt() for a list of socket options.

NOTE: The SO_SNDBUF and SO_RCVBUF options can only be set if there is no transmit or receive data pending at the socket. In general, the buffer sizes should only be configured before the socket is bound or connected. Buffer sizes set on listen sockets will propagate to spawned accept sockets.

NDK_shutdown – Close One Half of a Connected Socket

int NDK_shutdown(SOCKET s,
                 int    how);

Parameters

Return Value If it succeeds, the function returns 0. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_shutdown() function causes all or part of a full-duplex connection on the socket associated with a socket to be shut down. If how is SHUT_RD (0), further receives will be disallowed. If how is SHUT_WR (1), further sends will be disallowed. If how is SHUT_RDWR (2), further sends and receives will be disallowed.

NDK_socket – Create a Socket

SOCKET NDK_socket(int domain,
                  int type,
                  int protocol);

Parameters

Return Value If it succeeds, the function returns a file descriptor representing the socket. Otherwise, a value of INVALID_SOCKET is returned and the function fdError() can be called to determine the error:

Description The NDK_socket() function creates a socket, an endpoint for communication and returns the socket in the form of a file descriptor.

The domain parameter specifies a communications domain within which communication will take place; this selects the protocol/address family that should be used. This will always be AF_INET or AF_INET6 in this implementation.

The socket type parameter specifies the semantics of communication. The defined types are:

The protocol parameter specifies a particular protocol to be used with the socket. In this implementation of the stack, SOCK_STREAM must use IPPROTO_TCP, SOCK_DGRAM must use IPPROTO_UDP, and SOCK_RAW is unrestricted. To remain compatible with the industry, this parameter can be set to NULL on SOCK_STREAM or SOCK_DGRAM.

3.4 Raw Ethernet Sockets Programming Interface

Raw Ethernet sockets are a special type of sockets that allow an application to send and receive entire Ethernet packets without any intermediate processing by the stack, like stripping off headers, checksum calculations, etc., that are typically done for IPv4, IPv6 sockets. These sockets are expected to be used by applications that have very definite performance requirements and need ultimate control over packet processing. The Raw Ethernet sockets have both no-copy send and receive APIs. This facilitates the application’s performance requirements even further by reducing any overhead on the send or receive data paths due to memory allocation and copying. A corresponding Raw Ethernet Module has also been added to the core NDK stack that interfaces with the NIMU layer to send/receive data. For more details on the Raw Ethernet module, see Section A.15 of this document.

3.4.1 Function Overview

The following is a complete list of socket APIs implemented for the Raw Ethernet socket family (AF_RAWETH).

Function Description
NDK_socket() Creates a raw Ethernet socket
NDK_shutdown() Close one half of the socket
NDK_getsockopt() Gets the value of a socket option
NDK_setsockopt() Sets the value of a socket option
NDK_send() Sends raw Ethernet data using a previously opened raw Ethernet socket
NDK_getsendncbuff() Allocate space for a raw Ethernet packet and retrieve handle for the data buffer. This is used in conjunction with NDK_sendnc API for no-copy send.
NDK_sendnc() Send out raw Ethernet data without making a copy of it during the Tx path
NDK_sendncfree() Free buffer obtained from NDK_getsendncbuff() API
NDK_recvnc() Return a data buffer obtained from a no-copy receive operation
NDK_recvncfree() Free buffer obtained from NDK_recvnc() API

3.4.2 Raw Ethernet Sockets API Functions

This section describes the socket APIs listed before in detail and in particular when used with AF_RAWETH family sockets.

NDK_socket – Create a raw Ethernet socket.

SOCKET NDK_socket( int domain,
                   int type,
                   int protocol );

Parameters

Return Value If it succeeds, the function returns a file descriptor representing the socket. Otherwise, a value of INVALID_SOCKET is returned and the function fdError() can be called to determine the error:

Description The NDK_socket() function creates a socket, an endpoint for communication and returns the socket in the form of a file descriptor. The domain parameter specifies a communications domain within which communication will take place; this selects the protocol/address family that should be used. To create a raw Ethernet socket, the domain must be specified as AF_RAWETH. The socket type parameter specifies the semantics of communication. For raw Ethernet sockets, this parameter must be set to SOCK_RAWETH. The protocol parameter specifies the protocol to be used for the socket. This can be set to any custom protocol type for a raw Ethernet socket. Standard protocol types like IP (0x800), IPv6 (0x86DD), VLAN (0x8100), PPPoE Control (0x8863), PPPoE Data (0x8864) must not be used here for protocol parameter.

NDK_shutdown – Close one half of a connected socket.

int NDK_shutdown( SOCKET s,
                  int    how );

Parameters

Return Value If it succeeds, the function returns 0. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_shutdown() function causes all or part of a full-duplex connection on the socket associated with a socket to be shut down. If how is SHUT_RD (0), further receives will be disallowed. If how is SHUT_WR (1), further sends will be disallowed. If how is SHUT_RDWR (2), further sends and receives will be disallowed.

NDK_getsockopt – Get the value of a socket option.

int NDK_getsockopt(SOCKET s,
                   int    level,
                   int    op,
                   void   *pbuf,
                   int    *pbufsize);

Parameters

Return Value If it succeeds, the function returns 0. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_getsockopt() function returns the options associated with a socket. Options are always present at the uppermost socket level (SOL_SOCKET) for raw Ethernet sockets. When manipulating socket options, the level at which the option resides and the name of the option must be specified. The parameters pbuf and pbufsize identify a buffer in which the value for the requested option(s) are to be returned. pbufsize is a value-result parameter, initially containing the size of the buffer pointed to by pbuf, and modified on return to indicate the actual size of the value returned.

The following socket (SOL_SOCKET) level options are recognized for raw Ethernet sockets:

NOTE: The SO_IFDEVICE option must be configured to successfully use a raw Ethernet socket for communication.

NOTE: Options like SO_SNDWAT, SO_SNDTIMEO, etc., are not supported for raw Ethernet sockets, since there is no buffering on the send path and the operation is synchronous; i.e., once NDK_send()/NDK_sendnc() APIs are invoked for raw Ethernet sockets, these calls actually return only after enqueuing the data in the driver queue. Hence, no buffering or timeouts are required.

NDK_setsockopt – Set the value of a socket option.

int NDK_setsockopt( SOCKET s,
                    int    level,
                    int    op,
                    void   *pbuf,
                    int    bufsize);

Parameters

Return Value If it succeeds, the function returns 0. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_setsockopt() function sets option values associated with a socket. Options are always present at the uppermost socket level (SOL_SOCKET) for raw Ethernet sockets. When manipulating socket options, the level at which the option resides and the name of the option must be specified. The parameters pbuf and bufsize identify a buffer that holds the value for the specified option. Most socket-level options utilize an int parameter for pbuf.

The socket options supported for NDK_setsockopt() are the same as the ones specified in the NDK_getsockopt() API.

NDK_send – Transmit raw Ethernet data using a socket.

int NDK_send( SOCKET s,
              void   *pbuf,
              int    size,
              int    flags );

Parameters

Return Value If it succeeds, the function returns the number of bytes sent. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_send() function attempts to send data on a socket. The data to send is contained in the buffer specified by pbuf, with a length specified by size. The options in flags can be used to change the default behavior of the operation. No flag options are defined for raw Ethernet sockets. The function returns the length of the data transmitted on successful completion.

The data buffer specified here is allocated and freed up by the application itself. The packet buffer is copied over to the packet allocated on the transmit path by the Raw Ethernet module and hence the application buffer (pbuf) freeing is the responsibility of the application when this API returns. Based on the socket handle specified to this call, the raw Ethernet module retrieves the socket priority and device on which this packet needs to be transmitted and does the needful to transmit the data. All packets transmitted using the specified socket would inherit their priority from the socket.

The format of the data buffer sent as input to this function is shown as follows:

Figure 3-1 Raw Ethernet Buffer Format

NDK_getsendncbuff – Retrieve handle to buffer application can fill with raw Ethernet data to send.

int NDK_getsendncbuff( SOCKET   s,
                       uint32_t bufSize,
                       void     **phBuf,
                       void     **phPkt );

Parameters

Return Value If it succeeds, the function returns 0. Also, the phbuf and phPkt pointers are filled in with valid pointers to the data and packet buffers just allocated. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description This API needs to be called by the raw Ethernet application to obtain a buffer handle to use to transmit data without copy using the NDK_sendnc() API. This function ensures that bufSize byte memory is allocated for the raw Ethernet data buffer. It also allocates memory for the packet to hold this data buffer. The application can fill the required data in the buffer just obtained and needs to send both the data buffer and the packet pointers to NDK_sendnc() API to finally send out the packet. This packet and the data buffer are freed by the Ethernet driver once the transmit completes successfully. However, if the application would like to free up the buffer obtained because of some error it encountered during send process, it would have to use the NDK_sendncfree API and specify the packet buffer handle as a parameter and both the data buffer and packet buffer will be freed up.

NDK_sendnc – Send data out on the socket without any copy on transmit path.

int NDK_sendnc( SOCKET s,
                void   *pbuf,
                int    size,
                void   *hPkt,
                int    flags );

Parameters

Return Value If it succeeds, the function returns number of bytes transmitted. Otherwise, a value of -1 is returned, and the function fdError() can be called to determine the error:

Description The NDK_sendnc() API function attempts to send the data buffer and packet specified using the raw Ethernet socket handle provided. It doesn’t make any copies of the data on the transmit path and, therefore, provides a significant performance gain to the application using it. The buffer pointer and the packet handle must be obtained using the NDK_getsendncbuff() API before this function is called. The size of data buffer specified as input to this function must not exceed the size of buffer allocated using the NDK_getsendncbuff() API. No option flags are defined at this time for this API. This packet and the data buffer are freed by the Ethernet driver once the transmit completes successfully. However, If an error is returned from this API, the application is responsible for freeing up the packet and data buffers obtained earlier from the NDK_getsendncbuff() API by calling the NDK_sendncfree() API with the packet handle as input.

The format of the data buffer sent as input to this function is expected to be the following:

Figure 3-2 Raw Ethernet Buffer Format

NDK_sendncfree – Free the packet and data buffers obtained using the NDK_getsendncbuff() API.

void NDK_sendncfree( void *hFrag );

Parameters

Return Value None

Description The NDK_sendncfree() function frees the packet and data buffer obtained from calling the NDK_getsendncbuff() API. The calling parameter, hFrag, is the handle of the packet buffer to free (not the pointer to the buffer handle).

NDK_recvnc – Receive data from a socket without buffer copy

int NDK_recvnc( SOCKET s,
                void   **ppbuf,
                int    flags,
                void   **phBuffer );

Parameters

Return Value If it succeeds, the function returns the number of bytes received. Otherwise, a value of -1 is returned and the function fdError() can be called to determine the error:

Description The NDK_recvnc() function attempts to receive a data buffer from a socket. A pointer to the data buffer is returned in ppbuf. A system handle used to free the buffer is returned in phBuffer. Both of these pointers must be valid. The options in flags can be used to change the default behavior of the operation. No flags are defined for raw Ethernet sockets. The function returns the length of the message on successful completion. The receive operation always returns one packet buffer. The caller has no control over the size of the data returned in this buffer. If no messages are available at the socket, this call waits for a message to arrive for the receive timeout specified on this socket. If no timeout is specified this call blocks forever waiting for data to arrive on the socket. The function returns the data buffer available. When the caller no longer needs the data buffer, it is returned to the system by calling NDK_recvncfree(). Repeated failure to free buffers eventually causes the stack to stop receiving data.

NOTE: Take care when using the NDK_recvnc() API, because packet buffers come from the driver; there may be a limited supply available. The packet buffers must be promptly freed up by the application once processing is complete.

NDK_recvncfree – Return a data buffer obtained from a no-copy receive operation

void NDK_recvncfree(void *hBuffer);

Parameters

Return Value None

Description The NDK_recvncfree() function frees a data buffer obtained from calling NDK_recvnc(). The calling parameter hBuffer is the handle of the buffer to free (not the pointer to the buffer).

3.5 Full Duplex Pipes Programming Interface

Although sockets can be used for inter-task communications, it is not the most efficient method. The stack provides a second data communications model called pipes, which allow for local connection oriented communications.

A pipe is a full duplex connection oriented file descriptor. When a pipe is created, both ends of the pipe are returned to the caller as file descriptors.

Communication is performed using the standard file and sockets API functions. All the file descriptor functions are supported with pipes: fdSelect(), fdClose(), fdError().

Also, socket functions NDK_send() and NDK_recv() write and read data through the pipe. Both functions also support the following standard sockets message flags when using pipes:

Pipes are connection oriented, thus, when one end closes, the other end is altered by an error return from NDK_send() or NDK_recv(). It is therefore possible to make a blocking call on NDK_recv() without concern that the function will be deadlocked if the other end terminates the connection.

3.5.1 Pipe API Functions

Because pipes share file descriptor and IO functions with sockets, the only pipe oriented function is the creation of the connected pair.

NDK_pipe – Create a Full Duplex Pipe

int NDK_pipe( void **pfd1,
              void **pfd2);

Parameters

Return Value Returns zero on success or -1 on error. A more detailed error code can be found by calling fdError().

Description Creates a pre-connected full duplex pipe. The returned file descriptors can be used with all the fd file descriptor functions, as well as the NDK_send() and NDK_recv() socket functions.

Pipes are connection oriented, so like TCP, a read or write call can return ENOTCONN when the connection is broken by one side or the other.

NOTE: Both file descriptors must be closed to correctly close down (and free) a pipe.

3.6 Internet Group Management Protocol (IGMP)

Internet Group Management Protocol (IGMP) is designed to help routers in routing IP multicast traffic. Each router can have multiple ports, and it is inefficient for the router to replicate every IP multicast packet out of each active port. Using the IGMP protocol, the multicast router is able to keep track of which IP multicast addresses need to be routed to each individual port. This allows the router to limit IP multicast transmission to only those ports that require the multicast traffic.

The IGMP protocol assumes a client/server relationship between endpoints. The IGMP server is run by the multicast router to get IP multicast information about all the client on each of its individual ports. The IGMP client is only concerned with communicating its own multicast requirements to the local IGMP server, so that it will get the IP multicast packets that it requires.

The NDK does not support IP multicast routing, so there is no need to use IGMP in server mode. However, the software does support IGMP client operation.

The IGMP client module indicates to the IGMP server which multicast IP addresses that the client needs to receive. The IGMP API will also maintain the Ethernet multicast MAC address list at the Ethernet driver level.

An application can join or leave a multicast group using the well-known NDK_setsockopt() API. To do this, use the NDK_setsockopt() and NDK_getsockopt() APIs with the IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP options. For more details on options for joining or leaving a multicast group, see getsockopt() and setsockopt().

4 Configuration and Initialization

This chapter discusses the Configuration Manager API, which is the recommended method for configuring your application. In addition, this chapter discusses the initialization sequence for the NDK.

4.1 Configuration Methods

There are two ways to configure an application’s use of NDK modules:

Internally, the XGCONF configuration tool generates C code that uses the Configuration Manager API to update the configuration database.

NOTE: You should not mix configuration methods. If you have NDK applications that use the API-based configuration method, you should either continue to use that method or convert the configuration entirely to an *.cfg file configuration using XGCONF. If a project uses both methods, there will be conflicts between the two configurations.

4.2 Configuration Manager API

The Configuration Manager is a collection of API functions to help you create and manipulate a configuration. The manager API is independent of the configuration specification.

The configuration is arranged as a database with a master key (called Tag) that defines the class of configuration item. A second key (called Item) determines the sub-item type in the tag class. For each tag and item, there can be multiple instances. Items can be further distinguished by their instance value.

The configuration is based on an active database. That is, any change to the database can cause an immediate reaction in the system. For example, if a route is added to the configuration, it is added to the system route table. If the route is then removed from the configuration, it is removed from the system route table.

To facilitate the active procession of configuration changes in a generic fashion, the configuration API allows the installation of service provider callback functions that are called to handle specific tag values in the configuration.

Configurations can be set active or inactive. When a configuration is active, any change to the configuration results in a change in the system. When a configuration is inactive, it behaves like a standard database. Part of the main initialization sequence is to make the system configuration active, and then inactive when shutting down.

Both the configurations and configuration entries are referenced by a generic handle. Configuration functions (named as CfgXxx()) take a configuration handle parameter, while configuration entry functions (name as CfgEntryXxx()) take a configuration entry handle parameter. These handles are not interchangeable.

Configuration entry handles are referenced. This means that each handle contains an internal reference count so that the handle is not destroyed by one task while another task expects it to stay valid. Functions that return a configuration entry handle supply a referenced handle in that its reference count has already been incremented for the caller. The caller can hold this handle indefinitely, but should dereference it when it is through. There are three calls that dereference a configuration entry handle. These are: CfgRemoveEntry(), CfgGetNextEntry(), and most simply CfgEntryDeRef(). See individual function descriptions for more information.

The PPP module in the stack library and several modules in the NETTOOLS library make use of a default configuration to store and search for data. The default configuration is accessed by passing in a NULL configuration handle to any function that takes the hCfg parameter (except CfgFree()). The default configuration is specified by calling CfgSetDefault().

4.2.1 Function Overview

The configuration access functions (in functional order) are as follows:

Configuration Functions:

Function Description
CfgNew() Create a new configuration
CfgFree() Destroy a configuration
CfgSetDefault() Set default configuration
CfgGetDefault() Get default configuration
CfgLoad() Load configuration from a linear memory buffer
CfgSave() Save configuration to a linear memory buffer
CfgSetExecuteOrder() Set the tag initialization and shutdown order on execute
CfgExecute() Make the configuration active or inactive
CfgSetService() Sets service callback function for a particular tag
CfgAddEntry() Add a configuration entry to a configuration
CfgRemoveEntry() Remove entry from configuration
CfgGetEntryCnt() Get the number of item instances for a tag/item pair
CfgGetEntry() Get a referenced handle to a configuration entry
CfgGetNextEntry() Return supplied entry handle and get next entry handle
CfgGetImmediate() Get configuration entry data without getting an entry handle

Configuration Entry Functions:

Function Description
CfgEntryRef() Add a reference to a configuration entry handle
CfgEntryDeRef() Remove a reference to a configuration entry handle
CfgEntryGetData() Get configuration entry data from entry handle
CfgEntrySetData() Replace data block of entry data using entry handle
CfgEntryInfo() Get information on a configuration entry handle

For API details, refer to the CFG Module in the NDK API Reference Guide.

4.3 Configuration Specification

Specifying all the available configuration options for the stack would require a separate document. This section details that part of the configuration that is relied upon by the Network Control (NC) initialization functions, or the services contained in the NETTOOLS library. The stack itself does not reference the configuration system. It has its own simpler method that is detailed in Appendix A, but it is redundant when using the configuration API. In fact, they conflict, as the Network Control functions assume full control of it.

4.3.1 Organization

As already mentioned, the configuration is arranged as a database with the value Tag as a major key, and the value Item as a minor key. Every major stack configuration component has a major key (Tag) value, including: network services (protocol servers), connected IP networks, gateway routes, connected client entities, global system information, and low-level stack configuration.

Most of these tags require service callback functions to implement the system functionality. For example, when an IP network is added using the CFGTAG_IPNET tag, there must be a function that makes the corresponding system calls that adds the network to the system route table. All these server callback functions are contained in the NETCTRL directory. Although source code to these functions is provided, many of the system calls they make can only be understood by reading the attached appendices.

The tag values defined are:

NOTE: Configuration support is not available for IPv6 stack management. IPv6 stack management can be done only using the APIs and data structure exported by it. IPv6 stack APIs and data structures are discussed in Appendix G of this document.

4.3.2 Network Service Specification (CFGTAG_SERVICE)

The network services tag is perhaps the most time saving feature of the configuration. It allows you to instruct the system of what tasks to execute, and how they should be executed. It is also the most complicated configuration entry.

Network services are identified by a configuration Tag parameter value of CFGTAG_SERVICE.

Note that all these services are obtained directly from the NETTOOLS services API. The configuration system adds a level of abstraction so that a list of services can be added to a configuration, and then the service provider callback functions contained in the Network Control initialization routines can automatically load the services at runtime without having to call the NETTOOLS API directly.

4.3.2.1 Service Types

The type of service is indicated by the value of the Item parameter supplied to the CfgAddEntry() function. The defined service types include (by Item):

4.3.2.2 Common Argument Structure

Each individual service has its own specific configuration instance structure, but they all share a generic argument structure. This is defined as follows:

// Common Service Arguments
typedef struct _ci_srvargs {
    uint32_t Item;        // Copy of Item value (init to NULL)
    void *hService;       // Handle to service (init to NULL)
    uint32_t Mode;        // Flags
    uint32_t Status;      // Service Status (init to NULL)
    uint32_t ReportCode;  // Standard NETTOOLS Report Code
    uint32_t IfIdx;       // If physical Index
    uint32_t IPAddr;      // Host IP Address
    void(*pCbSrv)(uint, uint, uint, void *); // CbFun for status change
} CISARGS;

The individual fields are defined as follows:

4.3.2.3 Individual Configuration Entry Instance Structures

The following code defines the instance structures used for each of the defined configuration entries using the configuration service tag. Note that all structures contain the previously mentioned CISARGS structure. Some services require more information and their configuration entry structure contains an additional parameter structure as defined in the service’s NETTOOLS API. Others do not require a parameter structure.

// Telnet Entry Data
typedef struct _ci_service_telnet {
    CISARGS cisargs;      // Common arguments
    NTPARAM_TELNET param; // Telnet parameters
} CI_SERVICE_TELNET;

// NAT Service Entry Data
typedef struct _ci_service_nat {
    CISARGS cisargs;     // Common arguments
    NTPARAM_NAT param;   // NAT parameters
} CI_SERVICE_NAT;

// DHCP Server Entry Data
typedef struct _ci_service_dhcps {
    CISARGS cisargs;     // Common arguments
    NTPARAM_DHCPS param; // DHCPS parameters
} CI_SERVICE_DHCPS;

// DHCP Client Service
typedef struct _ci_service_dhcpc {
    CISARGS cisargs;    // Common arguments
    NTPARAM_DHCP param; // DHCP parameters
} CI_SERVICE_DHCPC;

// DNS Server Service
typedef struct _ci_service_dnss {
    CISARGS cisargs;    // Common arguments
} CI_SERVICE_DNSSERVER;

4.3.2.4 Specifying Network Services

4.3.2.4.1 Specifying Telnet Service Using the Configuration

The Telnet service can be specified as public because it can connect using any IP address, or an IP address of a specific interface. When accepting connections to any system IP address, the service is specified with the CALLBYIP flag and an IP address of INADDR_ANY. When a private connection is desired, the service is specified by the physical interface on which connections are allowed to occur. Because an IP address is required to initialize the service, the RESOLVEIP flag should also be set in the latter case. For example, the following code specifies that the telnet server should run using the IP address INADDR_ANY.

telnet_example()
{
    CI_SERVICE_TELNET telnet;

    bzero( &telnet, sizeof(telnet) );
    telnet.cisargs.IPAddr = INADDR_ANY;
    telnet.cisargs.pCbSrv = &ServiceReport;
    telnet.param.MaxCon = 2;
    telnet.param.Callback = &ConsoleOpen;

    CfgAddEntry( hCfg, CFGTAG_SERVICE, CFGITEM_SERVICE_TELNET,
                 0, sizeof(telnet), (unsigned char *)&telnet, 0 );
}

The above code is all that is required when using the configuration system to invoke this service.

4.3.2.4.2 Specifying DHCP Server Service Using the Configuration

Because the DHCP server service executes on a specific interface, it is never executed based on an IP address. Thus, it cannot be used with the CALLBYIP flag in the standard configuration service structure. However, because an IP host address is required to initialize the service on a specific interface, the RESOLVEIP flag should be set in cases where the IP address is not pre-assigned. For example, the following code specifies that the DHCP server should run on the interface specified by the physical index dhcpsIdx. Here, the home networks have already been written to the configuration, so the RESOLVEIP flag is not necessary. The address pool being used is already stored in IPPoolBase and PoolSize. The DHCPS is requested to report the local server address as a DNS server to DHCP clients.

dhcp_server_example()
{
    CI_SERVICE_DHCPS dhcps;

    bzero( &dhcps, sizeof(dhcps) );
    dhcps.cisargs.Mode = CIS_FLG_IFIDXVALID;
    dhcps.cisargs.IfIdx = dhcpsIdx;
    dhcps.cisargs.pCbSrv = &ServiceReport;

    // Report our address as a DNS server to clients, and use the
    // network's local domain name.
    dhcps.param.Flags = DHCPS_FLG_LOCALDNS | DHCPS_FLG_LOCALDOMAIN;

    // Assign the IP address pool
    dhcps.param.PoolBase = IPPoolBase;
    dhcps.param.PoolCount = PoolSize;

    CfgAddEntry( hCfg, CFGTAG_SERVICE, CFGITEM_SERVICE_DHCPSERVER, 0,
                 sizeof(dhcps), (unsigned char *)&dhcps, 0 );
}

The above code is all that is required when using the configuration system to invoke this service.

4.3.2.4.3 Specifying DHCP Client Service Using the Configuration

Because the DHCP client service executes on a specific interface, it is never executed based on an IP address. Thus, it cannot be used with the CALLBYIP flag in the standard configuration service structure. Also, because the service runs without an IP host address, the RESOLVEIP flag should never be set. For example, the following code specifies that the DHCP client should run on the interface specified by the physical Index dhcpIdx.

dhcp_client_example()
{
    CI_SERVICE_DHCPC dhcpc;

    bzero( &dhcpc, sizeof(dhcpc) );
    dhcpc.cisargs.Mode = CIS_FLG_IFIDXVALID;
    dhcpc.cisargs.IfIdx = dhcpIdx;
    dhcpc.cisargs.pCbSrv = &ServiceReport;
    CfgAddEntry( hCfg, CFGTAG_SERVICE, CFGITEM_SERVICE_DHCPCLIENT, 0,
                 sizeof(dhcpc), (unsigned char *)&dhcpc, 0 );
}

The above code is all that is required when using the configuration system to invoke this service.

4.3.2.4.4 Specifying DNS Service Using the Configuration

The service can be specified as public because it can connect using any IP address, or an IP address of a specific interface. When accepting connections to any system IP address, the service is specified with the CALLBYIP flag and an IP address of INADDR_ANY. When a private connection is desired, the service is specified by the physical interface on which connections are allowed to occur. Because an IP address is required to initialize the service, the RESOLVEIP flag should also be set in the latter case.

For example, the following code specifies that the server should run using the IP address INADDR_ANY.

dns_server_example()
{
    CI_SERVICE_DNSSERVER dnss;

    bzero( &dnss, sizeof(dnss) );
    dnss.cisargs.IPAddr = INADDR_ANY;
    dnss.cisargs.pCbSrv = &ServiceReport;
    CfgAddEntry( hCfg, CFGTAG_SERVICE, CFGITEM_SERVICE_DNSSERVER, 0,
                 sizeof(dnss), (unsigned char *)&dnss, 0 );
}

The above code is all that is required when using the configuration system to invoke this service.

4.3.2.4.5 Specifying NAT Service Using the Configuration

Because the NAT service executes on a specified public interface, it is never executed based on an IP address. Thus, it cannot be used with the CALLBYIP flag in the standard configuration service structure. In addition, because the public IP host address is required to initialize the service, the RESOLVEIP flag should be set when the IP address is not pre-assigned.

For example, the following code specifies that the NAT service should run on the interface specified by the physical index natIdx. Here, the DHCP client service is used to obtain the public IP address (the address assigned to natIdx), so at this point the IP address is unknown. Thus, the RESOLVEIP flag is set in the execution mode parameter. This informs the configuration service manager not to invoke NAT until it has resolved an IP address for the target interface. The RESTART flag is also set to tell the service to restart NAT if a public IP address is lost and regained. In this example, it is assumed that all networks in the 192.168.x.x/255.255.0.0 subnet are part of the NAT group to be translated.

The MTU parameter to the NAT configuration allows the programmer to set a limit on the MTU negotiated during a TCP connection. This prevents TCP packet traffic from being unnecessarily fragmented. For example, when routing between Ethernet and PPPoE over NAT, the MTU should be set to the smaller MTU of the two, which is PPPoE’s limit of 1492. In the example below, it is assumed that the system is Ethernet to Ethernet, and thus, it uses the full 1500.

nat_service_example()
{
    CI_SERVICE_NAT nat;

    bzero( &nat, sizeof(nat) );

    // Do not start NAT until we resolve an IP address on its IF
    nat.cisargs.Mode = CIS_FLG_IFIDXVALID | CIS_FLG_RESOLVEIP |
                       CIS_FLG_RESTARTIPTERM;
    nat.cisargs.IfIdx = natIdx;
    nat.cisargs.pCbSrv = &ServiceReport;

    // Include all 192.168.x.x addresses in NAT group
    nat.param.IPVirt = htonl(0xc0a80000);
    nat.param.IPMask = htonl(0xffff0000);
    nat.param.MTU = 1500;

CfgAddEntry( hCfg, CFGTAG_SERVICE, CFGITEM_SERVICE_NAT, 0,
             sizeof(nat), (unsigned char *)&nat, 0 ); }

The above code is all that is required when using the configuration system to invoke this service.

To use NAT, it must be configured via the following function. Also, by default, the NAT code is not called by the stack. This increases stack efficiency when NAT is not in use. To enable the NAT module, the IpNatEnable element of the stack configuration structure must be set.

Note that when using the NAT service feature in NETTOOLS or when using the configuration system, this low-level configuration is not required.

NatSetConfig – Configure the Network Address Translation Module

void NatSetConfig( uint32_t IPAddr,
                   uint32_t IPMask,
                   uint32_t IPServer,
                   uint32_t MTU);

Parameters

Description This function configures NAT with a virtual network and a physical server. Note that both the virtual and physical addresses must also be contained in the stack’s route table. NAT should only be used when the stack is acting as a router, and when there are more than one Ethernet devices present.

The MTU parameter must be in the range of 64 to 1500. When set less than 1500, TCP connection negotiation will be altered so that TCP sessions through NAT will be limited to the MTU specified. This prevents unnecessary fragmentation when using NAT over dissimilar packet devices. (Note this MTU is the IP packet MTU, not the TCP MTU.)

4.3.3 IP Network Specification (CFGTAG_IPNET)

The IPNET entry specifies what IP networks are to appear on which physical interfaces. When specifying an IPNET entry to the configuration, the Tag parameter is set to CFGTAG_IPNET, and the Item parameter is set to the Index (1 to n) of the physical interface on which the network is to appear.

The IPNET entry instance structure is defined as follows:

// IPNet Instance
typedef struct _ci_ipnet {
    uint32_t    NetType;   // Network address type flags
    uint32_t    IPAddr;    // IP Address
    uint32_t    IPMask;    // Subnet Mask
    void        *hBind;    // Binding handle (initially NULL)
    char        Domain[CFG_DOMAIN_MAX]; // IPNet Domain Name
} CI_IPNET;

The individual fields are defined as follows:

4.3.4 IP Gateway Route Specification (CFGTAG_ROUTE)

The ROUTE entry specifies a route from one network to another via a specified IP gateway. When specifying a ROUTE entry to the configuration, the Tag parameter is set to CFGTAG_ROUTE, and the Item parameter is not used (set to zero).

The ROUTE entry instance structure is defined as follows:

// Route Instance
typedef struct _ci_route {
    uint32_t     IPDestAddr;  // Destination Network Address
    uint32_t     IPDestMask;  // Subnet Mask of Destination
    uint32_t     IPGateAddr;  // Gateway IP Address
    void         *hRoute;     // Route handle (initially NULL)
} CI_ROUTE;

The individual fields are defined as follows:

4.3.5 Client Record Specification (CFGTAG_CLIENT)

The CLIENT entry specifies a record of a client that appears on the indicated physical interface. When specifying a CLIENT entry to the configuration, the Tag parameter is set to CFGTAG_CLIENT, and the Item parameter is set to the index (1 to n) of the physical interface on which the client appears.

Client records exist for two purposes:

  1. They are used to resolve DNS queries on virtual networks.
  2. They are used by the DHCP server service to track DHCP clients on the serviced virtual network.

Client records are created automatically in some DHCP server configurations (when using an address pool), but they can also be added manually. This allows an application to build a pre-defined fixed list of clients and their designated IP addresses on a virtual (home) network.

The CLIENT entry instance structure is defined as follows:

typedef struct _ci_client {
    uint32_t  ClientType;          // Entry Status
    uint32_t  Status;              // DHCPS Status (init to ZERO)
    uint32_t  IPAddr;              // Client IP Address
    char      MacAddr[6];          // Client Physical Address
    char      Hostname[CFG_HOSTNAME_MAX]; // Client Hostname
    uint32_t  TimeStatus;          // Time of last status change
    uint32_t  TimeExpire;          // Expiration Time from TimeStatus
} CI_CLIENT;

The individual fields are defined as follows:

4.3.6 Client User Account (CFGTAG_ACCT)

The ACCT entry specifies an account record of a client that has access to the system. When specifying an ACCT entry to the configuration, the Tag parameter is set to CFGTAG_ACCT, and the Item parameter is set to the account type. The NDK has only one generic account type. PPP authentication realms use this type. Valid types values are:

The ACCT entry instance structure is defined as follows:

typedef struct _ci_acct {
    uint32_t Flags;                     // Account Flags
    char     Username[CFG_ACCTSTR_MAX]; // Username
    char     Password[CFG_ACCTSTR_MAX]; // Password
} CI_ACCT;

The individual fields are defined as follows:

4.3.7 System Information Specification (CFGTAG_SYSINFO)

The SYSINFO entry contains various types of global system information. There is no service callback function associated with these entries, as they are static information only. When specifying a SYSINFO entry to the configuration, the Tag parameter is set to CFGTAG_SYSINFO, and the Item parameter is set to the system information item in question.

Note that the first 256 values for Item are reserved for items that exactly match the corresponding DHCP protocol information tag value. For example:

#define CFGITEM_DHCP_DOMAINNAMESERVER 6 // Stack's DNS servers
#define CFGITEM_DHCP_HOSTNAME 12 // Stack's host name

These values are read by various network services, and are written in one of two ways.

First, when the standard DHCP client is executing, it will take full control over the first 256 Item values. It fills in the entries when it obtains its address lease, and purges them when the lease expires. There is a set of default entries that the DHCP client will always request. Additional information requests can be made by configuring the DHCP client, and the resulting replies will be added to the configuration.

Second, when there is no DHCP client service, the network application must manually write values to the configuration for the Item values it views as important. A minimum configuration would include hostname, domain name, and a list of domain name servers. Note that multiple IP addresses should be stored as multiple instances of the same Item, not concatenated together with a longer byte length.

4.3.8 Extended System Information Tags

The following tag values are reserved for NDK and services configuration (see Appendix C for more information on PPP realms):

4.3.9 OS / IP Stack Configuration Item Specification (CFGTAG_OS, CFGTAG_IP)

The OS and IP tags specify entries that alter various configuration options that can be adjusted in the operating system and low-level stack operation. When specifying an entry to the configuration, the Tag parameter is set to CFGTAG_OS or CFGTAG_IP, and the Item parameter is set to the configuration item to set (these are listed below).

Creating a configuration entry results in an alteration of the system’s internal configuration structures, but because these entries are also part of the configuration object (hCfg), they can be stored off and recorded as part of the CfgLoad()/CfgSave() functionality. Thus, using the configuration API has a significant advantage over modifying the internal structures manually.

Removing an entry restores the default value to the internal stack configuration. Entries that are not present cannot be read, and an error return on read implies the entry is in its default state.

The following is the list of configuration items. All items are of type int or uint. They correspond exactly to the internal system configuration structures. For more information on these fields, see the internal configuration discussion in both the Section 2.1.1 section earlier in this document, and the Configuring the Stack section in the attached appendix Section A.11.

When creating a configuration entry for one of these tags, the entry should be specified as unique. For example, to enable routing in the IP stack that code would be as follows:

// Enable IP routing
uint32_t tmp = 1;
CfgAddEntry(hCfg, CFGTAG_IP, CFGITEM_IP_IPFORWARDING,
            CFG_ADDMODE_UNIQUE, sizeof(uint), (unsigned char *)&tmp, 0);

As another example, to set the size of the boot task’s stack, the code would be as follows:

int rc = 4096;
CfgAddEntry(hCfg, CFGTAG_OS, CFGITEM_OS_TASKSTKBOOT,
    CFG_ADDMODE_UNIQUE, sizeof(uint), (unsigned char *)&rc, 0);

The following item values correspond directly to the OS and IP Stack configuration structures _oscfg and _ipcfg.

For more information on these structures, see Section 2.1.1 and Section A.11.1.

When Tag is CFGTAG_OS, the value of Item can be one of the following:

When Tag is CFGTAG_IP, the value of Item can be one of the following:

4.4 Initialization Procedure

For applications that do not use XGCONF configuration, the basic process of stack initialization is as follows:

  1. Initialize the operating system environment with the initialization function NC_SystemOpen( Priority, OpMode ). This function must always be called first - before any other NDK related function. The calling parameters determine the priority and operating mode of the network event scheduler.
  2. Create a new configuration via CfgNew().
  3. Build the new configuration via configuration API calls, or load a previous configuration from non-volatile memory using CfgLoad().
  4. Boot the stack with the configuration by calling NC_NetStart( hCfg, pfnStart, pfnStop, pfnNetIP ) with a handle to the configuration, plus pointers to three user supplied callback functions for start, stop, and IP address change operations. The NC_NetStart() function does not return until the stack session has terminated. The configuration handle hCfg becomes the default configuration for the system.
  5. After some preliminary initialization, the NC_NetStart() function creates a new thread that calls the user supplied callback function for the start operation. At this point, the callback function creates task threads for its networking requirements. This start function does not need to return immediately, but should return at some point - i.e., the callback function should not take permanent control of the calling thread. If system shutdown is initiated before the start function returns, some resources may not be freed.
  6. Under normal operation, the network does not shut down until the NC_NetStop() function is called. At some point after a call to NC_NetStop(), the original NC_NetStart() thread calls the user supplied callback function for the stop operation. In this callback function, the application shuts down any operation it initiated in the start callback function and frees any allocated resources. After the stop callback function returns, NDK functionality is no longer available.
  7. The original call to NC_NetStart() returns with the return value as set by the return parameter passed in the call to NC_NetStop(). The application can immediately reboot the NDK by calling NC_NetStart() again, with or without reloading a new configuration. This is useful for a reboot command.

When the system is ready for a final shutdown, the following actions are performed:

  1. When NC_NetStart() returns and the session is over, call the CfgFree() function to free the configuration handle created with CfgNew().
  2. After all resources have been freed, call the NC_SystemClose() function to complete the system shutdown.

4.5 Network Control Initialization Procedure (NETCTRL)

The stack library includes code to perform system initialization based on the configuration. Initialization of the scheduling routines is performed by a network control layer called NETCTRL.

If you use the XGCONF configuration tool to configure the NDK, the following custom C functions are generated for you to manage NDK startup and thread scheduling. See the TI Network Developer’s Kit (NDK) User’s Guide (SPRU523) for details. You should take care not to create functions in your own application with these names.

4.5.1 Initialization Procedure

If you use the Configuration Manager API to configure your application, see Section 4.4 for the stack initialization process.

If you use the XGCONF configuration tool to configure the NDK, the ti_ndk_config_Global_stackThread() function is automatically generated to handle the initialization process and to act as the NDK scheduler thread. You can use XGCONF to configure Hook functions to be called at various point during the initialization and thread scheduling. See the section on “Global Hook Configuration” in the TI Network Developer’s Kit (NDK) User’s Guide (SPRU523).

If you use the XGCONF configuration tool to configure the NDK, the basic process of stack initialization occurs as follows:

  1. Run the Stack Thread Begin (Global.stackBeginHook) hook function if one is configured. Note that no NDK-related code can run in this hook function because the NC_SystemOpen() function has not yet run.
  2. Initialize the operating system environment with the initialization function NC_SystemOpen( Priority, OpMode ). This function must always be called first - before any other NDK related function. The calling parameters determine the priority and operating mode of the network event scheduler.
  3. Create a new configuration via CfgNew() (which is called from generated code).
  4. Run the Stack Thread Initialization (Global.stackInitHook) hook function if one is configured.
  5. The generated ti_ndk_config_Global_stackThread() function builds the new configuration via code generated from the XGCONF configuration.
  6. Boot the stack with the configuration by calling NC_NetStart( hCfg, pfnStart, pfnStop, pfnNetIP ) with a handle to the configuration, plus pointers to the three generated functions for start, stop, and IP address change operations. The NC_NetStart() function does not return until the stack session has terminated.
  7. After some preliminary initialization, the NC_NetStart() function creates a new thread that calls the generated networkOpen() function.
  8. Run the Network Open (Global.networkOpenHook) hook function if one is configured.
  9. The networkOpen() function creates task threads for its networking requirements.
  10. Under normal operation, the network does not shut down until the NC_NetStop() function is called. NC_NetStop() is called by the generated networkClose() function.
  11. Run the Network Close (Global.networkCloseHook) hook function if one is configured. In this hook function, the application should shut down any operation it initiated in the Network Open hook function and free any allocated resources.
  12. After the networkClose() function returns, NDK functionality is no longer available.
  13. The original call to NC_NetStart() returns with the return value as set by the return parameter passed in the call to NC_NetStop().
  14. The application can immediately reboot the NDK by calling NC_NetStart() again, with or without reloading a new configuration. This is useful for a reboot command.

When the system is ready for a final shutdown, the following actions are performed:

  1. When NC_NetStart() returns and the session is over, run the Stack Thread Delete (Global.stackDeleteHook) hook function if one is configured.
  2. Call the CfgFree() function to free the configuration handle created with CfgNew().
  3. After all resources have been freed, call the NC_SystemClose() function to complete the system shutdown.

4.5.2 Function Overview

The system initialization access functions (in functional order) are as follows:

Function Description
NC_SystemOpen() Initiate a system session
NC_SystemClose() Full system shutdown
NC_NetStart() Start the network with a supplied configuration
NC_NetStop() Halt the network, and pass a return code the caller of the NC_NetStart() function

For API details, refer to the NETCTRL Module in the NDK API Reference Guide.

5 Network Tools Library - Support Functions

Included with the stack package is a library of network tools. It provides auxiliary functionality to the stack library and contains source written to the socket layer that would normally be considered application level code. The library file is called NETTOOLS.LIB, and can be accessed by an application that includes the file NETTOOLS.H.

The support supplied by NETTOOLS can be categorized into two classes: support functions and services. The support functions consist of a programming API that can aid the development of network applications, while services are servers that execute on the stack platform.

This section describes the NETTOOLS support functions. Please note that these services are all IPv4 based. Based on the IPv6 documentation, these applications can be easily re-written to use IPv6 sockets instead for communication.

5.1 Generic Support Calls

This section contains a selection of functions that can be very useful when programming network applications. Some are standard Berkeley Software Distribution (BSD) Socket APIs, while others are custom to the stack–designed to save you the time and trouble of programming directly to the stack API.

5.1.1 Function Overview

The following is a summary of the support functions described in this section:

Function Description
inet_addr() Convert a string to a 32 bit IP address in network format
inet_aton() Convert a string to an in_addr structure record
NtAddNetwork() Add a host network to a logical interface handle
NtRemoveNetwork() Remove a network added with NtAddNetwork()
NtAddStaticGateway() Add a static gateway route to the route table
NtRemoveStaticGateway() Remove a static gateway route
NtIfIdx2Ip() Get the IP host address assigned to a physical interface Index
NtGetPublicHost() Get the system public IP address and domain name
NtIPN2Str() Convert 32 bit IP address in network format to string

5.1.2 Network Tools Support API Functions

inet_addr – Return 32-bit Binary Network Ordered IPv4 Address

uint32_t inet_addr(char *strptr);

Parameters

Return Value IP address or NULL.

Description This function converts an IP address printed in a character string to a 32-bit network ordered IP address value. Note that leading 0s in the address string are interpreted as octal. The function returns NULL on failure.

This function actually calls inet_aton(), which is the better form of the function.

inet_aton – Convert IP Address from String and Return in in_addr Structure

int inet_aton( char           *strptr,
               struct in_addr *pa );

Parameters

Return Value 1 upon success; 0 upon failure.

Description This function converts an IP address printed in a character string to a 32-bit network ordered IP address value. Note that leading 0s in the address string are interpreted as octal. The function return writes the IP address into the in_addr structure pointed to by the pa parameter. The function returns 1 on success and 0 on failure.

NtAddNetwork – Add Host Network to Interface by IF Handle

void *NtAddNetwork( void     *hIF,
                    uint32_t IPHost,
                    uint32_t IPMask);

Parameters

Return Value Handle to network binding on success or NULL on failure.

Description This function attempts to add the specified IP host address (and mask) to the specified logical interface handle. The function returns a handle to the binding that binds the IP address to the interface. On an error, the function returns NULL. The most common error would be that adding the host address caused a duplicate IP indication from another host.

NOTE: In place of this function, consider using the configuration system with the CFGTAG_IPNET configuration entry (see Section 4.3.3).

NtRemoveNetwork – Remove Host Network from Interface

void NtRemoveNetwork( void *hBind );

Parameters

Return Value None.

Description This function removes a network that was previously added with NtAddNetwork().

NtAddStaticGateway – Add Static Gateway Route to the Route Table

void *NtAddStaticGateway( uint32_t IPDestAddr,
                          uint32_t IPDestMask,
                          uint32_t IPGateAddr);

Parameters

Return Value Handle to newly created route or NULL on error.

Description This function adds a static gateway route to the system route table.

IPDestAddr is the IP base address of the IP network of the network that is made accessible via the IP gateway. This value should be pre-masked with the IPDestMask so that:

(IPDestAddr & IPDestMask) = IPDestMask

This is used as a sanity check by the system. For a default route, the value is zero.

IPDestMask is the mask of the IP network accessible by the IP gateway. For a host route, the value is 0xFFFFFFFF, while for a default route, the value is zero.

IPGateAddr is the IP address of the gateway through which the specified IP network is accessible. It must be an IP address that is available on a locally connected network, i.e., one gateway cannot point to another.

The function returns a handle to the route created by this configuration entry. All routes are represented as route handles internally to the stack. This is discussed further in the appendices at the end of this document. Note that the handle returned here is not referenced (see the appendix for more details). All it means for the purposes of this function is that the handle can be discarded by the caller. It will remain valid until the route is removed via NtRemoveStaticGateway().

NOTE: In place of this function, consider using the configuration system with the CFGTAG_ROUTE configuration entry (see Section 4.3.4).

NtRemoveStaticGateway – Remove Static Gateway Route from the Route Table

int NtRemoveStaticGateway( uint32_t IPTarget );

Parameters

Return Value Returns 1 if the route was removed, or 0 if it was not found.

Description This function removes a static gateway route from the system route table. It searches for the route by destination IP address and will remove the first matching static route it finds. Note that only routes with both the GATEWAY and STATIC flags set are considered for removal.

NtIfIdx2Ip – Get the 32-bit Representation of the IP Address of an Interface Index

int NtIfIdx2Ip( uint32_t IfIdx,
                uint32_t *pIPAddr );

Parameters

Return Value Returns 1 if an address was found, or 0 if it was not found.

Description This function obtains the first IP host address found that is assigned to the supplied interface Index. The host address (in network format) is written to the pointer pIPAddr.

NtGetPublicHost – Get the System Public IP Address and Domain Name

int NtGetPublicHost( uint32_t      *pIPAddr,
                     uint32_t      MaxSize,
                     unsigned char *pDomain);

Parameters

Return Value Returns 1 if information was found, or 0 if it was not found.

Description This function gets the best IP address and domain name to use for access to the external network. For determining the best address and domain name, public addresses and domain names are preferred over IP addresses and domain names of virtual networks. The IP address (in network format) is written to pIPAddr, and the domain name is copied to pDomain.

NtIPN2Str – Convert 32-bit IP Address in Network Format to String

void NtIPN2Str( uint32_t IPAddr,
                char     *pStrBuffer );

Parameters

Return Value None

Description This function performs a sprintf() of the IP address supplied in IPAddr to the buffer supplied in pStrBuffer. Note that no buffer size is provided. This is because the size is deterministic, and will not exceed 16 characters (including the NULL terminator).

5.2 DNS Support Calls

The concepts and code behind the Unix gethostbyname() and gethostbyaddr() functions is extensive, and there are public domain versions available, which can be easily run on the IP stack library.

Although the code to support the whole name, address and server database is quite large, the basic name resolution functions are quite useful. For this reason, the stack provides a basic form of these function calls, without incurring the overhead associated with a full implementation. The DNS resolver used by these client functions is the same as accessed by the DNS server. When the configuration contains client machine records (i.e., controls local domain names), these entries are checked when the matching domain is encountered. Otherwise (and for all other queries), the query is resolved via external DNS servers.

In addition to providing a more compact implementation, the calls provided here are reentrant, which is not true of the standard Unix counterparts.

5.2.1 Function Overview

The following is a summary of the support functions described in this section:

Function Description
DNSGetHostname() Return the hostname of the current host
DNSGetHostByAddr() Resolve a hostname from an IP address
DNSGetHostByName() Resolve a hostname and IP address from a hostname

5.2.2 Standard Types and Definitions

5.2.2.1 Host Entry Structure

The DNS client functions all take a pointer to a buffer. They treat this buffer as a pointer to a host entry structure. If the function takes a pointer to a scrap buffer, a host entry structure is allocated from the start of this scrap buffer. Thus, on successful return from one of these calls, the pointer to the scrap buffer may be treated as a pointer to a host entry structure.

The structure differs slightly from the conventional definition. It is defined as follows:

//
// Host Entry Structure
//
struct _hostent {
    char        *h_name;     // Official name of host
    int         h_addrtype; // Address Type (AF_INET)
    int         h_length;   // Address Length (4)
    int         h_addrcnt;  // Number of IP addresses found
    uint32_t    h_addr[8];  // List of up to 8 IP addresses (network format)
};

typedef struct _hostent HOSTENT;

5.2.2.2 Function Return Codes

DNS functions that return an error code use the following definitions. Those that are obtained directly from a DNS response packet are so noted:

Code Value Description
NDK_DNS_NOERROR 0 (DNS Reply Code) No error
NDK_DNS_EFORMERR 1 (DNS Reply Code) Format error
NDK_DNS_ESERVFAIL 2 (DNS Reply Code) Server failure
NDK_DNS_ENXDOMAIN 3 (DNS Reply Code) Non-existent domain
NDK_DNS_ENOTIMP 4 (DNS Reply Code) Not implemented
NDK_DNS_EREFUSED 5 (DNS Reply Code) Query refused
NDK_DNS_EOVERFLOW 16 Scrap Buffer Overflow
NDK_DNS_EMEMERROR 17 Memory Allocation Error (used for packets and temp storage)
NDK_DNS_ESOCKETERROR 18 Socket Error (call fdError() for socket error number)
NDK_DNS_ENODNSREPLY 19 No DNS server response

5.2.3 DNS Support API Functions

DNSGetHostname – Return the Hostname of the Current Host

int DNSGetHostname( char *pNameBuf,
                    int  size );

Parameters

Return Value Error code as defined above.

Description This function is quite similar to BSD’s gethostname(). It requests the hostname of the system’s public IP address (as obtained from NtGetPublicHost()). The hostname is copied into the buffer pointed to by pNameBuf with a maximum size of size. The name is NULL terminated when space allows.

DNSGetHostByAddr – Resolve a Hostname from an IP Address

int DNSGetHostByAddr( uint32_t IPAddr,
                      void     *pScrapBuf,
                      int      size );

Parameters

Return Value Error code as defined above.

Description This function is quite similar to BSD’s gethostbyaddr(). It uses DNS to resolve a hostname from the supplied IP address. On a successful return, pScrapBuf can be treated as a HOSTENT structure. The size of the scrap buffer (size) must be greater than the size of the structure as the structure will contain pointers into the scrap buffer, and the scrap buffer is also used for temporary name storage. 512 bytes should be sufficient for most requests.

DNSGetHostByName – Resolve a Hostname/Address from a Hostname

int DNSGetHostByName( char *Name,
                      void *pScrapBuf,
                      int  size);

Parameters

Return Value Error code as defined above.

Description This function is quite similar to BSD’s gethostbyname(). It uses DNS to resolve an official hostname and address from the supplied hostname. On a successful return, pScrapBuf can be treated as a HOSTENT structure. The size of the scrap buffer (size) must be greater than the size of the structure as the structure will contain pointers into the scrap buffer, and the scrap buffer is also used for temporary name storage. 512 bytes should be sufficient for most requests.

If the hostname Name is terminated with a dot (.), the dot is removed prior to lookup. If a dot appears anywhere in Name, an initial lookup on the unaltered name is attempted. If Name does not contain a dot, or if the initial lookup fails, the default domain name (from NtGetPublicHost()) is appended to the end of the supplied name. For example, if the domain name obtained from NtGetPublicHost() was ti.com, then a request for host.sc would attempt to resolve host.sc first, and then host.sc.ti.com, while a request for host would attempt to resolve host.sc.ti.com on the initial attempt.

5.3 TFTP Support

TFTP is supported via the received function. More information on TFTP can be found in RFC783, released by the Internet Engineering Task Force (IETF) organization.

5.3.1 TFTP Support API Functions

TFTP is accessed through this API. The network tools include the file NETTOOLS.H, which is required.

NtTftpRecv – Retrieve Data from a TFTP Server

int NtTftpRecv( uint32_t TftpIp,
                char     *szFileName,
                char     *pFileBuffer,
                uint32_t *pFileSize,
                uint16_t *pErrorCode);

Parameters

Return Value This function returns an error code indicating the results of the operation. Negative codes are error conditions.

In the following cases, pFileSize is set to the actual file size:

In the following cases, pFileSize is set to the actual number of bytes copied:

In the case of TFTPERROR_ERRORREPLY, the server error code written to pErrorCode should be one of the following standard TFTP codes, and the error message is copied to pFileBuffer:

Description TFTP (Trivial File Transfer Protocol), allows files to be transferred from a remote machine.

This function attempts to receive the file with the filename designated by szFileName from the TFTP server with the IP address in TftpIp, and copy the data into the memory buffer pointed to by pFileBuffer. Note that when specifying the name of the file in szFileName, certain operating systems have case sensitive naming conventions.

On entry, the parameter pFileSize must point to the size of the buffer pointed to by pFileBuffer. If the value at *pFileSize is null, the pFileBuffer parameter can be NULL.

This function attempts to receive the entire file, even if the buffer space is insufficient. The return value indicates if the file was received.

A return value of 1 indicates that the file was received and copied into the buffer. A return value of 0 indicates that the file was received, but was too large for the specified buffer. In both these cases, the actual size of the file in bytes is written back to *pFileSize.

A negative return value indicates that an error has occurred during transfer. In this case, the number of bytes actually consumed in the buffer is written back to pFileSize. An error return of TFTPERROR_ERRORREPLY is a special return value that indicates that an error code was returned from the TFTP server. In this case, the server’s TFTP error code is written to pErrorCode, and the server’s TFTP error message string is copied to the data buffer pointer to by pFileBuffer.

5.4 TCP/UDP Server Daemon Support

A server daemon is a single network task that monitors the socket status of multiple network servers. When activity is detected, the daemon creates a task thread specifically to handle the new activity. This is more efficient than having multiple servers, each with their own listening thread.

5.4.1 Server Daemon Support API Functions

Entries in the server daemon are created and destroyed through the following APIs. The network tools include the file NETTOOLS.H, which is required.

DaemonNew – Create a New TCP/UDP Server Entry

void *DaemonNew( uint32_t Type,
                 uint32_t LocalAddress,
                 uint32_t LocalPort,
                 int      (*pCb)(SOCKET,uint32_t),
                 uint32_t Priority,
                 uint32_t StackSize,
                 uint32_t Argument,
                 uint32_t MaxSpawn );

Parameters

Return Value This function returns a handle to a daemon , or NULL on error.

Description Once a new entry is created, the daemon will create the desired TCP or UDP socket, and start listening for activity.

In the case of TCP, when a new connection is established, a new task thread is created, and a socket session is opened. Then the user’s callback function is called on the new task thread, being supplied with both the socket to the new connection and the caller specified argument (as supplied to DaemonNew()). The callback function can keep the socket and task thread for as long as necessary. It returns from the callback once it is done with the connection. The function can choose to close the socket if desired. The return code informs the daemon whether the socket has been closed (0) or is still open (1).

In the case of UDP, when any data is available on the UDP socket, a new task thread is created, and a socket session is opened. Then the user’s callback function is called on the new task thread, being supplied with both the UDP socket and the caller specified argument (as supplied to DaemonNew()). The callback function can keep the socket and task thread for as long as necessary. It returns from the callback only when it is done with the data. (While the callback function holds the UDP socket, the daemon will ignore further activity on it.) The callback should return 1, as it should not close the UDP socket.

DaemonFree – Destroy a TCP/UDP Server Entry

void DaemonFree( void *hEntry );

Parameters

Return Value None.

Description Destroys a daemon entry, and closes the socket session of all child tasks spawned from the entry. Closing the socket sessions will result in all socket functions returning SOCKET_ERROR in all spawned child tasks. Thus, all spawned tasks should error out and return to the daemon, allowing them to be freed.

5.4.2 Server Daemon Example

The following is an example TCP echo server using the server daemon. The TCP server will use SOCK_STREAMNC for non-copy TCP. Its only job is to read from the socket, and write back what it reads.

To install the server on port 7, use the following code:

hEcho = DaemonNew( SOCK_STREAMNC, 0, 7, dtask_tcp_echo,
                   OS_TASKPRINORM, OS_TASKSTKNORM, 0, 3 );

This code allows up to three echo sessions to be running simultaneously on different threads. Note the IP specified is NULL, allowing echo connection on any local IP address assigned to the system.

To destroy the server and all its instances, the hEcho handle returned from DaemonNew() is used:

DaemonFree( hEcho );

The code for the callback function dtask_tcp_echo() is as follows:

int dtask_tcp_echo( SOCKET s, uint32_t unused )
{
    struct timeval to;
    int I;
    char *pBuf;
    void *hBuffer;

    (void)unused;

    // Configure our socket timeout to be 5 seconds
    to.tv_sec = 5;
    to.tv_usec = 0;
    NDK_setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, &to, sizeof(to) );
    NDK_setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof(to) );

    I = 1;
    NDK_setsockopt( s, IPPROTO_TCP, TCP_NOPUSH, &I, 4 );
    for(;;)
    {
        I = (int)NDK_recvnc( s, (void **)&pBuf, 0, &hBuffer );

        // If we read data, echo it back
        if(I > 0)
        {
            if(send( s, pBuf, I, 0) < 0 )
                break;
            NDK_recvncfree( hBuffer );
        }
        // If the connection got an error or disconnect, close
        else
            break;
    }
    fdClose( s );

    // Return "0" since we closed the socket
    return(0);
}

6 Network Tools Library - Services

Included with the stack package is a library of network tools. It provides auxiliary functionality to the stack library and contains source written to the socket layer that would normally be considered application level code. The library file is called NETTOOLS.LIB, and can be accessed by an application that includes the file NETTOOLS.H.

The support supplied by NETTOOLS can be categorized into two classes: support functions and services. The support functions consist of a programming API that can help develop network applications, while services are servers that execute on the stack platform.

This section describes the NETTOOLS services. Note that these services are all IPv4 based. Based on the IPv6 documentation, these applications can be easily re-written to use IPv6 sockets instead for communication.

6.1 Service Calling Conventions

6.1.1 Specifying Network Services Using the Configuration

If you are using XGCONF to configure your application, you can configure network services to be enabled in the application by checking the box in the property sheet to add the module to your configuration. See the SPRU523 (TI Network Developer’s Kit (NDK) User’s Guide.) and the context-sensitive help for details. If you are using XGCONF for configuration, you can ignore the APIs described in the subsections that follow.

If you are using the Configuration Manager API to configure your application, the subsections that follow describe the direct and configuration APIs used to add services to an application.

Although each service has its own specific API, it is usually more convenient to add services by specifying the service in the system configuration as opposed to calling each individual Open and Close API function. Included in the description of each network service is a description of its direct API, as well as an example of specifying the service in the system configuration.

6.1.1.1 Service Report Function

All the configuration examples in this section use a common service report callback function. The following is a very simple implementation of a service report function that calls printf() to print service status.

Note that this function relies on the physical value of items in the configuration specification found in the file: /ti/ndk/inc/nettools/netcfg.h.

static char *TaskName[] = { "Telnet","","NAT","DHCPS","DHCPC","DNS" };
static char *ReportStr[] = { "","Running","Updated","Complete","Fault" };
static char *StatusStr[] = { "Disabled","Waiting","IPTerm","Failed","Enabled" };

static void ServiceReport( uint32_t Item, uint32_t Status, uint32_t Report, void *h )
{
    printf( "Service Status: %-9s: %-9s: %-9s: %03d\n",
            TaskName[Item-1], StatusStr[Status],
            ReportStr[Report/256], Report&0xFF );
}

6.1.2 Invoking Network Services by NETTOOLS API

If you are using XGCONF to configure your application, you can configure network services to be enabled in the application by checking the box in the property sheet to add the module to your configuration. See the SPRU523 (TI Network Developer’s Kit (NDK) User’s Guide.) and the context-sensitive help for details. You can ignore the APIs described in this section.

If you are using the Configuration Manager API to configure your application, this section describes the APIs used to add services to an application. (Calls to these APIs are generated automatically by the XGCONF configuration.)

Each service API uses a common calling format. This allows the services to be invoked by the configuration system using callback functions provided in the Network Control software (which also performs system initialization). It is preferable to launch services via the configuration system, instead of manually calling each Open and Close function described in the following sections. However, because the source to the Network Control software uses these calls, they are documented here.

The common calling interface consists of a simple Open and Close concept. The Open function initiates the service and returns a service handle, while the Close function shuts down the service using the service handle returned from the Open call.

Each service Open call takes at least one parameter. This parameter is a pointer to a common argument structure called NTARGS. The specification of this structure is as follows:

typedef struct _ntargs {
    int       CallMode;       // Determines desired calling mode
#define NT_MODE_IFIDX1        // Call by specifying IfIdx
#define NT_MODE_IPADDR2       // Call by specifying IPAddr
    int       IfIdx;          // Physical interface Index (0-n)
    uint32_t  IPAddr;         // IP Address
    void      *hCallback;     // Handle to pass to callback function
    void(*pCb)(void *, uint); // Callback for status change
} NTARGS;

Note that this entry structure is a simplified version of that provided by the configuration system. This structure also contains a callback function. The callback function is a subset of that in the configuration system, and codes returned by this callback are passed through the configuration callback to the application.

The individual fields are defined as follows:

6.2 Telnet Server Service

If you are using XGCONF to configure your application, you can configure Telnet to be enabled in the application by checking the box in the property sheet to add the module to your configuration. See the SPRU523 (TI Network Developer’s Kit (NDK) User’s Guide.) and the context-sensitive help for details.

The Telnet Server service provides a mechanism for exposing a stream IO connection to any remote telnet client console.

A telnet connection is basically just a TCP connection to the well-known port designated for telnet. However, there is some data translation that occurs on the stream. Telnet has a set of commands that can change the behavior of the terminal, and can perform some character translation. The telnet server supplied here is designed to convert a normal TTY stream to a telnet stream and back. This allows any application to treat a telnet session as any other TTY session (like a serial port).

Connection to an application is achieved by use of an application supplied callback function that telnet calls when a new connection is established. This callback function returns the file descriptor of one end of a full duplex communications pipe. By allowing multiple calls to the callback function, console applications can be written to work with multiple IO streams.

6.2.1 Telnet Parameter Structure

This section describes a data structure that is generated automatically by the XGCONF configuration. If you are using XGCONF for configuration, you can ignore the structure described here.

The following structure defines the unique parameters of the Telnet service. It is located in the file: /ti/ndk/inc/nettools/inc/telnetif.h.

//
// Telnet Parameter Structure
//
typedef struct _ntparam_telnet {
   int     MaxCon;         // Max number of telnet connections
   int     Port;           // Port (set to NULL for telnet default)
   SOCKET  (*Callback)(struct sockaddr *);
                           // Connect function returns local pipe
} NTPARAM_TELNET;

This structure is used both when specifying the service to the configuration system or when bypassing the configuration and invoking the service API directly.

6.2.2 Invoking the Service via NETTOOLS API

In addition to the configuration option, this service can also be created and destroyed directly through this NETTOOLS API. If an application wishes to bypass the configuration system and launch the service directly, these calls can be used.

Function Description
TelnetOpen() Create an instance of the Telnet Server
TelnetClose() Destroy an instance of the Telnet Server

For API details, refer to the Telnet Module in the NDK API Reference Guide.

6.3 DHCP Server Service

When acting as a router, the NDK may also need to maintain the network configuration on one of its network devices. A DHCP server allows the stack to maintain the IP address of multiple Ethernet client devices. When combined with Network Address Translation (NAT), the DHCP server can be used to establish client membership in a private virtual network.

If you are using XGCONF to configure your application, you can configure the DHCP server to be enabled in the application by checking the box in the property sheet to add the module to your configuration. See the SPRU523 (TI Network Developer’s Kit (NDK) User’s Guide.) and the context-sensitive help for details.

6.3.1 Operation

The DHCP server can be optionally configured to allocate IP addresses out of a pool that is specified by an IP base address and the number of addresses in the pool. If no pool is specified, the server will use static client entries in the configuration system to resolve client address requests.

The server will respond to DHCP requests from a single Ethernet device. This allows for isolation of clients for a given interface, and allows multiple instances of the DHCP server to manage different IP address pools for different interfaces.

6.3.2 DHCP Server Parameter Structure

This section describes a data structure that is generated automatically by the XGCONF configuration. If you are using XGCONF for configuration, you can ignore the structure described here.

The following structure defines the unique parameters of the DHCP server service. It is located in the file: /ti/ndk/inc/nettools/inc/dhcpsif.h.

//
// DHCPS Parameter Structure
//
typedef struct _ntparam_dhcps {
        uint32_t   Flags;      // DHCPS Execution Control Flags
        uint32_t   PoolBase;   // First IP address in optional pool
        uint32_t   PoolCount;  // Number of addresses in optional pool
} NTPARAM_DHCPS;

This structure is used both when specifying the service to the configuration system or when bypassing the configuration and invoking the service API directly.

6.3.3 Invoking the Service via NETTOOLS API

In addition to the configuration option, this service can also be created and destroyed directly through this NETTOOLS API. If an application wishes to bypass the configuration system and launch the service directly, these calls can be used.

DHCPSOpen – Open a DHCP Server

void *DHCPSOpen( NTARGS        *pNTA,
                 NTPARAM_DHCPS *pNTP );

Parameters

Return Value Returns a pointer to a DHCPS instance structure that is used in calls to other DHCPS functions like DHCPSClose().

Description This function is called to initiate DHCPS control of an IP address pool on a given interface. The base address of the address pool does not have to be the first IP address in the subnet.

The DHCP Server executes on a specific interface. Thus, it is compatible with NT_MODE_IFIDX only.

DHCPSClose – Close an Instance of the DHCP Server

void DHCPSClose( void *hDHCPS );

Parameters

Return Value None.

Description This function is called to terminate DHCPS control of the previously supplied interface. This call also destroys the supplied DHCP server instance handle hDHCPS.

6.4 DHCP Client Support

At system start up, the DHCP client will try and acquire an IP address from the DHCP servers available on the network.

Note that the client will accept the first IP address offered. The INIT-REBOOT State (which requests a previously assigned IP address) is not supported.

More information on DHCP can be found in RFC2131 and RFC2132, released by the Internet Engineering Task Force (IETF) organization.

If you are using XGCONF to configure your application, you can configure the DHCP client to be enabled in the application by checking the box in the property sheet to add the module to your configuration. See the SPRU523 (TI Network Developer’s Kit (NDK) User’s Guide.) and the context-sensitive help for details.

6.4.1 Operation

The DHCP client is a special service that always executes immediately in a system. It is usually after the DHCP client obtains a public IP address that most of the other services in the system can initialize.

The DHCP client code makes more use of the service status report callback function than most of the other services. Recall from the beginning of this section that the least significant byte of the report code is reserved for service specific information.

The following report codes are returned in the LSB of the report code sent by the DHCP service:

Note that in each of the above cases, the DHCP portion of the system information configuration (the first 256 entries of CFGTAG_SYSINFO) has been erased and potentially reprogrammed. If an application needs to share the DHCP portion of the system information configuration, these DHCP report codes can be used to signal when to add additional application specific tags. For more information on DHCP and the CFGTAG_SYSINFO tag, see Section 4.3.7.

6.4.2 DHCP Client Parameter Structure

This section describes a data structure that is generated automatically by the XGCONF configuration. If you are using XGCONF for configuration, you can ignore the structure described here.

The following structure defines the unique parameters of the DHCP client service. It is located in the file: /ti/ndk/inc/nettools/inc/dhcpif.h.

//
// DCHP Parameter Structure
//
#define DHCP_MAX_OPTIONS 64 // Max number of allowed options

typedef struct _ntparam_dhcp {
     unsigned char  *pOptions;   // Options to request
     int            len;         // Length of options list
} NTPARAM_DHCP;

This structure is used both when specifying the service to the configuration system or when bypassing the configuration and invoking the service API directly.

6.4.3 Invoking the Service via NETTOOLS API

In addition to the configuration option, this service can also be created and destroyed directly through this NETTOOLS API. If an application wishes to bypass the configuration system and launch the service directly, these calls can be used.

DHCPOpen – Open a DHCP Server

void *DHCPOpen( NTARGS       *pNTA ,
                NTPARAM_DHCP *pNTP);

Parameters

Return Value Returns a pointer to a DHCP instance structure, which is used in calls to other DHCP functions like DHCPClose().

Description This function is called to initiate DHCP control of a given device.

DHCPOpen() starts the DHCP process. This process will discover if there are any DHCP servers on the network and request an IP address. The result of the search for an IP address will be passed to the application via the standard network tools status callback.

The Client will remain running so it can renew the IP address when necessary.

For any additional option tags entered into the DHCP client parameter structure, the resulting information from the DHCP server is written to the system configuration under the CFGTAG_SYSINFO entry. See Section 4.3.7 for more information.

The DHCP Client executes on a specific interface. Thus, it is compatible with NT_MODE_IFIDX only.

DHCPClose – Close an Instance of the DHCP Client

void DHCPClose( void *hDHCP );

Parameters

Return Value None.

Description This function is called to terminate DHCP control of the previously supplied interface and frees the supplied DHCP server instance handle hDHCP.

Note this function will also remove any IP address it has added to the system. In the case of a service shutdown, there will be no status callback indicating the address removal.

6.5 DNS Server Service

The DNS server service allows clients on a home network to resolve host names and addresses for clients on both the home and public networks.

If you are using XGCONF to configure your application, you can configure the DNS server to be enabled in the application by checking the box in the property sheet to add the module to your configuration. See the SPRU523 (TI Network Developer’s Kit (NDK) User’s Guide.) and the context-sensitive help for details.

6.5.1 Operation

The NDK contains a small DNS resolver that can resolve hostnames and addresses that are local to the system via the configuration, or those outside the system by using an external DNS server.

The DNS server service described here allows the same internal DNS resolver to be accessed by clients on a virtual (home) network. This allows clients on a home network to look up peers on the home network using the same DNS server that is used for external lookups. Thus, DNS service for the home network is transparent to these clients.

Because the DNS server service uses the same internal DNS resolver as the client services discussed earlier, the server adds very little overhead to the system.

6.5.2 DNS Server Parameter Structure

The DNS server service does not require a parameter structure.

6.5.3 Invoking the Service via NETTOOLS API

In addition to the configuration option, this service can also be created and destroyed directly through this NETTOOLS API. If an application wishes to bypass the configuration system and launch the service directly, these calls can be used.

DNSServerOpen – Create an Instance of the DNS Server

void *DNSServerOpen( NTARGS *pNTA );

Parameters

Return Value Returns a handle to the new server instance, or NULL if the service could not be created. This handle is used with DNSServerClose() to shut down the server when it is no longer needed.

Description Creates a DNS server task that can service external DNS requests using UDP.

DNSServerClose – Destroy an Instance of the DNS Server

void DNSServerClose( void *hDNSS );

Parameters

Return Value None.

Description Destroys the instance of the DNS server indicated by the supplied handle. Once called, the server is shut down. It waits for all spawned sessions to complete.

6.6 Network Address Translation (NAT) Service

The NAT service allows for the establishment of a home virtual network that is isolated and protected from the external public network. It provides a port based address translation function that allows all the clients on the home network to share a single public IP address. Thus, multiple clients can share the same ISP account.

If you are using XGCONF to configure your application, you can configure the NAT service to be enabled in the application by checking the box in the property sheet to add the module to your configuration. See the SPRU523 (TI Network Developer’s Kit (NDK) User’s Guide.) and the context-sensitive help for details.

6.6.1 Operation

The NDK contains both a network address translation module and an IP filtering model. When the translation service is enabled, any packet received from a client on a virtual network that is destined for the external public network is adjusted to use the stack’s public IP client address.

The translation is performed by allocating a translation record and holding it for a period of time. The translation records are timed out based on their protocol. In TCP, records are timed out based on the state of their TCP connection. UDP and ICMP translations time out based on when they were last used.

In addition to translation, the stack contains an IP filter option (always enabled by this service) that filters packets from the public network from being seen by the private network. For example, if someone on a public network knew the IP address and the subnet mask of the router’s (stack in route mode) private network, it could set a gateway route to the router’s public IP host address and the router would route packets from the public to the private network and back (internally it does not distinguish between public and private while routing). The IP filter prevents this. It also prevents an entity on a public network from accessing protocol servers (like Telnet) that are running on the private network. This allows the router to present different Telnet interfaces to the public than it does to clients in the home.

The NAT service is executed on the public interface - i.e., the interface that is assigned a valid public IP host address (used to carry traffic for the virtual client addresses). There can only be one instance and thus only one public IP address, but the service can serve multiple virtual (home) networks in the system so long as they can be combined and still exclude the public IP. If the combination of these networks results in an overlap with the public network, the service fails.

For example, assume interface If-1 is connected to the physical network 128.32.12.x/255.255.255.0, and there are two home networks (192.168.0.x/255.255.255.0) on If-2 and (192.168.1 .x/255.255.255.0) on If-3. To run NAT on both home networks, the NAT interface would be If-1 (the public interface), and the NAT group (virtual) network would be 192.168.0.0/255.255.254.0, which covers both home networks.

For more information on NAT operation, including how to program proxy filters, see Appendix B, Network Address Translation.

6.6.2 NAT Server Parameter Structure

This section describes a data structure that is generated automatically by the XGCONF configuration. If you are using XGCONF for configuration, you can ignore the structure described here.

The following structure defines the unique parameters of the NAT server service. It is located in the file: /ti/ndk/inc/nettools/inc/natif.h.

//
// NAT Parameter Structure
//
typedef struct _ntparam_nat {
     uint32_t    IPVirt;   // Virtual IP address
     uint32_t    IPMask;   // Mask of virtual subnet
     uint32_t    MTU;      // NAT packet MTU (normally 1500 or 1492)
} NTPARAM_NAT;

This structure is used both when specifying the service to the configuration system or when bypassing the configuration and invoking the service API directly.

6.6.3 Invoking the Service via NETTOOLS API

In addition to the configuration option, this service can also be created and destroyed directly through this NETTOOLS API. If an application wishes to bypass the configuration system and launch the service directly, these calls can be used.

NATOpen – Enable the NAT Service

void *NATOpen( NTARGS *pNTA,
               NTPARAM_NAT *pNTP );

Parameters

Return Value Returns a handle to the NAT instance (1), or NULL if the service could not be created. This handle is used with NATClose() to disable the service when it is no longer needed.

Description Enables the Network Address Translation Service. Although the function returns a handle for compatibility with the standard NETTOOLS API, only one instance of the NAT service is allowed.

This service utilizes the virtual and external network information using the configuration system. If the configuration system was not used to create the network records, this function will fail.

The NAT service executes on a specific public interface. Thus, it is compatible with NT_MODE_IFIDX only.

NATClose – Disable the NAT Service

void NATClose( void *hNAT );

Parameters

Return Value None.

Description Disables the NAT service.

7 Registering NDK Hook Functions

The NDK provides a function, NDK_registerHook, that allows the user to register their own functions to hook into predetermined internal portions of the NDK. Some NDK modules already provide their own methods of hooking into their code, but we have started to consolidate the hooks into the NDK_registerHook method.

7.1 Operation

7.1.1 Hook Function Structure

All user hook functions must be of the type NDK_HookFxn defined in ti/ndk/inc/socketndk.h as:

typedef int (*NDK_HookFxn) (uintptr_t);

NDK_registerHook - Register a hook function

void NDK_registerHook( int         type,
                       NDK_HookFxn fxn  );

Parameters

Description Registers a NDK_HookFxn of a given type. For instance a CREATE_SKT_CTX_HOOK type will register the function inside socket creation calls

7.2 Hook Function Types

The NDK currently supports three hook function types. The symbols for the types are displayed below, and can be found in /ti/ndk/inc/socketndk.h

7.2.1 Create/Close Socket Context

These two types allow you to hook into the process of creating or closing a socket. Both the create and close hooks will send a SOCKET as an argument.

The caller of the create socket context hook expects an integer return value indicating the socket’s “context”. This context can be whatever you want it to be, but it will ultimately be stored on the SOCK struct’s int32_t Ctx field.

The close socket context hook is meant to remove any “context” that was created in the create socket context hook call. The caller does not expect any return value.

The NDK’s own slnetif module implements these hooks in ti/ndk/slnetif/slnetifndk.c by registering the addSktToTable and removeSktFromTable functions as open and close context hooks respectively.

7.2.2 NetStart Error

This type will hook into the various error states of the NC_NetStart function found in ti/ndk/netctrl/netctrl.c. This allows you to configure what action should be taken, and what information should be displayed at these error states. If this hook function is not present, default actions will be taken at error states.

The caller of this hook function will pass a NetStartError_Obj (definition found in ti/ndk/inc/socketndk.h) as an argument. The NetStartError_Obj’s error field will be filled in with the type of the error. The two currently supported error types (found in ti/ndk/inc/stack/inc/nimuif.h) are:

Below is an example of how this hook function type can be used:

#include <ti/ndk/inc/stkmain.h>
#include <ti/ndk/inc/netmain.h>

static int netCtrlErrorHook(uintptr_t errorStruct)
{
    NetStartError_Obj *ncError = (NetStartError_Obj *)errorStruct;

    if(ncError->error == NIMU_ERR_ALL_FAILED)
    {
        DbgPrintf("All NIMU devices failed to initialize. Shutting down stack\n");
        NC_NetStop(ncError->error);
    }
    else if(ncError->error == NIMU_ERR_SOME_FAILED)
    {
        DbgPrintf("Some NIMU devices failed to initialize. Continuing stack startup\n");
    }
    else
    {
        DbgPrintf("Unrecognized NC_NetStart error.\n");
    }

    /*
     * This hook type does not need a return value, but returning 0 to satisfy
     * the NDK_HookFxn type
     */
    return(0);
}

static void ndkStackThread(void *argUnused)
{
    NDK_registerHook(NETSTART_ERROR_HOOK, (NDK_HookFxn)(&netCtrlErrorHook));

    do
    {
        rc = NC_NetStart(hCfg, networkOpen, networkClose, networkIPAddr);
    } while(rc > 0);
}

A Internal Stack Functions

In the source code to the network control functions, there are several calls to internal stack functions. This is similar to calling the kernel in other operating environments. This section contains a partial list of internal stack functions provided to aid in the comprehension of kernel oriented calls.

Note the following points for this section:

  1. This section is required only for system programming that needs low level access to the stack for configuration and monitoring. This API does not apply to general sockets application programming.
  2. In addition to the internal functions described here, there are scheduling and configurations tools available that make any direct coding to these functions unnecessary.

A.1 Overview

The control API is the collection of functions supplied in the stack library. The entire API is exposed, although the vast majority of functions and objects will only be used internally to the stack.

A.1.1 Interrupts and Preemption

It should be noted that no part of the stack is interrupt driven. Neither can any stack function be called at interrupt time. All interrupt processing is performed in the HAL or OS libraries, and is thus externally-defined code, which allows the development of a HAL/OS architecture that is best suited for a given operating environment, without affecting the operation of the stack.

The stack may or may not be preempted, depending on the operating environment in use. A non-preemptive architecture is possible because the stack code does not use polling loops nor make any internal blocking type calls, but preemption is also supported.

A.1.2 Proper Use of the llEnter() and llExit() Functions

The internal stack functions are not designed to be reentrant. This allows the stack to operate freely without the concept of a critical section, which is implementation dependent and potentially detrimental to real-time operation. Thus, access to stack functions must be strictly controlled. The form of this control is dependent on the system environment, and is embodied as two low level OS library functions, llEnter() and llExit(). These functions are called before and after a section of code where any stack functions are called. For example:

llEnter();
StackFunction1();
StackFunction2();
llExit();

These functions can be thought of as entering and exiting kernel mode.

To make normal user functions appear to be re-entrant, some user functions (like the sockets API) make internal calls to llEnter() and llExit() when calling into the stack. If an application needs to call both user functions and internal stack functions, care must be taken so that standard user functions are not called between an llEnter() / llExit() pair (this would cause an error if they in turn called llEnter()).

The following are good general guidelines:

A.1.3 Objects

Many of the control API functions deal with object handles. These handles are created by a variety of class functions contained in the stack. When using an object handle, it is important to realize how the object handle will be treated by the function being called.

Associated with every object is the concept of who owns it, who is using it, and who will eventually free it. In general, when an application creates an object, the application owns it, the application is the only one using it, and the application must eventually free it. Unfortunately, the matter becomes somewhat confused when object handles are shared between applications - especially when the scope of the handle creator may be shorter than the handle itself.

In this system, there are two basic object types:

A.2 Stack Executive (Exec)

At the heart of the stack is the Executive API (Exec). The Executive acts as a message dispatcher for the internal stack components. This action is mostly hidden from the application, but there are some public functions.

A.2.1 API Functions

ExecOpen – Prepare the System for Execution

void ExecOpen();

Description Prepares the stack for execution by initializing the individual components. Until ExecOpen() is called, the system cannot do any work, but after calling this function, objects like routes and bindings can be created.

ExecClose – Shutdown Stack and Cleanup

void ExecClose();

Description Completes stack execution. This function is called to perform final clean up on the system after all user objects (like devices and bindings) have been destroyed.

ExecLowResource – Signal Low Resource Condition

void ExecLowResource();

Description Informs the stack that memory resources are getting dangerously low. As a result of this call, the stack will abandon certain operations that hold excessive resources. (Pending ARP packets are thrown away, IP packet fragments pending reassembly are abandoned, etc.)

ExecTimer – Signal 1/10th Second Timer Tick

void ExecTimer();

Description This function is called ten times a second to inform the stack that one tenth of a second has elapsed. This function is called from a normal task thread, never an ISR. In theory, the function can be called from anywhere, but in practice, it is always called from a scheduler thread that also handles network packets. For more information, see the description of the NETCTRL functions in the TI Network Developer’s Kit (NDK) User’s Guide (SPRU523).

A.3 Packet Buffer Manager (PBM) Object

The NDK uses a common packet buffer object that is managed by a module called the Packet Buffer Manager (PBM). The implementation of this manager determines the buffer strategy for the entire system.

Internally, the packet buffer objects are pointers to a structure of type PBM_Pkt; however, the buffers are abstracted into a handle of type PBM_Handle for use by code outside of the NDK. This helps protect the reserved members of the packet buffer structure from being misused.

If you are using XGCONF to configure your application, you can configure the size and location of the buffers managed by the PBM in the Buffers page of the NDK Global module configuration. See the SPRU523 (TI Network Developer’s Kit (NDK) User’s Guide.) and the context-sensitive help for details.

A.3.1 Object Type

Static - PBM objects are owned by a single entity and destroyed by their owner. Ownership of a packet buffer changes as it is passed via function calls.

A.3.2 API Function Overview

The PBM API functions are as follows:

Initialization/Shutdown Functions:

Function Description
PBM_open() Open the Packet Buffer Manager
PBM_close() Close the Packet Buffer Manager

Create/Destroy Functions:

Function Description
PBM_alloc() Create New Packet Buffer
PBM_free() Destroy (Free) Packet Buffer
PBM_copy() Create an exact copy of the Packet Buffer

Property Functions:

Function Description
PBM_getBufferLen() Get the length of the physical data buffer
PBM_getDataBuffer() Get a pointer to the physical data buffer
PBM_getValidLen() Get the length of the valid data in the buffer
PBM_getDataOffset() Get the buffer offset to the start of the valid data
PBM_getIFRx() Get the device handle of the ingress Ethernet device
PBM_setValidLen() Set the length of the valid data in the buffer
PBM_setDataOffset() Set the buffer offset to the start of the valid data
PBM_setIFRx() Set the device handle of the ingress Ethernet device

A.3.3 API Function Description

PBM_open – Open the Packet Buffer Manager

uint32_t PBM_open();

Parameters

Return Value Function returns 1 on success, and 0 on failure.

Description This function is called once to open the PBM module and allow it to initialize its internal queues.

PBM_close – Close the Packet Buffer Manager

void PBM_close();

Parameters

Return Value None.

Description This function is called at system shutdown to allow the PBM module to shut down and free any memory it has allocated.

PBM_alloc – Create New Packet Buffer

PBM_Handle PBM_alloc( uint32_t MaxSize );

Parameters

Return Value Handle to the packet buffer or NULL on memory allocation error.

Description This function is called to create a new packet buffer handle. When first created, the packet is entirely uninitialized, except for the physical characteristics of the data buffer (the buffer pointer and its physical length). The length of the buffer will be the same or greater than that specified by the caller in MaxSize.

PBM_free – Destroy (Free) Packet Buffer

void PBM_free( PBM_Handle hPkt );

Parameters

Return Value None.

Description This function is called to destroy a packet buffer. When called, all objects associated with the packet buffer are dereferenced or destroyed.

PBM_copy – Create an exact copy of the Packet Buffer

PBM_Handle PBM_copy( PBM_Handle hPkt );

Parameters

Return Value Handle to the new copy of the packet buffer or NULL on memory allocation error.

Description This function makes a duplicate copy of a packet buffer. It is usually called to copy a packet to be distributed to multiple destinations, or to be sent to multiple egress devices.

PBM_getBufferLen – Get the Length of the Physical Data Buffer

uint32_t PBM_getBufferLen( PBM_Handle hPkt );

Parameters

Return Value Length of the physical data buffer in bytes.

Description This function is called to get the length of the physical data buffer associated with the packet buffer handle. Note that the buffer length is fixed for the life of the buffer and cannot be changed.

PBM_getDataBuffer – Get a Pointer to the Physical Data Buffer

unsigned char *PBM_getDataBuffer( PBM_Handle hPkt );

Parameters

Return Value Pointer to the physical data buffer.

Description This function is called to get a pointer to the physical data buffer associated with the packet buffer handle. Note that the physical buffer is fixed and cannot be changed.

PBM_getValidLen – Get the Length of the Valid Data in the Buffer

uint32_t PBM_getValidLen( PBM_Handle hPkt );

Parameters

Return Value Byte length of the valid data stored in the packet buffer.

Description This function is called to get the length of the valid data currently held in the packet buffer. When a packet buffer is created, it has no valid data, so this value is initially zero.

PBM_getDataOffset – Get the Buffer Offset to the start of the Valid Data

uint32_t PBM_getDataOffset( PBM_Handle hPkt );

Parameters

Return Value Byte offset from the start of the physical data buffer to the first byte of valid data.

Description This function is called to get the offset in bytes from the start of the physical data buffer to the first byte of valid data. When a packet buffer is created, it has no valid data, so this value is initially zero.

PBM_getIFRx – Get the Device Handle of the Ingress Ethernet Device

void *PBM_getIFRx( PBM_Handle hPkt );

Parameters

Return Value NULL for locally created packets, or a handle to the device on which the packet was received.

Description This function is called to get the handle to the ingress device where the packet contained in the packet buffer originated. Packet drivers in the HAL (both serial and Ethernet based) record the logical handle associated with all incoming packets. This identifies the packet type as well as the interface on which the packet was received.

PBM_setValidLen – Set the Length of the Valid Data in the Buffer

void PBM_setValidLen( PBM_Handle hPkt,
                      uint32_t   length );

Parameters

Return Value None.

Description This function is called to set the length of the valid data in the packet buffer. It informs the system of the number of bytes of valid data that are stored in the physical data buffer. When a packet buffer is created, it has no valid data, so this value is initially zero.

PBM_setDataOffset – Set the Buffer Offset to the Start of the Valid Data

void PBM_setDataOffset( PBM_Handle hPkt,
                        uint32_t   offset );

Parameters

Return Value None.

Description This function is called to set the offset in bytes from the start of the physical data buffer to the first byte of valid data. It informs the system of where valid data is stored in the physical data buffer. When a packet buffer is created, it has no valid data, so this value is initially zero.

PBM_setIFRx – Set the Device Handle of the Ingress Ethernet Device

void PBM_getIFRx( PBM_Handle hPkt,
                  void       *hDevice );

Parameters

Return Value None.

Description This function is called to set the handle to the ingress device where the packet contained in the packet buffer originated. Packet drivers in the HAL (both serial and Ethernet based) record the logical handle associated with all incoming packets. This identifies the packet type, as well as the interface on which the packet was received.

A.4 Packet Buffer Manager Queue (PBMQ) Object

The PBM module also includes a queue object that can be used to queue packet buffers for later use. The queue is a first in first out system, so it can be used to queue in-order packets as well as free buffers.

The PBMQ object is just a structure of type PBMQ. Once this structure is declared and initialized, it is ready for use.

A.4.1 Object Type

Static - PBMQ objects are owned by a single entity and destroyed by their creator.

A.4.2 API Function Overview

The PBM API functions are as follows:

Function Description
PBMQ_init() Initialize a PBMQ object for use
PBMQ_count() Return the number of PBM packet buffers on the queue
PBMQ_enq() Enqueue a PBM packet buffer onto the queue
PBMQ_deq() Dequeue a PBM packet buffer off the queue

A.4.3 API Function Description

PBMQ_init – Initialize a PBMQ Object for Use

void PBMQ_init( PBMQ *pQ );

Parameters

Return Value None.

Description This function is called once to initialize a PBMQ structure for use.

PBMQ_count – Return the Number of PBM Packet Buffers on the Queue

uint32_t PBMQ_count( PBMQ *pQ );

Parameters

Return Value Number of queued buffers.

Description This function is called once to return the number of PBM packet buffers currently on the indicated queue.

PBMQ_enq – Enqueue a PBM Packet Buffer onto the Queue

void PBMQ_enq( PBMQ       *pQ,
               PBM_Handle hPkt );

Parameters

Return Value None.

Description This function is called to add the supplied PBM packet buffer to the indicated queue.

PBMQ_deq – Dequeue a PBM Packet Buffer Off the Queue

PBM_Handle PBMQ_deq( PBMQ *pQ );

Parameters

Return Value Handle to PBM packet buffer, or NULL on empty queue.

Description This function is called to remove a PBM packet buffer from the indicated queue. The function returns a handle to the PBM packet buffer removed from the queue, or NULL if the queue was empty.

A.5 Jumbo Packet Buffer Manager (Jumbo PBM) Object

The PBM object is capable of handling memory allocations only up to buffer sizes of maximum MMALLOC_MAXSIZE (3068 bytes). For handling memory allocation for jumbo frames, i.e., packets typically larger than 1500 bytes in size, and that could be as large as 10K bytes, the PBM object invokes the Jumbo PBM APIs internally. The Jumbo PBM is responsible for handling memory allocation and de-allocation for Jumbo frames.

The following are some of the main features of Jumbo PBM:

For a sample implementation of the Jumbo PBM, refer to the source file /ti/ndk/stack/jumbo_pbm.c.

A.5.1 API Function Overview

The Jumbo PBM API are as follows:

Function Description
_jumbo_mmInit() API to initialize the Jumbo PBM object
jumbo_mmAlloc() Allocates memory requested for the new packet buffer
jumbo_mmFree() Frees up the memory held in the packet buffer
_jumbo_mmCheck() Dump the current memory usage in Jumbo PBM object

A.5.2 API Function Description

_jumbo_mmInit – Initialize the Jumbo PBM object for use.

int _jumbo_mmInit();

Parameters

None

Return Value Always returns 1 to indicate success.

Description This function is called during the system initialization to initialize the Jumbo PBM memory and any relevant data structures.

jumbo_mmAlloc – Allocate a new Jumbo packet buffer.

void *jumbo_mmAlloc( uint32_t Size );

Parameters

Return Value Pointer to the newly allocated packet buffer.

Description This function is called by the PBM object when an application/driver requests for a packet buffer larger than what it can handle, i.e., MMALLOC_MAXSIZE (3068 bytes). This API allocates memory out of jumbo memory pool and returns a pointer to the packet buffer just allocated.

jumbo_mmFree – Frees up memory held by the packet buffer.

void jumbo_mmFree( void *p );

Parameters

Return Value None

Description This API returns the packet buffer to the jumbo memory pool for use again by the application. The packet buffer handle passed as an argument to this function must be a valid handle obtained using jumbo_mmAlloc() API earlier.

_jumbo_mmCheck – Dumps the memory usage stats for jumbo PBM object.

void _jumbo_mmCheck( uint32_t CallMode,
                     int (*pPrn)(const char *,...) );

Parameters

Description This function iterates through the Jumbo PBM object’s memory allocation table and dumps the current memory usage stats according to the arguments specified.

A.6 Stack Event (STKEVENT) Object

Although technically not part of the NDK, the STKEVENT event object is a central component to the low level architecture. It ties the HAL layer to the network scheduler thread. The network scheduler thread waits on events from various device drivers in the system including the Ethernet, serial, and timer drivers. The device drivers use the STKEVENT object to inform the scheduler that an event has occurred.

A.6.1 Object Type

Static - The STKEVENT object is created and owned by the network scheduler.

A.6.2 API Function Overview

The STKEVENT object is implemented entirely via #define MACROs and therefore, does not have a true API. This allows the network scheduler to present an abstracted API to the HAL layer for network events. The STKEVENT object is a simple structure and manipulated directly by the network control module (NETCTRL). This is discussed further in the TI Network Developer’s Kit (NDK) User’s Guide (SPRU523).

The two MACRO functions are as follows:

Property Functions:

Function Description
STKEVENT_init() Initialize a new STKEVENT object to NULL
STKEVENT_signal() Signal a new STKEVENT event code

A.6.3 API Function Description

STKEVENT_init – Initialize a new STKEVENT object to NULL

void STKEVENT_init( STKEVENT_Handle  hEvent,
                    Semaphore_Handle hSem )

Parameters

Return Value None.

Description This function is called once to initialize the STKEVENT object so it is ready for use.

NOTE: This function is implemented as a multi-line macro, so care should be taken when using it in the body of an if/else statement.

STKEVENT_signal – Signal a New STKEVENT Event Code

void STKEVENT_signal( STKEVENT_Handle hEvent,
                      uint32_t        EventCode,
                      uint32_t        fHwAsynch )

Parameters

Return Value None.

Description This function is called from a device driver to signal an event to the network scheduler for further processing. The STKEVENT handle hEvent is an event handle supplied to the device driver when the driver is first initialized. The EventCode parameter specifies the type of event. The defined events include the following:

The fHwAsynch flag specifies whether the event was triggered by an external asynchronous hardware source. Examples of asynchronous events include hardware interrupts or timer PRDs. An example of a non-asynchronous event would be detecting an event from within a driver service check function. Service check functions are called periodically (or polled) by the scheduler.

NOTE: This function is implemented as a multi-line macro, so care should be taken when using it in the body of an if/else statement.

To make full use of the stack objects described in this section, it is necessary to understand some of the stack’s basic building block components. One such component is the Link Layer Information Object, or LLI for short.

An LLI object is an ARP table entry. This implementation of the IP stack combines the traditional route table and ARP table into a single table with a single API. Routes that need to use the ARP function include an ARP status object, called LLI. Normally, you only use an LLI object to inspect the ARP status of the route table.

The ARP entries can be of two types:

Both the dynamic and static ARP entries are supported in NDK stack. The next section discusses the APIs exported by NDK stack to configure dynamic and static ARP entries.

A.7.1 ARP Revalidation Logic

Each dynamic LLI/ARP entry in NDK is associated with a non-zero Keep-alive timeout (controlled using CFGITEM_IP_RTKEEPALIVETIME). This timeout determines the length of time in seconds that an LLI entry and its associated route entry is valid. The routing module in the stack internally runs a timer and periodically checks to see if any routes or associated LLI entries are about to expire. If it finds a route/LLI entry that is about to expire, it checks to see if that LLI entry is active, i.e., if it has been used in the last Route Inactivity timeout seconds (configured using CFGITEM_IP_RTARPINACTIVITY). If so, the LLI module initiates an ARP Request/Reply exchange to revalidate the LLI entry even before it expires and disrupts any ongoing communication. If an ARP reply is received successfully for the request sent out earlier, the LLI entry is marked valid again for another Keep-alive timeout seconds and any packets using the route/LLI entry are sent out of the device. However, if no ARP reply is received, the ARP request is retransmitted and this process is repeated 3 times before the revalidation process is aborted and the associated LLI/Route entries are deleted. Also, if the LLI/Route entry was inactive or never used in the last Route Inactivity Timeout seconds ARP revalidation process is not done for such an entry and is deleted immediately from the system. Once the LLI/Route entries are deleted they have to be re-established using a successful ARP Request-Reply exchange triggered by an application’s attempt to transmit packets to the intended destination.

A.7.2 Object Type

Static - LLI objects are owned and destroyed by their creator.

A.7.3 Information Structure

The following data structure is used to hold information regarding an ARP/LLI entry in the NDK stack. This structure is especially useful in presenting the LLI entry info in a simple, compact way to an application requesting information about the LLI entries configured in the stack.

/**
 * @brief
 *  This structure describes the LLI/ARP Information Object.
 *
 * @details
 *  This data structure is used by the LLI module to populate
 *  LLI/ARP Entry information contained in the NDK Kernel
 *  in a simple, user-friendly way to the application.
 *
 */
typedef struct _lli_info
{
    /**
     * @brief Links to other LLI_INFO Objects
     */
    LIST_NODE Links;

    /**
     * @brief Boolean Flag to indicate whether this LLI
     * entry is a static / dynamic entry.
     */
    unsigned char     IsStatic;

    /**
     * @brief The 4 byte IPv4 address associated with this
     * LLI/ARP Entry.
     */
    uint32_t       IPAddr;

    /**
     * @brief The 6 byte Ethernet MAC address associated with this
     * LLI/ARP Entry.
     */
    unsigned char     MacAddr[6];
} LLI_INFO;

A.7.4 API Function Overview

The LLI API functions are as follows:

Function Description
LLIGetMacAddr() Get the Mac Address Associated with this LLI.
LLIValidateRoute() Validate an IP address/MAC address pairing in the route table.
LLIAddStaticEntry Add a new static ARP entry/update an existing static ARP entry/modify a dynamic entry to a static ARP entry in the stack.
LLIRemoveStaticEntry Remove a previously configured static ARP entry from the stack.
LLIGetStaticARPTable Retrieve a copy of the static ARP table from the stack.
LLIFreeStaticARPTable Cleans the memory allocated by a previous call to LLIGetStaticARPTable API.

A.7.5 API Functions

LLIGetMacAddr – Get the Mac Address Associated with this LLI

uint32_t LLIGetMacAddr( void          *hLLI,
                        unsigned char *pMacAddr,
                        uint32_t      MaxLen );

Parameters

Return Value Returns 1 if the Mac address for the LLI is valid and it was successfully written to the supplied buffer.

Returns 0 if the LLI does not contain a valid Mac address, or one of the calling parameters is invalid.

Description This function is called to return the six byte Mac address associated with the LLI. It is used in system programming to obtain the hardware address from an LLI contained in a route entry.

LLIValidateRoute – Validate an IP Address/MAC Address Pairing in the Route Table

void *LLIValidateRoute(void          *hIF,
                       uint32_t      IPAddr,
                       unsigned char *MacAddr );

Parameters

Return Value Referenced handle to route or NULL if there was no room to create the entry.

Description This function is called to create or update an entry in the stack route table for the supplied IP address. The entry for the given IP address is marked as valid, and assigned the supplied MAC address. Packets sent to the IP address will be assigned the given MAC address, and no ARP request will be sent.

This function also updates the route in the LLI (ARP) expiration list. It allows an application to change the state of the ARP entry even if the stack has already created the route. It should be used when it is unclear if the route (really ARP table entry) already exists or not.

Note that this function returns a referenced route handle. This handle must be dereferenced using the RtDeRef() function when it is no longer required. Because the route is treated as a standard ARP entry (with a standard expiration time as supplied in the configuration structure), the route can be dereferenced immediately.

LLIAddStaticEntry – Add/update a static ARP entry or change a dynamic entry to a static ARP entry

int LLIAddStaticEntry( uint32_t      IPAddr,
                       unsigned char *pMacAddr );

Parameters

Return Value Returns 0 on success or -1 on invalid input/error in LLI entry creation.

Description This API validates the input and returns an error (-1) if any of the following conditions are met:

If no error encountered, this API does the following:

This API is an application level API and can be called from outside the kernel mode.

LLIRemoveStaticEntry – Remove a previously configured static ARP entry from the stack.

int LLIRemoveStaticEntry( uint32_t IPAddr );

Parameters

Return Value Returns 0 on success or -1 on invalid input/error in LLI entry removal.

Description This API searches for a static route and associated LLI entry in the NDK stack using the IPv4 address specified and if no entry found returns an error, i.e., -1. If a valid entry is found, it removes the route and corresponding LLI entries and cleans up any memory associated with them. On successful clean up, returns 0. If this API is not explicitly called to remove a previously configured static ARP entry, it is cleaned up only during the NDK stack shutdown.

This API is an application level API and can be called from outside the kernel mode.

LLIGetStaticARPTable – Retrieve a copy of the static ARP table from the stack.

void LLIGetStaticARPTable( uint32_t*  pNumEntries,
                           LLI_INFO** pStaticArpTable );

Parameters

Return Value Updates pNumEntries with the number of static ARP Entries and pStaticArpTable with a list of LLI_INFO structures containing the information of all static ARP entries configured in the stack.

Description This API can be used to retrieve the number of static ARP entries and a replicated list of such entries configured in the system. This API traverses through the route and LLI (ARP) table configured in NDK, finds any static routes/LLI entries configured, and creates a copy of them and returns them as a linked list of LLI_INFO structures for the requesting application to use. In case of a memory allocation error or if no static ARP entries found, this API returns the number of entries (pNumEntries) as zero to indicate the same.

This API is an application level API and can be called from outside the kernel mode.

LLIFreeStaticARPTable – Cleans the memory allocated by a previous call to LLIGetStaticARPTable API.

void LLIFreeStaticARPTable( LLI_INFO* pStaticArpTable );

Parameters

Return Value None

Description This function is called to clean the memory allocated by a previous call to LLIGetStaticARPTable API. This function cleans the replicated copy of the static ARP table.

This API is an application level API and can be called from outside the kernel mode.

A.8 Binding Object

For a device object to live on the network, it must have an IP address and knowledge of its IP subnet. The process of assigning an IP address and subnet to a device binds the device with the desired IP addressing.

A.8.1 Object Type

Static - Binding objects are generally created and destroyed by the same entity.

A.8.2 BIND API Functions

Although the Bind object API is larger than that discussed here, this section covers the portion of the API that is encountered by a system application.

BindNew – Create New IP Binding

void *BindNew( void     *hIF,
               uint32_t IPAddr,
               uint32_t IPMask );

Return Value Returns a handle to the Bind object, or NULL on error.

Description Binds the indicated IP address and mask to the supplied Ether device. The handle to the Ether device object is specified as hIF - or an handle to an interface, because the interface may or may not be an Ethernet device (but always is in this version).

The IP address and mask arguments are given the type uint32_t. The IP data must be supplied in network format. If unsure of the network format for your hardware, use the htonl() macro function on the native format (where 1.2.3.4 = = 0x01020304).

BindFree – Destroy IP Binding Object

void BindFree( void *hBind );

Description Destroys the indicated Bind object, and frees its associated memory. This function removes the IP address and subnet association in the system route table. It has no effect on the Ether object involved in the binding.

BindGetFirst – Start Enumeration of Binding Objects

void *BindGetFirst();

Description Returns a handle to the first binding installed in the system (or NULL if no bindings exist).

BindGetNext – Continue Enumeration of Binding Objects

void *BindGetNext( void *hBind );

Description Returns a handle to the binding in the installed binding list that follows the indicated binding (or NULL if no more bindings exist). Note that bindings are not internally kept in chronological order in which they were installed.

BindGetIF – Get the Ether Object that is Bound by this Binding Object

void *BindGetIF( void *hBind );

Description Returns a handle to the Ether object that is bound by this binding object. Note that a binding is nothing more than an assignment of an Ether object to an IP address/network.

BindGetIP – Get the IP Address/Network that is Bound by this Binding Object

void BindGetIP( void     *hBind,
                uint32_t *pIPHost,
                uint32_t *pIPNet,
                uint32_t *pIPMask );

Description Returns the IP address and mask as requested by the calling arguments. Any of the pointer arguments can be NULL if the information is not required.

The arguments are defined as follows:

A.9 Route Object

The route manager maintains IP routing information. It is called by various routines to get and set route information. A route object is a destination on the network. Locally, it consists of an egress interface and a next hop IP address.

This section describes a subset of the route object. Flags, features, and API calls have been omitted for simplicity. Also, documenting the entire API would require the documentation of other stack objects that are not covered in this document.

A.9.1 Object Type

Referenced - Route objects are referenced and dereferenced as needed. The object is removed when the reference count reaches ZERO.

A.9.2 Route Entry Flags Definition

Associated with each route is a collection of entry/status flags. These flags indicate the type of route and its status. Most system programming is not concerned with the route entry flags. They are listed here for completeness. The definition of the various flags is as follows:

A.9.3 Route Entry Flags Guidelines

See the following for some general guidelines to use when creating new routes. Use the definitions listed above with the following legal flag combinations:

A.9.4 API Functions

The Route API is the most extensive API that a system task uses outside of the stack routines themselves. As with the other stack APIs, this guide does not document the entire API.

Calls that accept a CallFlags argument can be supplied with the FLG_RTF_REPORT flag to indicate that the call should result in a route report to the route control object. The route control object is described later in this section.

RtRef – Reference a Route

void RtRef( void *hRt );

Description Called to add one to the reference count of a route. An application that keeps a route it did not create itself should reference the route before it uses it, and dereference it when it is through.

RtDeRef – Dereference a Route

void RtDeRef(void *hRt);

Description Called to remove one from the reference count of a route. An application dereferences a route when it is through with it. This is the same (to the application) as destroying the route. The route is actually destroyed when its reference count reaches zero.

RtCreate – Create New Route

void *RtCreate( uint32_t      CallFlags,
                uint32_t      RtFlags,
                uint32_t      IPAddr,
                uint32_t      IPMask,
                void          *hIF,
                uint32_t      IPGateway,
                unsigned char *pMacAddr );

Parameters

Call Flags

Return Value Referenced handle to newly created route.

Description Called to create a new host or network route and add it to the route table. Existing routes cannot be modified via this call.

Some flag combinations are incorrect, and the following rules are strictly enforced.

RtFind – Find a Route

void RtFind( uint32_t CallFlags,
             uint32_t IPAddr );

Call Flags

Return Value Referenced handle to best match route (or NULL)

Description This call searches the route table for a route that matches the supplied IP address. The search always returns the best match for a route. The best match is a match with the most bits in the subnet mask. Thus, a host match takes priority over a network match.

When there is more than one route with the same subnet mask, the following matching guidelines are used (listed from best to worst):

Sometimes a search is desired where particular matches are desired. The following flags can be combined with the value of CallFlags to change the behavior of the search:

RtSetTimeout – Set the Timeout for a Non-static Route

void RtSetTimeout(void *hRt, uint32_t dwTimeOut);

Description This call allows an application to specify that the stack should time out a referenced route. When the route is added to the timeout list, the system will add a reference. Thus, once the application sets the timeout value, it should call RtDeRef() to dereference the route. The route will stay valid until the timeout value is exceeded, after which it is dereferenced by the system. Note that if this function is called and the route is not dereferenced by the caller, it will still be removed from the system route table when the expiration time elapses, but the object will not be freed.

RtSetFailure – Set the Timeout for a Non-Static Route

void RtSetFailure( void     *hRt,
                   uint32_t CallFlags,
                   uint32_t FailCode );

Call Flags

Description This call allows an application to specify a particular error with a route, or clear a previously indicated error. Setting an error clears the FLG_RTE_UP bit in the flags. When use of the route is attempted, the specified error is returned. Defined error codes for the FailCode argument are:

RtRemove – Remove Route from System Route Table

void RtRemove( void     *hRt,
               uint32_t CallFlags,
               uint32_t FailCode);

Call Flags

Description This call allows an application to remove a route from the system route table independently of any held references to the route. It is similar to the RtSetFailure() call, but differs in two ways:

  1. It removes the route from the system route table so that it can no longer be returned by RtFind().
  2. It calls the IP and Sockets layers to flush the route from any local cache.

Calling this function clears the FLG_RTE_UP bit in the flags. When use of the route is attempted, the error specified in FailCode is returned. Defined error codes for the FailCode argument are:

RtGetFailure – Get the Error Code of a Downed Route

uint32_t RtGetFailure(void *hRt);

Return Value Failure code or NULL for normal operation.

Description This call allows an application to retrieve the error code of a route where the FLG_RTE_UP bit is not set in the route flags. Defined error codes are:

RtGetFlags – Get the Route Flags

uint32_t RtGetFlags(void *hRt);

Description This function returns the state of the route flags for the indicated route. The flag values and definitions were discussed earlier in this section.

RtGetIPAddr – Get the Route IP Address

uint32_t RtGetIPAddr(void *hRt);

Return Value IP host/network address.

Description This function returns the specified route’s IP address in network format.

RtGetIPMask – Get the Route IP Subnet Mask

uint32_t RtGetIPMask(void *hRt);

Return Value IP subnet mask.

Description This function returns the specified route’s IP subnet mask in network format.

RtGetGateIP – Get the Route Gateway IP Address

uint32_t RtGetGateIP(void *hRt);

Return Value IP address of the Gateway or NULL.

Description This function returns the Gateway IP address for the specified route (assuming the FLG_RTF_GATEWAY bit is set in the route flags).

RtGetIF – Get the Route’s Destination Hardware Interface

void *RtGetIF( void *hRt );

Return Value Pointer to Ether Object representing target interface.

Description This function returns an Ether device handle to the egress (target) device of the route. Even local IP addresses have target devices (the device they are bound to).

RtGetMTU – Get the MTU of a Packet Sent via this Route

uint32_t RtGetMTU(void *hRt);

Return Value Packet payload MTU in bytes.

Description This function returns the MTU (not including layer 2 header) of a packet sent via the supplied route.

RtWalkBegin – Start Walking the Route Table

void *RtWalkBegin();

Return Value Pointer to first route in system route table or NULL if no routes.

Description This function initiates a walk of the route table. It returns the first route in the table. The walk must be terminated with RtWalkEnd() for the system to behave properly.

RtWalkNext – Get Next Route While Walking the Route Table

void *RtWalkNext(void *hRt);

Return Value Pointer to next route in system route table or NULL if no routes.

Description This function gets the next route (based off the previous route supplied) in a walk of the route table. The walk must be terminated with RtWalkEnd() for the system to behave properly.

RtWalkEnd – Stop Walking the Route Table

void RtWalkEnd(void *hRt);

Description This function completes the walk of the route table. The last route (if any) obtained from RtWalkBegin() or RtWalkNext() is specified in the calling argument. Otherwise, NULL is used.

A.10 Route Control Object

The route control object is more of a function than an object. It serves as a collection point for route related information in the system. A routing daemon may use this information, or it could simply be logged as debugging information.

When so configured, route control messages are transformed into debug messages by the stack and logged via DbgPrintf(). By default, the route control debug messages are disabled. Also, the message function can be hooked by an application.

Note, control messages can also be suppressed individually by not supplying the FLG_RTF_REPORT flag to the Route object API function when the call is made (as mentioned in the previous section).

A.10.1 Route Control Messages

The basic form of the route control message is an unsigned int message value, with two unsigned 32 bit values for additional data. In most cases these are immediate data. In one instance, the value is actually a 32 bit memory pointer.

Messages are passed internally to the stack via the function:

void RTCReport( uint32_t Msg,
                uint32_t Param1,
                uint32_t Param2 );

Applications should not call this function directly.

The possible values for Msg are as follows:

MSG_RTC_UP – Route is Valid/Pending

Parameters

Description Called after a down message indicating that a route that had previously been in the down state is now up again. This does not mean that the route has been validated, but only that it will attempt to validate itself if used.

MSG_RTC_DOWN – Route is Down

Parameters

Description Called when a route goes down due to an error. Packets sent via a route in this state will generate an error. The most common reason for a route to go down is for a non-response to 5 successive ARP requests. In this case, the route will come back up after the down time has expired.

MSG_RTC_MISS – Route Find “Missed” on Route

Parameters

Description Called when the route table was searched for a route and no matching route was found. This message will never be sent when there is a default route in the table because all searches will have a match (unless a special restricted search is performed).

MSG_RTC_NEW – New Route has been Entered into the Route Table

Parameters

Description Called when a new route is created and entered into the route table. Routes can be created by applications, when new bindings are created, by ICMP redirects, or when local host routes are cloned from local subnet routes.

MSG_RTC_EXPIRED – Route has Expired

Parameters

Description Called when a route with an expiration timeout has expired and been removed from the table.

MSG_RTC_REMOVED – Route has been Manually Removed

Parameters

Description Called when a route has been manually removed from the table. This message is not generated when static routes are removed at system shutdown. Generally, a route can only be removed when its reference count reaches zero. This cannot happen to a static route or a route with an expiration timeout. For the former, no message is ever generated. For the latter, the MSG_RTC_EXPIRED message is used.

MSG_RTC_MODIFIED – Route has been Manually Modified

Parameters

Description Called when a route has been manually modified via the RtModify() call. The stack does not use this function, so if it is not called by an application, this message will never occur.

MSG_RTC_REDIRECT – Route has been Redirected

Parameters

Description Called when an ICMP redirect message is received for a given IP host address. Because the invention of classless subnets, all redirects are treated as HOST redirects. If the stack is configured to generate redirect routes automatically (will do so by default), this message will occur after the new static host redirect route has been created (which will also generate a MSG_RTC_NEW message). If the stack does not create the redirect route, this message occurs before the socket layer is notified so that if a new route is created as a result of this message, the sockets layer will find it.

MSG_RTC_DUPIP – A Duplicate IP Address has been Detected in the System

Parameters

Description Called when an ARP packet is received from a device that has an IP address that is the same as the IP address of the stack on that physical interface. Depending on the age of the address, the application may wish to destroy the binding.

A.10.2 Route Control API Functions

RTCAddHook – Hook RTC Messages

uint32_t RTCAddHook (void (*pfn)(uint, uint32_t, uint32_t));

Return Value 1 if the hook was installed, or NULL on an error (too many hooks).

Description Called to hook a message function to receive route control messages. The argument is a pointer to a message function of the type:

void MyMsgFun( uint32_t Msg,
               uint32_t Param1,
               uint32_t Param2 );

Note that the supplied callback function is called from within an llExit()/llEnter() pair, and thus may call the stack API directly, but may not call any applications API functions, like sockets functions. If such action is required, the callback function may call llExit() when called and then llEnter() before returning.

When the hook is no longer required, the function may be unhooked by calling RTCRemoveHook().

RTCRemoveHook – Unhook RTC Messages

void RTCRemoveHook (void (*pfn)(uint, uint32_t, uint32_t));

Return Value None.

Description Called to remove a previously hooked callback function.

A.11 Configuring the Stack

The stack has multiple configuration options that can be changed by the system programmer. This is possible by altering the default values in a stack configuration structure before the stack is initialized.

A.11.1 Configuration Structure

This section describes a data structure that is generated automatically by the XGCONF configuration. If you are using XGCONF for configuration, you can ignore the structure described here.

The stack internal configuration structure is _ipcfg. Any element in this structure may be modified before the initial system call to ExecOpen(). This structure should not be modified after this initial call.

The _ipcfg structure is of type IPCONFIG, which is defined as follows:

typedef struct _ipconfig {
      uint32_t IcmpDoRedirect;         // Update RtTable on ICMP redirect (1=Yes)
      uint32_t IcmpTtl;                // TTL for ICMP messages RFC1700 says 64
      uint32_t IcmpTtlEcho;            // TTL for ICMP echo RFC1700 says 64
      uint32_t IpIndex;                // IP Start Index
      uint32_t IpForwarding;           // IP Forwarding (1 = Enabled)
      uint32_t IpNatEnable;            // IP NAT Enable (1 = Yes)
      uint32_t IpFilterEnable;         // IP Filtering Enable (1 = Yes)
      uint32_t IpReasmMaxTime;         // Max reassembly time in seconds
      uint32_t IpReasmMaxSize;         // Max reassembly packet size
      uint32_t IpDirectedBCast;        // Look for directed BCast IP addresses
      uint32_t TcpReasmMaxPkt;         // Max reasm pkts held by TCP socket
      uint32_t RtcEnableDebug;         // Enable Route Control Messages (1=On)
      uint32_t RtcAdvTime;             // Time in sec to send RtAdv (0=don't)
      uint32_t RtcAdvLife;             // Lifetime of route in RtAdv
      int      RtcAdvPref;             // Preference Level (signed) in RtAdv
      uint32_t RtArpDownTime;          // Time 5 failed ARPs keep Rt down (sec)
      uint32_t RtKeepaliveTime;        // VALIDATED route timeout (sec)
      uint32_t RtArpInactvity;         // ARP Inactivity Timeout (sec)
      uint32_t RtCloneTimeout;         // INITIAL route timeout (sec)
      uint32_t RtDefaultMTU;           // Default MTU for internal routes
      uint32_t SockTtlDefault;         // Default Packet TTL
      uint32_t SockTosDefault;         // Default Packet TOS
      int      SockMaxConnect;         // Max Socket Connections
      uint32_t SockTimeConnect;        // Max time to connect (sec)
      uint32_t SockTimeIo;             // Default Socket IO timeout (sec)
      int      SockTcpTxBufSize;       // TCP Transmit buffer size
      int      SockTcpRxBufSize;       // TCP Receive buffer size (copy mode)
      int      SockTcpRxLimit;         // TCP Receive limit (non-copy mode)
      int      SockUdpRxLimit;         // UDP Receive limit
      int      SockBufMinTx;           // Min Tx space for "able to write"
      int      SockBufMinRx;           // Min Rx data for "able to read"
      uint32_t PipeTimeIo;             // Default Pipe IO timeout (sec)
      int      PipeBufSize;            // Pipe internal buffer size
      int      PipeBufMinTx;           // Min Tx space for "able to write"
      int      PipeBufMinRx;           // Min Rx data for "able to read"
      uint32_t TcpKeepIdle;            // Time (in 0.1 sec) connection muse be idle
                                       // for TCP to send first keepalive probe.
      uint32_t TcpKeepIntvl;           // Time (in 0.1 sec) between consecutive TCP
                                       // keep alive probes
      uint32_t TcpKeepMaxIdle;         // Time (in 0.1 sec) that a TCP connection can
                                       // go without responding to a probe before
                                       // being dropped
      uint32_t IcmpDontReplyBCast;     // Don't Reply To ICMP ECHO REQ packets
                                       // sent to BCast or Directed BCast
      uint32_t IcmpDontReplyMCast;     // Don't Reply To ICMP ECHO REQ packets
                                       // sent to Multi-Cast
      uint32_t RtGarp;                 // How to handle received gratuitous ARP
      uint32_t IcmpDontReplyEcho;      // Don't Reply to ICMP ECHO packets
      uint32_t UdpSendIcmpPortUnreach; // Send ICMP Port Unreach if UDP port
                                       // is opened or not.
      uint32_t TcpSendRst;             // Send RST if TCP port is opened or not.
      int      SockRawEthRxLimit;      // Raw Ethernet Receive limit
} IPCONFIG;

The structure entries are defined as follows:

_ipcfg.IcmpDoRedirect – Update Route Table on ICMP Redirect

Default Value 1 (Yes)

Description When set to true (1), causes ICMP to automatically create a route to perform redirects on an IP host to the gateway supplied in the redirect message. If set to false (0), you can take whatever action you feel necessary as the ICMP redirect will also generate a route control message.

_ipcfg.IcmpTtl – TTL for ICMP Messages

Default Value 64

Description This is the TTL value ICMP will use in messages it generates as a result of routing IP packets. Legal values are in the range of 1-255.

_ipcfg.IcmpTtlEcho – TTL for ICMP ECHO Reply Messages

Default Value 255

Description This is the TTL value ICMP will use in echo reply messages it generates in response to receiving echo requests. Legal values are in the range of 1-255.

_ipcfg.IpIndex – IP Start Index

Default Value 1

Description This is the initial value that is placed in the IP Id field for IP packets generated by the system. Legal values are in the range of 1-65535.

_ipcfg.IpForwarding – IP Forwarding Enable

Default Value 0 (No)

Description When set to true (1), this allows the stack to forward packets it receives for other IP address to their next hop destination (i.e., it allows the stack to act as a router).

_ipcfg.IpNatEnable – IP Network Address Translation Enable

Default Value 0 (No)

Description When set to true (1), this allows the stack to make use of the network address translation (NAT) module. Note that in addition to setting this structure element, NAT must also be configured. This is described in the following section.

_ipcfg.IpFilterEnable – P Filtering Enable

Default Value 0 (No)

Description When set to true (1), this allows the stack to make use of the IP filtering module. Note that this is automatically turned on when NAT is enabled in the stack. This module is described in more detail in the NAT Service section of this document.

_ipcfg.IpReasmMaxTime – Maximum IP Packet Reassembly Time in Seconds

Default Value 10

Description This is the maximum time that the stack will hold IP packet fragments while attempting to assemble a complete packet. If the time expires before all the fragments arrive, the packet is discarded.

_ipcfg.IpReasmMaxSize – Maximum IP Packet Reassembly Packet Size in Bytes

Default Value 3020

Description This is the maximum packet size that the stack will attempt to reassemble. As soon as the stack determines that the total packet size exceeds this value, the packet is discarded. The default size of 3020 is the maximum size given the default implementation of the packet buffer manager (PBM). If a larger size is desired, then large buffer support must be added to the PBM module. This value is not otherwise restricted. Note the MAC and IP headers are not included in this size limit.

_ipcfg.IpDirectedBCast – Look for Directed Broadcast IP Packets

Default Value 1 (Yes)

Description A directed broadcast address is one where all the bits in the subnet portion of the address are set to 1. For example, on the network 192.168.1.0:255.2555.255.0, the IP address 192.168.1.255 would be a directed broadcast IP. This address is treated as a broadcast for both IP send and receive. The IP layer can be told to disable directed broadcast by setting this value to zero. When disabled, the directed broadcast address is treated like any other host address.

_ipcfg.TcpReasmMaxPkt – Maximum Reassembly Packets Held by TCP Socket

Default Value 2

Description The TCP layer has its own packet reassembly module, allowing TCP packets to arrive out of order, and yet be properly reassembled without the need to retransmit data. One potential issue with embedded environments where the socket receive buffers are large is that a significant number of packets can be tied up in TCP if the first packet of a large burst is lost. This value allows you to specify the maximum number of packets the TCP layer will hold per socket pending reassembly, or in other words, the maximum number of out of order packets allowed.

_ipcfg.RtcEnableDebug – Enable Route Control Messages

Default Value 0 (No)

Description Route control messages keep the system informed of route updates. When set to Yes (1), this variable causes RTC to process the route control message and convert the message into a debug call to llDebugMessage(). Note that an application may also hook into the RTC message loop using the RTCAddHook () function.

_ipcfg.RtcAdvTime – Time in Seconds to Send Router Advertisments

Default Value 0 (Do not Send Router Advertisements)

Description The stack has the ability to automatically send ICMP router advertisements at a predetermined interval. Setting this variable to a non-zero value determines the interval.

_ipcfg.RtcAdvLife – Lifetime of Route in Router Advertisments

Default Value 120

Description If sending router advertisements (see above), this is the route lifetime that will be sent in the ICMP message.

_ipcfg.RtcAdvPref – Preference Level of Route in Router Advertisments

Default Value 0

Description If sending router advertisements (see above), this is the route preference level that will be sent in the ICMP message. This value is signed.

_ipcfg.RtDownTime – Time in Seconds a Route is “Down” Due to Failed ARP

Default Value 20

Description To stop an application from sending endless packets to a route that is not responding to ARP, the route is brought down for a period of time so that the application will receive an error when IP attempts to send. After the designated time, the route is brought back up and will attempt more ARP requests if used again.

_ipcfg.RtKeepAliveTime – Time in Seconds a Validated Route is Held

Default Value 1200

Description Routes should not be held indefinitely. Use of a route is also not sufficient to keep the route alive. This value represents the time an ARP validated route is held before it expires. If the route is revalidated via ARP during this period, the period is extended for this interval from that point in time.

_ipcfg.RtArpInactivity – ARP Inactivity timeout in seconds

Default Value 3

Description Time in seconds beyond which a route and an associated LLI/ARP entry if unused is considered inactive or idle. Inactive ARP entries lifetime is not extended using ARP revalidation process and are instead deleted. The associated route entry is also removed.

_ipcfg.RtCloneTimeout – Default Timeout in Seconds of a Cloned Route

Default Value 120

Description When a host route is first cloned from a network route, it is assigned this default timeout. Once the route is validated via ARP, the timeout is extended (see above).

_ipcfg.RtDefaultMTU – Default MTU for Local Routes

Default Value 1500

Description When a route is created, it gets its MTU from the egress device. However, if the route is local to the system, there is no egress device. In this case, a default MTU is used.

_ipcfg.SockTtlDefault – Default TTL for Packets Sent via a Socket

Default Value 64

Description This is the default IP packet TTL value of packets sent via a socket. Note that the application can override this value with the sockets API.

_ipcfg.SockTosDefault – Default TOS for Packets Sent via a Socket

Default Value 0

Description This is the default IP packet TOS value of packets sent via a socket. Note that the application can override this value with the sockets API.

_ipcfg.SockMaxConnect – Maximum Connections on a Listening Socket

Default Value 8

Description This is the maximum number of connections a socket will pend waiting for a sockets NDK_accept() call from the application. Note: This value is also the upper bounds of the maximum connection argument supplied by an application via the sockets listen() function (calls with higher values are silently rounded down).

_ipcfg.SockTimeConnect – Maximum Time in Seconds to Wait on a Connect

Default Value 80

Description This is the maximum amount to time the sockets layer will wait on an actively connecting socket. The default value of 80 is a few seconds longer than the TCP keep time, so TCP will generate the official (more accurate) timeout error.

_ipcfg.SockTimeIo – Maximum Time in Seconds to Wait on Socket Read/Write

Default Value 0

Description This is the maximum amount of time the sockets layer will wait on a read or write operation without any progress. For example, if the user calls send() with a very large buffer, the function will not time out so long as some fraction of the data is sent during the timeout period. After every successful transfer of data, the timeout period is reset. A timeout value of ZERO means never time out.

_ipcfg.SockTcpTxBufSize – TCP Transmit Buffer Size

Default Value 8192

Description This is the size of the TCP send buffer. A TCP send buffer is allocated for every TCP socket. This value cannot be overridden by the sockets option function.

_ipcfg.SockTcpRxBufSize – TCP Receive Buffer Size (Copy Mode)

Default Value 8192

Description This is the size of the TCP receive buffer allocated for a standard TCP socket. Note that only SOCK_STREAM sockets use receive buffers. This value cannot be overridden by the sockets option function.

_ipcfg.SockTcpRxLimit – TCP Receive Limit (Non-Copy Mode)

Default Value 8192

Description This is the maximum number of cumulative bytes contained in packet buffers than can be queued up at any given TCP based socket. Note that only TCP sockets using SOCK_STREAMNC queue packet buffers directly to a socket. This value cannot be overridden by the sockets option function.

_ipcfg.SockUdpRxLimit – UDP Receive Limit

Default Value 8192

Description This is the maximum number of cumulative bytes contained in packet buffers than can be queued up at any given UDP or RAW based socket. This value cannot be overridden by the sockets option function.

_ipcfg.SockBufMinTx – Min Size in Bytes for Socket “Able to Write”

Default Value 2048

Description This is the size in bytes required to be free in the TCP buffer before it is regarded as able to write by the system. (Affects how the write fd set behaves in a select() call.) This value is usually about 25% to 50% of the send buffer size. UDP and RAW IP sockets are always able to write.

_ipcfg.SockBufMinRx – Min Size in Bytes for Socket “Able to Read”

Default Value 1

Description This is the size in bytes required to be present in a socket buffer for it to be regarded as able to be read by the system. (Affects how the read fd set behaves in a select() call.) Alter at your own risk.

_ipcfg.PipeTimeIo – Maximum Time in Seconds to Wait on Pipe Read/Write

Default Value 0

Description This is maximum amount to time the file layer will wait on a read or write operation on a pipe without any progress. For example, if the user calls send() with a very large buffer, the function will not time out as long as some fraction of the data is sent during the timeout period. After every successful transfer of data, the timeout period is reset. A timeout value of ZERO means never time out.

_ipcfg.PipeBufSize – Size in Bytes of Each End of a Pipe Buffer

Default Value 1024

Description This is the size of a Pipe send and receive buffer. This value is only examined when pipes are created, so changing this value will not affect the buffering of existing pipes.

_ipcfg.PipeBufMinTx – Min Size in Bytes for Pipe Able to Write

Default Value 256

Description This is the size in bytes required to be free in the Pipe buffer before it is regarded as able to write by the system. (Affects how the write fd set behaves in a select() call.) It is usually about 25% to 50% of the send buffer size. This value is only examined when pipes are created, so changing this value will not affect the buffering of existing pipes.

_ipcfg.PipeBufMinRx – Min Size in Bytes for Pipe “Able to Read”

Default Value 1

Description This is the size in bytes required to be present in the Pipe receive buffer for it to be regarded as able to be read by the system. (Affects how the read fd set behaves in a select() call.) Alter at your own risk. This value is only examined when pipes are created, so changing this value will not affect the buffering of existing pipes.

_ipcfg.TcpKeepIdle – Keep Idle Time (0.1 Sec Units)

Default Value 72000 (2 hours)

Description This parameter only affects sockets that have specified the SO_KEEPALIVE socket option. It is the time a TCP connection can be idle before KEEP probes are sent.

_ipcfg.TcpKeepIntvl – Keep Probe Interval (0.1 Sec Units)

Default Value 750 (75 seconds)

Description This parameter only affects sockets that have specified the SO_KEEPALIVE socket option. It specifies the time between probe intervals once TCP begins sending KEEP probes.

_ipcfg.TcpKeepMaxIdle – Keep Probe Timeout (0.1 Sec Units)

Default Value 6000 (10 minutes)

Description This parameter only affects sockets that have specified the SO_KEEPALIVE socket option. It is the time the TCP will continue to send unanswered KEEP probes before timing out the connection.

_ipcfg.IcmpDontReplyBCast – Do NOT Reply to ICMP Echo Request Packets Sent to Broadcast/Directed Addresses

Default Value 0 (Reply to ICMP Echo Request packets sent to broadcast/directed broadcast addresses)

Description When set, causes ICMP to not reply to ICMP Echo Request packets sent to broadcast or directed broadcast addresses.

_ipcfg.IcmpDontReplyMCast – Do NOT Reply to ICMP Echo Request Packets Sent to Multicast Addresses

Default Value 0 (Reply to ICMP Echo Request packets sent to multicast addresses)

Description When set, causes ICMP to not reply to ICMP Echo Request packets sent to multicast addresses.

_ipcfg.RtGarp – How to Handle Received Gratuitous ARP Packets

Default Value 0 (Discard received gratuitous ARP packets)

Description The hosts may send gratuitous ARP packets to broadcast their [IP number, MAC address] information to inform other hosts in the network. This parameter determines the handling policy of received gratuitous ARP packets based on the following configuration values:

_ipcfg.IcmpDontReplyEcho – Reply to ICMP Echo Request Packet

Default Value 0 (Reply to ICMP Echo Request packets with ICMP Echo Reply)

Description The ICMP Echo Request can be used to determine if a host on the Internet is responding. The host receiving ICMP Echo Request replies with ICMP Echo Reply packet. The ping program uses ICMP Echo Request/Reply packets. It is a valuable tool diagnosing host and network problem. However, it can also be used to discover the IP numbers of hosts connected on the Internet. This option allows configuring to reply ICMP Echo Request packets or not.

_ipcfg.UdpSendIcmpPortUnreach – Reply with ICMP Port Unreachable if UDP Port is Not Listened

Default Value 1 (Reply with ICMP Port Unreachable message)

Description The “port scanning” is used to discover which services is used on a host. If a UDP port is not listened, the NDK replies with an “ICMP Port Unreachable” message to any received UDP datagram. So, you can find that if a UDP port is not open, and by exclusively determine which ports are open. This option allows configuring to reply or not to not listened UDP ports.

_ipcfg.TcpSendRst – Reply with RST message if TCP Port is Not Listened

Default Value 1 (Reply with RST message)

Description The “port scanning” is used to discover which services is used on a host. If a TCP port is not listened, the NDK replies with an RST message to a connection attempt (received SYN message). So, you can find that if a TCP port is not open, and by exclusively determine which ports are open. This option allows configuring to reply or not to not listened TCP ports.

_ipcfg.SockRawEthRxLimit – Raw Ethernet socket receive limit

Default Value 8192

Description This is the maximum number of cumulative bytes contained in packet buffers than can be queued up at any given Raw Ethernet socket. This value cannot be overridden by the sockets option function.

A.12 Network Address Translation

The stack includes a small network address translation (NAT) function that can be used to setup virtual networks when the stack is acting as a router. When enabled, NAT will translate routed packets sent from or to a targeted virtual network.

If you are using XGCONF to configure your application, you can configure the NAT service to be enabled in the application by checking the box in the property sheet to add the module to your configuration. See the SPRU523 (TI Network Developer’s Kit (NDK) User’s Guide.) and the context-sensitive help for details.

A.12.1 Operation

NAT works by altering the TCP/UDP port numbers of a packet sent from a virtual network, and then using an alternate IP on the physical network to transfer the packet. For ICMP packets, the Id field of ICMP requests is used.

When configured, NAT will have a target virtual network that consists of an IP base address and a subnet mask. It also has a physical IP address that is used as a type of proxy for the translated packets.

The types of packets translated include:

The translation entries are created dynamically, and have a lifetime based on their protocol. ICMP and UDP translation entries have a fixed time limit based on the last time they were accessed. TCP expiration is based on the state of the TCP connection.

Note that some protocols (like FTP) communicate TCP/UDP port information in the packet payload. These types of protocols will not function under NAT without a custom proxy program to alter the packet payload. Individual proxies are not provided.

A.13 Network Interface Management Unit (NIMU)

A.13.1 Synopsis

NIMU architecture replaces the existing LL packet layer architecture, originally designed to communicate with only one instance of the device driver. It adds flexibility to the NDK core stack by adding the capability to communicate and control multiple device drivers simultaneously. In comparison to single-port serial device applications, the new NIMU architecture is best suited for Ethernet type devices where it is common to have multiple instances running concurrently.

NIMU implementation has two parts:

Figure A-1 showcases NIMU architecture.

Figure A-1 NIMU Architecture

A.13.2 Data Structure Definition

The NIMU network interface object is equivalent to the device independent packet layer. Each NIMU network interface object is described with the following data structure:

typedef struct NETIF_DEVICE
{
    LIST_NODE links;

    /* Device Identification */
    uint32_t index;
    char name[MAX_INTERFACE_NAME_LEN];

    /* Device Specific Information */
    uint32_t flags;
    uint32_t mtu;
    unsigned char mac_address[6];

    /* Internal Use: Number of references held. */
    uint32_t RefCount;
    uint32_t type;

    /* Pointer to 'driver specific private data' */
    void* pvt_data;

    /*****************************************************************
     ***************** Driver Interface Functions ********************
     *****************************************************************/

    int (*start)(struct NETIF_DEVICE* ptr_net_device);
    int (*stop)(struct NETIF_DEVICE* ptr_net_device);
    void (*poll)(struct NETIF_DEVICE* ptr_net_device, uint32_t timer_tick);
    int (*send)(struct NETIF_DEVICE* ptr_net_device, PBM_Handle hPkt);
    void (*pkt_service) (struct NETIF_DEVICE* ptr_net_device);
    int (*ioctl)(struct NETIF_DEVICE* ptr_net_device, uint32_t cmd,
         void* pbuf, uint32_t size);
    int (*add_header) (struct NETIF_DEVICE* ptr_net_device, PBM_Handle hPkt,
         unsigned char* dst_mac, unsigned char* src_mac, uint16_t ether_type);

} NETIF_DEVICE;

The structure entries are described below:

Description Holds pointers to the previous and next devices in the chain of NIMU registered devices in the system.

index – Unique number to identity the device in the system

Description Numerically identifies devices uniquely in the system. Driver authors should not alter this value. It will be assigned in NIMURegister(). The value will be the device’s .init function’s index in the NIMU Device Table + 1.

name – Device name

Description Name of the device to identify it uniquely in the system. Driver authors can specify a name for the device; but in the case of conflicts its value will be modified to be unique in the system. Therefore, if driver authors are using this in their code it is best to re-read the value after the ‘registration’ process.

flags – Device specific flags

Description Contains additional information which further describes the network device and its properties. The following flags may be set by a driver author when the NETIF_DEVICE structure is initialized. Flags should not be set at runtime, because the driver modifies flags to maintain information about driver status.

    /* Initialization function in driver */
    ptr_device = mmAlloc(sizeof(NETIF_DEVICE));
    mmZeroInit (ptr_device, sizeof(NETIF_DEVICE));
    ...
    ptr_device->flags = NIMU_DEVICE_NO_FREE;

mtu – Maximum transmission unit for the device

Description This defines the maximum size of a packet that can be transmitted over the device without any fragmentation. Driver authors should configure this value to the maximum data payload that can be carried without the corresponding Layer2 header. For example, in Ethernet this will be 1514 (maximum data payload) - 14 (L2 Ethernet header) = 1500. By default, the value for mtu is 1500.

mac_address – Hardware address of this device

Description This is the hardware address of the device that uniquely identifies the device.

RefCount – Reference count of the device

Description This indicates the number of references of network interface objects held by components. Network Interface objects can only be removed from the system if there are no references of them held in the system. This is used internally by the NIMU control module and should never be modified by the driver.

type – Defines the interface type

Description For compatibility with the old network interface object, this is set to HTYPE_ETH or HTYPE_PPP depending on the type of network interface object. Moving forward, this field will be obsolete and application authors should use the field instead.

pvt_data – Pointer to device-specific private data

Description This can be used by the driver authors to store any additional driver-specific data. Memory allocation, Initialization and cleanup is the responsibility of the driver author the NDK NIMU module does not use this field in any way and is opaque to the NDK core stack.

The driver interface functions can be described as follows:

start – Callback function registered by driver to open the controller

#include <stkmain.h>

int (*start) (NETIF_DEVICE* ptr_netif_device);

Return Value The function returns a value of 0 on success and a negative value on error.

Description The device start function is a registered call back function which is populated by the device driver author in the network interface object control block. Once the network interface object is registered with the NIMU NDK core stack during the registration process, the NDK core stack will call out this function. Device driver authors are recommended to place hardware initialization and start-up code in this callback function. After the successful completion of the API, the driver should be able to receive and transmit packets from the hardware. Driver authors must specify this callback function or else the NIMU registration will fail.

stop – Callback function registered by driver to close the controller

#include <stkmain.h>

int (*stop) (NETIF_DEVICE* ptr_netif_device);

Return Value The function returns a value of 0 on Success and a negative value on error.

Description The device stop function is a registered call back function which is populated by the device driver author in the network interface object control block. The function is invoked by the NDK core stack when the NIMU network interface object is being unregistered from the NIMU system. Driver authors are recommended to place the hardware controller shutdown code in this API. After the successful completion of this API, the driver should not be able to receive or transmit packets. Driver authors must specify this callback function or else the NIMU registration will fail.

poll – Callback function registered by driver to be called periodically by the NDK.

#include <stkmain.h>

void (*poll) (NETIF_DEVICE* ptr_netif_device, uint32_t timer_tick);

Return Value None

Description The call back function is used by the NDK core stack to periodically call the driver. Driver authors can use this function to monitor link activity or do any work outside the kernel mode context. The callback function registered here is not called from kernel mode.

The following table summarizes the significance of timer_tick.

timer_tick Definition
1 The polling function is called because of a timer event. It is recommended that device authors use this for doing periodic activities such as link management, watchdog timers, etc.
0 This indicates that the polling function has been called because the driver has indicated a STKEVENT_signal. This is useful for device authors to signify some activity outside kernel mode.

send – Callback function registered by driver to send packets.

#include <stkmain.h>

int (*send) (NETIF_DEVICE* ptr_netif_device,
             PBM_Handle    hPkt );

Return Value The function returns a value of 0 on success and a negative value on error. On success, the packet memory cleanup needs to be handled by the driver; but if an error is returned, the NDK core stack will clean up the packet.

Description The device send function is an API which is used by the core NDK stack to pass packets to the driver for transmission. Driver authors must specify this callback function or else the NIMU registration will fail.

pkt_service – Callback function registered by driver for the NDK stack to receive packets

#include <stkmain.h>

void (*pkt_service) (NETIF_DEVICE* ptr_netif_device);

Return Value None

Description The API is used by the NDK core stack to receive packets from the driver. This is indicated through the STKEVENT_signal API once the packets have been received. The NDK core scheduler detects this signal and ensures that the appropriate packet service API is called. This function is called from kernel mode. The drivers should handle conditions where even though this API is called there are no packets in the receive queue.

ioctl – Callback function registered by driver for the NDK stack to get/set configuration

#include <stkmain.h>

void (*ioctl) (NETIF_DEVICE  *ptr_device,
               uint32_t      cmd,
               void          *pBuf,
               uint32_t size );

Return Value The function returns a value of 0 on Success and a negative value on error.

Description This API is used by the NDK core stack to be able to get/set configuration in the drivers. The interface can be used to configure the multicast address list, change device MAC address, etc. Each NIMU network interface object can also identify its own custom IOCTL commands to do any device-specific configuration. This function is called from kernel mode.

add_header – Callback function registered by driver for the NDK stack to add custom L2 header

#include <stkmain.h>

void (*add_header)(NETIF_DEVICE*  ptr_netif_device,
                   PBM_Handle     hPkt,
                   unsigned char* dst_mac,
                   unsigned char* src_mac,
                   uint16_t       ether_type);

Return Value None

Description This is a registered call back function added by the driver to add custom layer2 headers on packets. This is called by the NDK core stack after layer3 has done its work. Ethernet driver authors can set this API to be NIMUAddEthernetHeader. However, if the driver is not a standard Ethernet driver, this can be used to add an appropriate layer2 header. For example, If the driver is USB-RNDIS, then this function can be defined to add not only the standard Ethernet header, but also the RNDIS header. If there are custom headers, then ensure there is sufficient head room in the packet buffers which are being allocated. Configuration of header and trailer sizes are provided by the NIMU exported API NIMUSetRsvdSizeInfo. This function is called from kernel mode.

A.13.3 NIMU Configuration

For sample NIMU API usage, please refer to the DSK6455 and DM642 Ethernet driver code in the latest platform specific packages.

A.13.4 API Function Overview

Function Description
NIMURegister Registers a NIMU compliant network interface object
NIMUUnregister Un-registers a NIMU compliant network interface object
NIMUGetRsvdSizeInfo Get current header and trailer size
NIMUSetRsvdSizeInfo Set current header and trailer size
NIMUReceivePacket Interface API to pass packets to the NDK core stack
NIMUIoctl Get/set configuration from the NIMU module and drivers

A.13.5 API Function Description

NIMURegister – Callback Registers a NIMU compliant network interface object

#include <stkmain.h>

int NIMURegister (NETIF_DEVICE* ptr_netif_device);

Return Value The function returns a value of 0 on success and a negative value on error.

Description The API is used to register a NIMU compliant network interface object with the core NDK NIMU module. As part of the registration process, the API will also invoke the start API to open and begin the corresponding the network device.

NIMUUnregister – Un-registers a NIMU compliant network interface object

#include <stkmain.h>

int NIMUUnregister (NETIF_DEVICE* ptr_netif_device);

Return Value The function returns a value of 0 on success and a negative value on error.

Description The API is used to un-register a previously registered NIMU compliant network interface object from the core NDK NIMU module. As part of the un-registration process, the API will also invoke the stop API to close the corresponding network device. This API can only be called from within kernel mode.

NIMUGetRsvdSizeInfo – Get current header and trailer size

_include <stkmain.h>_

_void NIMUGetRsvdSizeInfo ( int *hdr_size,
                            Int *trail_size );_

Return Value The function returns the current header and trailer reserved space.

Description All packets set aside by the network interface management unit are allocated reserving some space for headers and trailers. The API is used to get the current header and trailer size that NIMU will use from this point on. This API can only be called from within kernel mode.

NIMUSetRsvdSizeInfo – Set current header and trailer size

#include <stkmain.h>

void NIMUSetRsvdSizeInfo (int hdr_size,
                          int trail_size);

Return Value None

Description All packets set aside by the network interface management unit are allocated reserving some space for headers and trailers. The API is used to set the current header and trailer size that NIMU will use from this point on. There are no validations done on the header and trailer size values passed to the function. It is assumed that the system authors have configured this value correctly. This API can only be called from within kernel mode.

NIMUReceivePacket – Interface API to pass packets to the NDK Core stack

#include <stkmain.h>

int NIMUReceivePacket (PBM_Handle hPkt);

Return Value The function returns the 0 on success and <0 on error. Note that in case of error, the function will clean the packet memory.

Description This is the interface function which has been exported by the NIMU module to pass received packets to the networking stack. This API is available only for NIMU network interface objects and replaces the EtherRxPacket API, which handles the LL packet architecture. This API can only be called from within kernel mode.

NIMUIoctl – Get/Set configuration from the NIMU Module and Drivers

#include <stkmain.h>

int NIMUIoctl( uint32_t cmd,
               NIMU_IF_REQ *ptr_nimu_ifreq,
               Void *pBuf,
               uint32_t size );

Return Value The function returns the 0 on success and a negative value on error.

Description This function is used to get and set configuration parameters from either the NIMU module or to the NIMU network interface objects driver attached to the NIMU module. The NIMU_IF_REQ structure is defined as follows:

typedef struct NIMU_IF_REQ
{
    uint32_t index;                        /* Device Index */
    char     name[MAX_INTERFACE_NAME_LEN]; /* Device Name */
} NIMU_IF_REQ;

The structure is used to identify the NIMU network interface object which is being referred to. The introduction of NIMU network interface objects can be referred by a name or by a unique index. The NIMU IOCTL Interface searches for a matching device using the name; if no device is found then the device index is used to find a matching device. If none is found then an error is returned. The cmd parameter is used to specify the command, and pBuf and size are defined on a per command basis. The following table summarizes the set of supported IOCTL commands and the values expected in the pBuf and size.

Command pBuf Size Description
NIMU_GET_DEVICE_HANDLE &(void *) 4 Use this to get the NIMU device handle associated with the device.
NIMU_GET_DEVICE_MTU &uint16_t 2 Use this to get the MTU associated with the device.
NIMU_SET_DEVICE_MTU &uint16_t 2 Use this to set the MTU associated with the device. No validations are done on this.
NIMU_GET_DEVICE_MAC &(unsigned char[6]) 6 Get the device MAC address.
NIMU_SET_DEVICE_MAC &(unsigned char[6]) 6 Set the device MAC address. In this case the MAC address is also passed down to the NIMU Network interface object so that the MINI-driver can also be reconfigured. If this is not supported by the driver the IOCTL fails.
NIMU_GET_DEVICE_NAME &(unsigned char[20]) 20 Use this API to translate the device index to device name. The device name is returned in pBuf
NIMU_GET_DEVICE_INDEX &uint16_t 2 Use this API to translate the device name to device index. The device index is returned in pBuf
NIMU_ADD_MULTICAST_ADDRESS &(unsigned char[6]) 6 This API is used to add a multicast MAC address to the device multicast list. This IOCTL needs to be handled in the NIMU Network Interface Object Driver.
NIMU_DEL_MULTICAST_ADDRESS &(unsigned char[6]) 6 This API is used to delete a multicast MAC address to the device multicast list. This IOCTL needs to be handled in the NIMU Network Interface Object Driver.
NIMU_ENABLE_RX_CSO_ERROR_HW_DROPS &(uint32_t) 4 This API is used to control the behavior of the MAC hardware, regarding devices with hardware checksum offload (CSO) support. This IOCTL is used to configure the MAC hardware, on the RX channel specified in pBuf, to drop packets received with bad checksum values. When this setting is in effect, such bad packets will be dropped by the hardware; as such, they will never reach the NIMU driver code. Only supported for devices with hardware checksum offload enabled. Affects RX packets only. If this is not supported by the driver, the IOCTL fails.
NIMU_DISABLE_RX_CSO_ERROR_HW_DROPS &(uint32_t) 4 This API is used to control the behavior of the MAC hardware, regarding devices with hardware checksum offload (CSO) support. This IOCTL is used to configure the MAC hardware, on the RX channel specified in pBuf, to not drop packets received with bad checksum values. When this setting is in effect, such bad packets will be ignored by the hardware; as such, they will be passed up to the NIMU driver code where they must be dropped in software. Only supported for devices with hardware checksum offload enabled. Affects RX packets only. If this is not supported by the driver, the IOCTL fails.

Typically, most of the requests are done for a specific network interface object; but in some cases, configuration might be required for all NIMU network interface objects. In the case of these special cmd, the value of the interface request is NULL. These special case cmd are shown in the following table.

Command pBuf Size Description
NIMU_GET_NUM_NIMU_OBJ &uint16_t 2 Use this to get the number of NIMU network interface objects active in the system.
NIMU_GET_ALL_INDEX &uint16_t (Number of NIMU Objects) * sizeof(uint16_t) The API is used to populate an array of all device index’s present in the system. Memory allocated should be sufficient to store all this information. Use the above API to get an active count and allocate memory appropriately for pBuf.

This API cannot be called from kernel mode. It is recommended that system application authors use this API instead of directly trying to access low layer information.

A.14 Virtual LAN (VLAN) Support

A.14.1 Synopsis

Virtual LAN (VLAN) support in NDK adds the capability of receiving and transmitting VLAN tagged packets through the stack. VLAN support in NDK is available only in conjunction with NIMU architecture.

Figure A-2 highlights the VLAN module components in reference to the NIMU enabled NDK stack.

Figure A-2 VLAN Module Placement in NIMU Enabled NDK Stack

The NIMU core and VLAN modules in the NDK stack are closely tied together. The VLAN module is brought down by the NIMU as a part of its de-initializing routine during the system shutdown. Similarly, the NIMU module’s packet receive routine is responsible for handing over all VLAN tagged packets to the VLAN module for processing. It does so by checking all incoming packets for their L2 type and if it is VLAN, forwards the packets onto the VLAN module of the stack for further processing.

VLAN implementation has 2 parts:

A VLAN network interface object is a NIMU device characterized additionally by these VLAN specific attributes:

A.14.2 User Priority Mapping Configuration

A user application can configure the user priority for a VLAN device using the NDK_setsockopt() API, which is discussed in great detail in Section 3.3.3. The following sections provide an example on how the VLAN user priority can be communicated by an application to the VLAN module in the core stack and how they are translated to bits in VLAN header of the packets.

A.14.2.1 User Priority Configuration

Figure A-3 shows two applications that use the NDK stack to communicate with other devices on the network through an Ethernet interface. The applications use the socket APIs described in Section 3.3.3 to send/receive packets through the device. With VLAN support, there should be a mechanism which the application authors will use to communicate the VLAN user priority value to associate their socket to the stack. This enables the stack to mark the user priority bits appropriately in the VLAN headers of the packets.

In Figure A-3, let’s assume that Application1 is a high priority application and Application2 is a lower priority application. The network administrators on which the Ethernet is physically connected should have defined a Traffic Conditioning Agreement. All devices connected on that network should comply to this agreement that defines the user priority values for high and low priority application.

Figure A-3 VLAN Example

Assuming that the traffic condition agreement mentioned in Section A.14.2.1 is as follows:

Application Priority VLAN User Priority Bits
High 7
Low 5
Undefined 0

This implies that all packets generated by Application1 should be marked with the user priority value of 7. Similarly, all packets generated by Application2 should be marked with the user priority value of 5.

Each application is capable of sending and receiving packets only through the socket interface; therefore, you need to ensure that all sockets that send packets in Application1 are mapped to the correct priority level to ensure that they are marked to the user priority value of 7.

The other advantage of working out priority at the socket level instead of the application level is that granularity exists which allows prioritization even inside the applications. For example, one data flow in Application1 could be low priority while the other could be high priority.

As mentioned before, the user priority can be communicated by the application using NDK_setsockopt API with the socket option parameter set to SO_PRIORITY and the value set to the appropriate user priority. Note that the valid range for the user priority is 0 - 7; although a value of 0xFFFF can be used to reset the priority back to default. For more details on how to get/set the user priority using getsockopt() and setsockopt() socket APIs, see Section 3.3.3.

Example

The following example configures the socket priority to be 0:

uint16_t priority = 0;
if (NDK_setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority) < 0)
    printf ("TEST Case Failed: 0x%x\n", fdError());

NOTE: Configuration of the socket priority is equivalent of indicating what the priority of the application is. The socket priority has no meaning outside the stack and is only used internally.

A.14.2.2 Marking Packet Priority

This section documents how the translation needs to be done to mark the user priority bits. Once an application has marked the socket priority, the VLAN module translates the socket priority to user priority bits using the following formula:

prio_mapping[socket_priority] = VLAN User Priority

The table in the previous section shows the traffic conditioning agreement for the VLAN user priority bits. It indicates that Application1 has HIGH priority so the VLAN module should mark its VLAN user priority bits set to 7 and that Application2 has LOW priority so the VLAN module should mark its VLAN user priority bits set to 5.

To satisfy this requirement, Application1 should have the following code snippet:

uint16_t priority = 0;
if (NDK_setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority) < 0)
    printf ("TEST Case Failed: 0x%x\n", fdError());

The idea is that all packets generated by Application1 will have a socket priority of 0 (HIGH).

Similarly, Application2 should have the following code snippet:

uint16_t priority = 1;
if (NDK_setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority) < 0)
    printf ("TEST Case Failed: 0x%x\n", fdError());

The idea is that all packets generated by Application2 will have a socket priority of 1 (LOW).

The VLAN device should be created with the following code snippet:

/* By default: We configure the priority mapping to be as follows:-
 * Priority | VLAN User Priority
 * -----------------------------------
 *    0     |     7
 *    1     |     5
 *    2     |     0
 *    3     |     0
 *    4     |     0
 *    5     |     0
 *    6     |     0
 *    7     |     0
 */
prio_mapping[0] = 7;
prio_mapping[1] = 5;
prio_mapping[2] = 0;
prio_mapping[3] = 0;
prio_mapping[4] = 0;
prio_mapping[5] = 0;
prio_mapping[6] = 0;
prio_mapping[7] = 0;

/* Use the VLAN API to create a new VLAN device. */
if (VLANAddDevice (src_index, 10, 0, prio_mapping) < 0)
    printf ("Error VLAN Failed errcode=%d\n");

The important point to note in this example is the configuration of the user priority table. This is basically the translation of the traffic conditioning agreement to an array.

With the VLAN device created as follows, if a packet is transmitted from Application1 it is marked with a socket priority of 0, and then it is translated by the VLAN module to user priority 7.

A.14.3 Configuring VLAN To Send Data To A Linux PC

This section provides an overview of how to set up a VLAN device using the available commands in the NDK Telnet console. The commands are used to create and configure a VLAN device, and then send data over that device to a Linux PC. Note that the steps in this section assume Ubuntu Linux; however, similar commands should be available on other versions of Linux.

A.14.3.1 Configuring VLAN On the Linux PC

  1. Ensure your Linux box has the vlan application. If not, it can be installed by running the following command:

    sudo apt-get install vlan
  2. The ncat or a similar application is also needed. It can be installed by running the following command:

    sudo apt-get install ncat
  3. The Wireshark application is recommended, but not required. It can be installed by running the following command:

    sudo apt-get install wireshark
  4. Load the 8021q module into the kernel:

    sudo modprobe 8021q
  5. Assuming you’re connected on interface eth0 (with IP address 192.168.0.31):

    ifconfig
    eth0  Link encap:Ethernet  HWaddr --:--:--:--:--:--
          inet addr:192.168.0.31  Bcast:192.168.0.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:446 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:38520 (38.5 KB)  TX bytes:33324 (33.3 KB)
  6. Use the vconfig command to add a new VLAN interface with VLAN ID 10. This will create a new interface, but it will not have any IP address assigned to it:

    sudo vconfig add eth0 10
  7. Assign an IP address to the VLAN interface, choosing a different network:

    sudo ip addr add 10.90.90.15/24 broadcast 10.90.90.255 dev eth0.10
  8. Ensure that the interface is up:

    sudo ip link set up eth0.10
  9. You should now see the following detailed in the ifconfig command for the VLAN interface:

    eth0.10   Link encap:Ethernet  HWaddr --:--:--:--:--:--
          inet addr:10.90.90.15  Bcast:10.90.90.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:21 errors:0 dropped:0 overruns:0 frame:0
          TX packets:26 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:5951 (5.9 KB)  TX bytes:6643 (6.6 KB)
  10. Use ncat to run a UDP echo server on port 10000, in order to receive incoming VLAN packets sent from the NDK:

    ncat -e /bin/cat -k -u -l 10000
  11. Run Wireshark on the VLAN interface eth0.10 to capture packets sent to the VLAN interface
  12. The Linux side is now ready to receive VLAN UDP packets

A.14.3.2 Configuring VLAN In The NDK

The following steps provide an overview of how to add and configure a VLAN device, using the commands available in the Telnet console.

  1. Build an NDK application that enables the Telnet console. The app should be DHCP enabled to get an IP automatically.
  2. Load and run the application.
  3. From a terminal, telnet into the NDK application using the IP address obtained from DHCP.
  4. Run the ipaddr command to display the list of interfaces and associated IP addresses configured for the NDK:

    >ipaddr all
    Interface Name: eth0
    Interface Id  : 1
    IP Address    : 192.168.0.9
    IP Mask       : 255.255.255.0
    MTU           : 1500 bytes
    MAC Address   : --:--:--:--:--:--
  5. Use the vlan command to add a new VLAN interface on top of the existing Ethernet interface (IF number 1). Use VLAN ID 10 and priority of 4:

    >vlan add 1 10 4
    Successfully created new VLAN Device 2
  6. Repeat the ipaddr command. You should now see the new VLAN interface, in addition to the previously seen Ethernet interface:

    >ipaddr all
    Interface Name: eth0:10
    Interface Id  : 2
    MTU           : 1496 bytes
    MAC Address   : --:--:--:--:--:--
    Interface Name: eth0
    Interface Id  : 1
    IP Address    : 192.168.0.9
    IP Mask       : 255.255.255.0
    MTU           : 1500 bytes
    MAC Address   : --:--:--:--:--:--
  7. Use the ipaddr command to assign an IP address to the VLAN interface:

    >ipaddr 2 10.90.90.21 255.255.255.0
  8. Again, repeat the ipaddr command. You will see the VLAN interface now has the IP associated with it:

    >ipaddr all
    Interface Name: eth0:10
    Interface Id  : 2
    IP Address    : 10.90.90.21
    IP Mask       : 255.255.255.0
    MTU           : 1496 bytes
    MAC Address   : --:--:--:--:--:--

    A.14.3.3 Sending Data Over The VLAN Device

  9. Run the vlan send command to send UDP packets to the Linux laptop on port 10000:

    >vlan send 10.90.90.15
    TEST Case Passed: Priority 0x8 is incorrect and was detected
    Packet with priority 0 has been sent
    Packet with priority 1 has been sent
    Packet with priority 2 has been sent
    Packet with priority 3 has been sent
    Packet with priority 4 has been sent
    Packet with priority 5 has been sent
    Packet with priority 6 has been sent
    Packet with priority 7 has been sent
    Packet with default priority has been sent
  10. On the Linux PC, you should see the packets being sent in Wireshark. If you inspect the Ethernet frame, you will see the VLAN header and ID of 10

A.14.4 VLAN API Functions

This section details the VLAN APIs. Note, some functions are for internal stack usage only.

A.14.4.1 Function Overview

The following are the APIs exported by the VLAN module in the core stack.

Function Description
VLANInit Initializes the VLAN module in the core NDK stack.
VLANDeinit Deinitalizes the VLAN module and shuts down all the VLAN enabled NIMU network interface objects in the system.
VLANReceivePacket Handles all VLAN tagged packets on the receive path.
VLANAddDevice Creates a VLAN device on a specified NIMU interface.
VLANDelDevice Deletes a previously created VLAN device.

A.14.4.2 API Functions

VLANInit – Initializes the VLAN module in the core NDK stack.

#include <stkmain.h>

void VLANInit (void)

Return Value None

Description This function is used to initialize the VLAN module in the NDK core stack. It is for internal stack usage only and should not be called by any application directly. This function is called as a part of the stack bring up, i.e., NC_NetStart invocation by the user application. It initializes the header and trailer sizes for VLAN NIMU objects that would be created in the system later by a user application.

VLANDeinit – Deinitalizes VLAN module and shuts down VLAN enabled NIMU network interface objects

#include <stkmain.h>

void VLANDeinit (void);

Return Value None

Description This function is used to shut down the VLAN module in the core stack. It closes and shuts down any VLAN enabled NIMU network interface objects existent in the system. This function is for internal stack usage only and should not be called by any application directly. It is called as a part of stack teardown, i.e., when the user application invokes NC_NetStop function.

VLANReceivePacket – Handles all VLAN tagged packets on the receive path

#include <stkmain.h>

uint32_t VLANReceivePacket (PBM_Handle hPkt);

Return Value Returns 0xFFFF on error and on success returns the values of the encapsulated protocol.

Description This function is called by the NIMU receive function when a VLAN tagged packet is received. It validates the packet and ensures that there is a valid VLAN node on the system that can process the packet. This function is for internal stack usage only and should not be called by any application directly.

VLANAddDevice – Create a VLAN Network Interface object

#include <stkmain.h>

int VLANAddDevice (uint32_t      src_index,
                   uint16_t      vlan_id,
                   unsigned char default_priority,
                   unsigned char prio_mapping[]);

Return Value This function returns the device index of the new VLAN network interface object created on a success or a negative value on error.

Description This API enables system developers to create a VLAN Network Interface object on a specified NIMU source interface based on the arguments supplied. Note that this function must be called from user mode only.

VLANDelDevice – Deletes a previously created VLAN device

#include <stkmain.h>

int VLANDelDevice (uint16_t dev_index);

Return Value This function returns 0 on a success or a negative value on error.

Description This API deletes a previously created VLAN network interface object. The device index passed is the index returned from the prior invocation of the VLANAddDevice API. Note that this function must be called from user mode only.

A.15 Raw Ethernet Module

A.15.1 Synopsis

A new module called the Raw Ethernet Module has been added to the NIMU enabled NDK stack to handle the Raw Ethernet packet traffic through NDK. A Raw Ethernet packet can be defined as an Ethernet packet whose Protocol type (Offset 12 in the Ethernet header) doesn’t match any of the well-known standard protocol types like IP (0x800), IPv6 (0x86DD), VLAN (0x8100), PPPoE Control (0x8863), PPPoE Data (0x8864). The Raw Ethernet Module interfaces with the application and the stack to provide the APIs required in configuring a raw Ethernet socket, and in sending and receiving packets using it.

Figure A-4 shows the placement of the Raw Ethernet module in the NDK stack.

Figure A-4 Raw Ethernet Channel Manager Module in NDK

As Figure A-4 illustrates, traditional data path for the IP packets is different from the data path followed by the Raw Ethernet packets. The complete Layer 4, Layer 3 and Layer 2 processing in the NDK IP stack is bypassed for the Raw Ethernet packets. The Raw Ethernet packets alternatively travel through the Raw Ethernet Module which maintains a mapping between the custom Ethernet type configured and the interface on which the packets should be transmitted. This mapping is maintained in the “Raw Ethernet Socket” object structure. Also, shown in the above figure is the implementation of Raw Ethernet prioritization implementation in the driver using separate queues for IP and raw Ethernet traffic. This implementation is just an illustration of how this feature can be extended further to suit any application demands and is not really tied in with the design of Raw Ethernet Socket and Modules.

NOTE: The Raw Ethernet Module is supported only with NIMU enabled NDK stacks and drivers.

For more details on the Raw Ethernet Socket APIs, see Section 3.4 of this document.

A.15.2 Raw Ethernet Data Prioritization - Socket Priority Use Case

A user application can use the Socket Priority to add any sort of special differentiation that the raw Ethernet application would require for all packets travelling using the specified socket. For example, if there exists two raw Ethernet sockets, they could be both configured with different priorities using NDK_setsockopt() API discussed in Section 3.3.3. Further, a desired QoS scheme can be implemented in the Ethernet driver using this priority that is carried onto the packets. It can be also used by an application to map the priority to certain transmission properties like EMAC channel number on which the packets are to be transmitted etc. This section describes one such implementation of Socket Priority to implement Raw Ethernet Packet Prioritization over traditional IP traffic.

A.15.2.1 Socket Priority Configuration

In this specific use case, the application requires that:

For the implementation to achieve this also has two requirements:

The following example creates a Raw Ethernet socket and configures the EMAC channel number to 3 using socket priority:

SOCKET sraw = INVALID_SOCKET;
uint16_t priority = 0;
int retVal, val;

/* Allocate the file environment for this task */
fdOpenSession( TaskSelf() );

/* Create the raw Ethernet socket */
sraw = NDK_socket(AF_RAWETH, SOCK_RAWETH, 0x300);
if( sraw == INVALID_SOCKET )
{
    printf("Fail socket, %d\n", fdError());
    fdCloseSession (TaskSelf());
    return;
}

/* Configure the transmit device */
val = 1;
retVal = NDK_setsockopt(sraw, SOL_SOCKET, SO_IFDEVICE, &val, sizeof(val));
if(retVal)
    printf("error in setsockopt \n");

/* Configure the EMAC channel number */
val = 3;
retVal = NDK_setsockopt(sraw, SOL_SOCKET, SO_PRIORITY, &val, sizeof(val));
if(retVal)
    printf("error in setsockopt \n");
/* Peek into the packet to check out if any prioritization is needed.
 *
 * All raw Ethernet packets are tagged with the EMAC channel number onto
 * which they need to be sent out in the PktPriority field.
 */
if (((PBM_Pkt *)hPkt)->PktPriority != PRIORITY_UNDEFINED)
{
    /* Enqueue the packet in the Raw Tx Queue and send it for transmission.
     * Use the PktPriority field now as the EMAC channel number on which
     * packet needs to be Txed
     */
}
else
{
    /* This is just a normal IP packet. Enqueue the packet in the
     * Tx queue and send it for transmission.
     */
}

For more details on this use case implementation, please refer to the Ethernet driver code packaged in the NSP for TCI6488.

A.15.3 API Function Overview

The following APIs are exported by the Raw Ethernet module of the core NDK stack:

Function Description
RawEthTxPacket API to send out Raw Ethernet data using Raw Ethernet sockets
RawEthTxPacketNC API to send out Raw Ethernet data using Raw Ethernet sockets without any copy
RawEthRxPacket API to handle raw Ethernet packets received by the stack

A.15.4 API Functions

RawEthTxPacket – Sends out Raw Ethernet data, creates a copy of the data.

int RawEthTxPacket (void* hRawEthSock,
                    char* pBuffer,
                    int   len);

Parameters

Return Value

Description This is the API called by the Raw Ethernet sockets module to transmit data sent by the application. This API allocates memory for the packet and the buffer according to the length specified, copies over the contents of the application buffer to the packet, and finally populates any socket priority configured on the socket to the PktPriority field of the packet for further use by the stack or the driver. It also increments the Raw Ethernet success stats if the transmit succeeded. This API invokes the NIMUSendPacket API to send out the packet eventually.

RawEthTxPacketNC – Sends out Raw Ethernet data, without any copy of the data.

int RawEthTxPacketNC (void *hRawEthSock,
                      char* pBuffer,
                      int len,
                      void *hPkt);

Parameters

Return Value

Description This is the API called by the Raw Ethernet sockets module to transmit data sent by the application without making any copy of it on the Tx path. This API is very useful for applications which have very definite performance requirements for their application. The buffers and packet handles passed here can be obtained in advance using the socket API getsendncbuff() by the application , then can be used to fill data and finally invoke the sendnc() API to transmit this data. The send no-copy API of the raw Ethernet socket module in turn invokes this API for finally transmitting the data. The raw Ethernet socket APIs are all discussed in detail in Section 3.4 of this document. This API validates the packet and buffer handles, and populates any socket priority configured on the socket to the PktPriority field of the packet for further use by the stack or the driver. It finally invokes the NIMUSendPacket API to send out the packet eventually. It also increments the Raw Ethernet success stats if the transmit succeeded. No memory allocations or copies are made by this API and hence is less intensive in terms of performance.

RawEthRxPacket – Receive handler for Raw Ethernet traffic in the stack.

int RawEthRxPacket (PBM_Handle hPkt);

Parameters

Return Value

Description This API is called by the NIMU layer when the Ethernet if a raw Ethernet socket object exists for the Ethernet type in the packet and if so enqueues in the socket buffer for the application to receive. No copies are made of the packet and the buffers further on the receive path by the Raw Ethernet socket layer and the packet is as is handed over for the application to use. This API also increments the Raw Ethernet receive stats accordingly.

A.16 Obtaining Stack Statistics

Stack statistics are available from global structures or global arrays exported by the stack library. The declaration of these global identifiers appears in the interface specification for the individual protocols. The following protocols contain statistics information:

Protocol Statistics Definition
IP IPIF.H
ICMP ICMPIF.H
TCP TCPIF.H
UDP UDPIF.H
Raw Transport (non-TCP/UDP) RAWIF.H
Network Address Translation NATIF.H
Raw Ethernet AWETHIF.H

B Network Address Translation

This section is required only for system programming that needs low level access to the Network Address Translation (NAT) layer. This API does not apply to sockets application programming.

This section describes functions that are callable from the kernel layer. You should be familiar with the operation of the operation of llEnter()/llExit() functions before attempting to use the APIs described in this section (see Section A.1.2).

NAT has a unique status in the stack software because it can be an integral part of programming at both the user and kernel levels, or can be entirely redundant and even purged from the stack build.

This section describes the operation of the Network Address Translation software included in the NDK, how to configure it, how to install port mappings, and how to program proxy filter routines to support protocols like FTP.

B.1 NAT Operation

NAT is a translation of packet IP address. It is used by the stack when routing, to translate the IP address of a packet to/from a private LAN from/to a public WAN. NAT is required when the IP address paradigms on either side of the router are incompatible; for example, virtual addresses vs. physical addresses, or private vs. public. In the case of a home LAN, NAT allows multiple clients on the home LAN to use a single ISP account by sharing the router WAN IP address obtained from the ISP.

B.1.1 Typical Configuration

For the examples that follow, consider the typical configuration illustrated in Figure B-1. The NDK is executing as a home router (HR) and connects the home LAN subnet (192.168.0.x) to the Internet (WAN) via an ISP that has assigned HR an address of 128.1.2.12. The hosts on the home network (H1 and H2) have obtained their internet addresses from HR via DHCP. The IP of HR on the home LAN as well as the IP subnet used by the home LAN is pre-configured in HR. Figure B-1 also shows a host on the public internet (IH) to which the LAN hosts will connect. Lastly, it is assumed that the home LAN subnet is virtual, and NAT is required to allow H1 and H2 to share the WAN IP address assigned to HR by the ISP (128.1.2.12).

Figure B-1 Basic Home Network Configuration

B.1.2 Basic NAT

When sharing a single WAN IP address, the IP address obtained from the ISP is assigned to the router (the NDK in routing mode). Client machines that are to share the IP address are placed on the home LAN. The router routes traffic between the LAN and the WAN (internet via the ISP).

As packets traverse from the LAN to the WAN across the router, the source IP address of the packet (a LAN address) is replaced with the public IP address of the router. The result is that all packets sent to the WAN appear to have originated from the router with the public IP address obtained from the ISP.

As packets traverse from the WAN to the LAN across the router, the destination IP address of the packet (the router’s WAN IP as obtained from the ISP) is replaced with the home LAN IP address of the physical client machine to which the packet is ultimately destined.

To perform this translation successfully, some details must be addressed. First, to allow multiple clients to share the public IP address in a non-ambiguous fashion, there must exist a deterministic method of mapping packets from the WAN to their correct destination on the LAN. This is done by keeping records of LAN IP clients that have initiated IP traffic, and by altering the TCP/UDP port (or ICMP Id field) as well as the IP address when performing the translation.

Every time a LAN client sends a packet to the WAN, the local IP address, port/id, and protocol is recorded for reverse mapping, as well as the destination IP address and port for security. When a packet is received from the WAN, the destination port/id is checked against the current database of NAT entries to see if the packet’s destination address and port/id should be translated to a LAN client.

For example, when accessing the Internet, all communication is normally initiated by the client. In this case, communication will be initiated by H1 or H2. Assume that H1 attempts to establish an HTTP connection with the Internet host (IH). It will send a connection request to the IP address assigned to IH, and a TCP port value of 80, which is HTTP. The request will be from its own IP address with an ephemeral port value that is picked from a pool (consider it random for these purposes- for example, 1001). So the request will be addressed as follows:

Packet 1

To From Protocol
64.1.1.100 : 80 192.168.0.32 : 1001 TCP

When the router HR receives this packet, it searches for a NAT entry that matches the From address of the packet. Because this is the first packet, assume the table is empty. When no entry is found, (skipping proxies for now) the router will create a new entry. It does this by recording information from packet 1, as well as picking a new port value from its own pool that has been specifically reserved for NAT (assume the range is 50000 to 55000, and that it chooses 50001). The new port is used as the packet’s source port. The NAT entry record would look like the following:

NAT Entry Table

Foreign IP Foreign Port Local IP Local Port Mapped Port IP Protocol TCP State Timeout
64.1.1.100 80 192.168.0.32 1001 50001 TCP SYNSENT 00:01:00

The Local IP and Local Port values are those that are local to hosts on the home LAN. The Foreign IP value is the foreign side of the connection as viewed by hosts on the home LAN. The Mapped Port value is the source port when the packet is sent from HR. The source IP address used in the packet is that assigned to HR by the ISP. The IP protocol of the packet is recorded, and when using TCP, the state of the TCP connection is tracked to establish a reasonable timeout value. The SYNSENT value indicates that a connection request was sent. Before a full connection is established, the timeout is set fairly low - for example, 1 minute.

As the packet is transmitted from HR to the ISP, it would look like the following:

Packet 1 (modified)

To From Protocol
64.1.1.100 : 80 128.1.2.12 : 50001 TCP

When IH receives the packet, it believes that the connection request came from HR. It thus sends the response packet to HR. The packet would be addressed as follows:

Packet 2 (response to packet 1)

To From Protocol
128.1.2.12 : 50001 64.1.1.100 : 80 TCP

When HR receives the packet, it checks the NAT entry table for an entry with a Mapped Port value equal to the destination port of the packet (in this case 50001). The value of Protocol and the source IP address/port values must also match the Protocol, Foreign IP, and Foreign Port fields of the NAT entry. This helps ensure that the reply is from the desired server.

Here, HR finds the entry and proceeds to modify the packet. It replaces the destination address/port with the local address/port stored in the entry. It also resets the timeout of the entry. After modification, the packet would be addressed as follows:

Packet 2 (modified)

To From Protocol
192.168.0.32 : 1001 64.1.1.100 : 80 TCP

Once a connection is established, the timeout of the entry is set high (for example, five hours), because TCP connections can stay connected for an indefinite period of time without exchanging any packets.

If H2 attempts to connect to the same host simultaneously, it can share the public IP address assigned to HR. For example, H2 sends a connection request to IH addressed as follows:

Packet 3

To From Protocol
64.1.1.100 : 80 192.168.0.33 : 1024 TCP

HR would not find a NAT entry for 192.168.0.33:1024, so it would create one:

NAT Entry Table

Foreign IP Foreign Port Local IP Local Port Mapped Port IP Protocol TCP State Timeout
64.1.1.100 80 192.168.0.33 1024 50002 TCP SYNSENT 00:01:00
64.1.1.100 80 192.168.0.32 1001 50001 TCP CONNECT 04:59:23

The modified packet and its reply would proceed similar to packets 1 and 2. Packets that pass from the LAN to the WAN are searched based on Local IP combined with Local Port. Packets that pass from the WAN to the LAN are searched based on Mapped Port. Note that for all entries on the NAT entry table, these values are unique.

B.1.3 NAT Port Mapping

So far, you have examined communication that has been initiated by hosts on the home LAN. Note that any unsolicited packets sent to HR from the WAN will not match any entry in the NAT table. These packets will be forwarded to the internal protocol stacks on HR, where they may or may not be used.

Now assume that a host on the home LAN (for example, H2) must place an HTTP server on the Internet. With what has been examined so far, it would be impossible to contact such a server from the WAN because no unsolicited traffic (like an HTTP connect request) can pass from the WAN to the LAN. However, H2 can acquire a portion of HR’s WAN presence by mapping one of the well-known port values on the public WAN IP address to itself through port mapping.

In port mapping, a NAT entry is created to send all traffic destined for a specific port on the public IP address to an alternate destination. For well-known ports like HTTP, the port value is not usually altered. Only the destination IP address changes. In this case, port 80 (HTTP) on the public IP address is mapped to port 80 of the LAN host H2. The entry would look as follows:

NAT Entry Table

Foreign IP Foreign Port Local IP Local Port Mapped Port IP Protocol TCP State Timeout
wild wild 192.168.0.32 80 80 TCP - STATIC

When a connection request arrives from a remote host for the public IP address assigned to HR, as with the basic NAT discussion of the previous section, the destination port of the packet is matched with the Mapped Port value of the NAT entry. Normally, the Foreign IP and Port of the NAT entry must also match for source IP and port of the packet, but here the values are wild. This is because when the entry is created, the foreign peer is unknown. Because, every TCP connection state must be tracked in its own NAT entry, a second entry must be spawned. Any match of a wild NAT entry will spawn a fully qualified entry. For example, assume the following packet arrives:

Packet 4

To From Protocol
128.1.2.12 : 80 64.1.1.100 : 2006 TCP

The resulting NAT entry table would be:

NAT Entry Table

Foreign IP Foreign Port Local IP Local Port Mapped Port IP Protocol TCP State Timeout
64.1.1.100 2006 192.168.0.32 80 80 TCP SYNSENT 00:01:00
wild wild 192.168.0.32 80 80 TCP - STATIC

The packet sent to the LAN by HR would be:

Packet 4 (modified)

To From Protocol
192.168.0.32 : 80 64.1.1.100 : 2006 TCP

Note that the wildcard entry’s timeout is STATIC. This means that the entry will never expire. Note that when the new entry is spawned, it acquires a timeout.

One last point to note here is that the installation of a port map for port 80 does not prohibit HR from running its own HTTP server hosted on its private LAN IP address (192.168.0.1). This means that local hosts could get to a local HTTP server on 192.168.0.1, and the public HTTP server on 192.168.0.32, but outside hosts connecting to 128.1.2.12 could only get to the public HTTP server on 192.168.0.32.

For example, assume the same topology as before, with the HR running both and HTTP and Telnet servers, H1 running an HTTP server, and H2 running a Telnet server. This is illustrated in Figure B-2.

Figure B-2 Public Servers on the Home Network

To make the servers on H1 and H2 public, the following NAT port mapping entries are installed on HR:

NAT Entry Table

Foreign IP Foreign Port Local IP Local Port Mapped Port IP Protocol TCP State Timeout
wild wild 192.168.0.33 23 23 TCP - STATIC
wild wild 192.168.0.32 80 80 TCP - STATIC

With these mappings, the externally available HTTP server and Telnet server publicly accessible on the WAN IP (128.1.2.12) are actually executing on H1 and H2. However, HR can have its own HTTP and Telnet servers and make them available to hosts on the LAN.

Also note that, regardless of how hosts on the LAN access HR (either through 192.168.0.1 or 128.1.2.12), their packets are not processed via NAT. Thus, they are never altered. The following are some connection examples:

Client Protocol Used Target Address Resulting Server Connection
IH HTTP 128.1.2.12 HTTP on H1
H2 HTTP 128.1.2.12 HTTP on HR
H2 HTTP 192.168.0.1 HTTP on HR
H2 HTTP 192.168.0.32 HTTP on H1
IH Telnet 128.1.2.12 Telnet on H2
H1 Telnet 128.1.2.12 Telnet on HR
H1 Telnet 192.168.0.1 Telnet on HR
H1 Telnet 192.168.0.33 Telnet on H2

B.1.4 NAT Proxy Filters

B.1.4.1 Problem Synopsis

Translating the IP destination address of a packet via NAT guarantees that all packets can be redirected to their correct physical destination, but it does not guarantee that the information will be understood by the recipient. Because one side of the connection always believes they are actually connected to a different IP address than their physical peer, there is a possibility that the application using the information will become confused. The confusion arises when there is information in the packet payload that is dependent on the IP address/port of the peer connection.

B.1.4.2 Problem Example - FTP Clients on the LAN

As a straightforward example of a situation that requires a proxy filter, consider FTP (file transfer protocol). FTP actually uses two ports to transmit a file. The first port establishes the control connection. Then, new ports establish the data connection to actually send the file. The FTP protocol allows an FTP client to specify its port for the data connection to the server. If no port is specified by the client, the client’s control port value is used.

The above scenario presents a couple problems for standard NAT. First, if NAT creates an entry for the FTP control connection, the entry could not be used for the data connection. As an example, H1 sends an FTP connection request to IH. The packet would be addressed as follows:

Packet 1

To From Protocol
64.1.1.100 : 21 192.168.0.32 : 1137 TCP

HR would not find a NAT entry for 192.168.0.33:1137, so it would create one:

NAT Entry Table

Foreign IP Foreign Port Local IP Local Port Mapped Port IP Protocol TCP State Timeout
64.1.1.100 21 192.168.0.32 1137 50003 TCP SYNSENT 00:01:00

The modified packet and its reply would proceed as discussed in Section B.1.2. The modified packet would be:

Packet 1 (modified)

To From Protocol
64.1.1.100 : 21 128.1.2.12 : 50003 TCP

Now assume that eventually the FTP server on IH attempts to establish a data connection back to what it thinks is the FTP client’s ephemeral port (50003). Note classic FTP uses port 20 to establish data connections. Its connection request packet would be:

Packet 2 (Data connection request)

To From Protocol
128.1.2.12 : 50003 64.1.1.100 : 20 TCP

Because there is no NAT entry record that will match the address values in this packet (specifically port 20 in the From field), this packet will not be forwarded to the FTP client. For this to work, there must be a port mapping installed for 64.1.1.100 that has a wildcard port value (it is not certain that the connection request will arrive on port 20). The NAT entry table would be as follows:

NAT Entry Table

Foreign IP Foreign Port Local IP Local Port Mapped Port IP Protocol TCP State Timeout
64.1.1.100 wild 192.168.0.32 1137 50003 TCP - STATIC
64.1.1.100 21 192.168.0.32 1137 50003 TCP CONNECT 04:58:39

With such a mapping, if a connection request from port 20 arrived, the wild card entry would be matched, and another entry spawned for port 20 on IH. The table would look as follows:

Foreign IP Foreign Port Local IP Local Port Mapped Port IP Protocol TCP State Timeout
64.1.1.100 20 192.168.0.32 1137 50003 TCP SYNSENT 00:01:00
64.1.1.100 wild 192.168.0.32 1137 50003 TCP - STATIC
64.1.1.100 21 192.168.0.32 1137 50003 TCP CONNECT 04:58:39

The second issue in dealing with an FTP client is that the client can change the port on which the FTP server attempts connection. This is done via a PORT command sent from the client to the server. The PORT command contains information about the client in the packet payload.

For example, assume the FTP client (H1) creates a new socket for the data connection, and its ephemeral port value is 1142. H1 would then send an FTP PORT command on the control connection to the server. The server would then attempt a connection. The following is an approximation of the operation (it is not the exact syntax of the port command).

Packet 3 (FTP Client H1 Sends Port Command for Port 1142)

To From Protocol Packet Payload
64.1.1.100 : 21 192.168.0.32 : 1137 TCP “PORT 192.168.0.32, 1142”

As a reminder, the FTP server would normally see the packet as:

Packet 3 (modified incorrectly)

To From Protocol Packet Payload
64.1.1.100 : 21 128.1.2.12 : 50003 TCP “PORT 192.168.0.32, 1142”

This packet creates a couple of problems. First, the IP address in the PORT command does not match the IP address of the FTP server’s connected peer. This would produce an error. Plus, the IP address in the PORT command is not a real Internet address. Lastly, even if the FTP server tried to connect to 128.1.2.12:1142, there is no mapping for the port number in the NAT entry table.

The correct procedure for modifying this packet is to solve all the above problems. First, a new NAT entry is created for 192.168.0.32:1142. The foreign IP address is left as a wildcard because as before, because it is not certain what port the FTP server will use. The NAT entry table would then look as follows:

NAT Entry Table

Foreign IP Foreign Port Local IP Local Port Mapped Port IP Protocol TCP State Timeout
64.1.1.100 wild 192.168.0.32 1142 50004 TCP - 00:02:00
64.1.1.100 wild 192.168.0.32 1137 50003 TCP - STATIC
64.1.1.100 21 192.168.0.32 1137 50003 TCP CONNECT 04:58:39

To review, note that you have the original NAT entry for the FTP control connection, and now two wildcard entries for possible FTP data connection requests.

The final step of the modification is to alter the payload of the packet so that the information in the PORT command matches the WAN IP address of HR (128.1.1.21) and the Mapped Port of the new NAT entry (50004). The correctly modified packet would be:

Packet 3 (modified correctly)

To From Protocol Packet Payload
64.1.1.100 : 21 128.1.2.12 : 50003 TCP “PORT 128.1.2.12, 50004”

It is also possible for a client to request the FTP server to create a new port (the PASV command), but that does not create any issues for FTP clients on the LAN. If the FTP server were on the LAN and the client on the WAN, the proxy process would key off the PASV command.

B.1.4.3 NDK Support for Proxy Filters

The modification procedure discussed above does have some multifaceted problems:

  1. The creation of the first data connection wildcard entry depends on the knowledge by some entity that an FTP control connection has occurred, and what IP/PORT the connection occurred on.
  2. The creation of the second data connection wildcard entry depends on the detection of a PORT command being passed from the client to the server.
  3. The modification of the data payload of the packet containing the PORT command requires that some entity is examining packet payloads.
  4. Modification of a TCP packet payload can permanently alter the values of the TCP sequence and acknowledge fields in the TCP header of all future packets on the control connection.

The first three problems are very specific to FTP, and the fourth problem (TCP sequence) is specific to any alteration of a TCP packet payload. Fortunately, the proxy filter support routines remove much of the burden of supporting these transformations.

The solution is twofold. First, the stack allows you to install proxy filter callback functions on specified TCP/UDP port values, either outgoing (for clients) or incoming (for servers). There are three callback functions involved.

The first callback function Enable is called when a new connection is attempted, or when the NAT entry expires. This function allows you to establish the basic connection state for the protocol in question. In the case of the FTP client example, the first wildcard data connection mapping would be installed here. Note that this function can also be used to filter connection requests. If this function returns zero, the connection request is ignored.

The second and third callback functions are mirrors of the other. They are the Tx and Rx functions. The Tx callback is called with the IP header of every packet that passes from the LAN to the WAN for the connection in question, while the Rx callback is called with the IP header of every packet that passes from the WAN to the LAN. While in these functions, the programmer can call a packet modify function to modify the payload of the packet. The system will automatically track and perform modifications to the TCP sequence values (when using TCP).

In the case of the FTP client, there would be no Rx callback because only packets from the client need to be examined. The Tx callback would look for PORT commands from the client, and when one was detected, it would install the second wildcard port mapping as discussed in the previous section, and then modify the packet payload so that the PORT command reflected the WAN IP of HR, and the Mapped Port of the NAT entry.

B.1.4.4 FTP Proxy Filter Example Code

From the discussion in this section, it would be easy to draw the conclusion that developing proxy filter code would be horribly complicated. However, the actual implementation is straightforward. The code to implement the filter discussed in Section B.1.4.3 is shown below. The API for NAT and Proxy is discussed in the following sections.

//
// GetVal - Convert ASCII decimal string to integer
//
static uint32_t GetVal( unsigned char **pData )
{
    uint32_t v = 0;
    while(**pData &gt;= '0' &amp;&amp; **pData &lt;= '9')
        v = v*10 + (*(*pData)++ - '0');
    (*pData)++; return(v);
}

//
// FTPCProxyEnable - Proxy for FTP Clients behind firewall
//
// NOTE: Proxy callback function operate at the kernel level. They
// may not make calls to user-level functions.
//
int FTPCProxyEnable( NATINFO *pin, uint32_t Enable )
{
    void *hNat;

    // Some implementations of FTP require the host to listen for
    // connections on the ephemeral port used to connect to the FTP
    // server. We create a STATIC mapping to handle this.
    if( Enable )
    {
        // Create it
        hNat = NatNew( pNI-&gt;IPLocal, pNI-&gt;PortLocal, pNI-&gt;IPForeign, 0,
                       IPPROTO_TCP, pNI-&gt;PortMapped, 0 );
        pNI-&gt;pUserData = hNat;
    }
    else
    {
        // Destroy it
        NatFree( pNI-&gt;pUserData );
    }
    return(1);
}

//
// FTPCProxyTx - Proxy for FTP Clients behind firewall
//
// NOTE: Proxy callback function operate at the kernel level. They
// may not make calls to user-level functions.
//
int FTPCProxyTx( NATINFO *pNI, IPHDR *pIpHdr )
{
    uint16_t    Length, Offset;
    TCPHDR    *pTcpHdr;
    unsigned char     *pData;
    void *hNAT;
    NATINFO   *pNINew;
    char      tmpstr[32];
    uint16_t    PortNew;
    uint32_t       IPNew;

    pData = (unsigned char*)pIpHdr;

    // Get pointer to TCP header
    Offset = (pIpHdr-&gt;VerLen &amp; 0xf) * 4;
    pTcpHdr = (TCPHDR *)(pData + Offset);

    // Get length of the IP payload
    Length = HNC16(pIpHdr-&gt;TotalLen) - Offset;

    // Get the offset into the TCP payload and payload size
    Offset += pTcpHdr-&gt;HdrLen &gt;&gt; 2;
    Length -= pTcpHdr-&gt;HdrLen &gt;&gt; 2;

    // Get pointer to TCP payload
    pData += Offset;

    //
    // For clients, we only care about sending PORT commands
    //
    // For example, if our client IP is 192.138.139.32, and it reports
    // port 384, the form of the command sent to the FTP server would
    // be: "PORT 192,138,139,32,1,128\r\n"
    //
    // We replace the Client IP with the router's IP, and the client
    // port with a NAT port which is mapped to the client port.
    //
    if(!strncmp( pData, "PORT ", 5) )
    {
        // Get the IP/Port declared by sender
        // Form is "i1,i2,i3,i4,p1,p2"
        pData += 5;
        IPNew = ((uint32_t)GetVal (&amp;pDada)) &lt;&lt; 24;
        IPNew |= ((uint32_t)GetVal (&amp;pDada)) &lt;&lt; 16;
        IPNew |= ((uint32_t)GetVal (&amp;pDada)) &lt;&lt; 8;
        IPNew |= ((uint32_t)GetVal (&amp;pData));
        IPNew = htonl(IPNew);
        PortNew = GetVal(&amp;pData);
        PortNew = (PortNew&lt;&lt;8) + GetVal (&amp;pData);

        // Add a NAT mapping to client's IP and Port
        hNAT = NatNew(IPNew, PortNew, pNI-&gt;IPForeign, 0, IPPROTO_TCP,
                      0, NAT_IDLE_SECONDS);
        if(!hNAT)
            return(0);

        // Get Server IP and Mapped Port
        IPNew = htonl( NatIpServer );
        pNINew = NatGetPNI( hNAT );
        PortNew = pNINew-&gt;PortMapped;

        // Print a replacement string with IP and Port
        sprintf(tmpstr, "%u,%u,%u,%u,%u,%u\r\n",
                ((uint)(IPNew &gt;&gt; 24)), ((uint)(IPNew &gt;&gt; 16)&amp;0xFF),
                ((uint)(IPNew &gt;&gt; 8)&amp;0xFF), ((uint)(IPNew)&amp;0xFF),
                PortNew&gt;&gt;8, PortNew&amp;0xFF);

        // Replace the original string with ours
        ProxyPacketMod( Offset+5, Length-5, strlen(tmpstr), tmpstr );
    }
    return(1);
}

B.2 NAT Port Mapping

NAT port mapping allows a client machine on the LAN (or home network) to appear on a specific port of the router’s public WAN IP address. This API (and NAT in general) is only used when the NDK is acting as an IP router, and when the IP network on one side of the router is using virtual IP addresses.

The functions described in this section illustrates how to install and remove port mappings. The functional operation of NAT and NAT Port Mapping is discussed in more detail in Section B.1.

B.2.1 Function Overview

The following functions create and destroy port mappings:

Function Description
NatNew() Create a new NAT entry (for port mapping)
NatFree() Free a NAT entry
NatGetPNI() Get a pointer to a NAT entry’s NATINFO structure

B.2.2 NAT Entry Information Structure

A port mapping is just a NAT entry. Each NAT entry has its own information structure. This NATINFO structure allows you to examine the status of a particular entry.

The specification of the NATINFO structure is as follows:

typedef struct _natinfo {
        uint32_t TcpState;          // Current TCP State (Simplified)
#define NI_TCP_CLOSED  0            // Closed or closing
#define NI_TCP_SYNSENT 1            // Connecting
#define NI_TCP_ESTAB   2            // Established
        uint32_t      IPLocal;      // Translated IP Address
        uint16_t      PortLocal;    // Translated TCP/UDP Port
        uint32_t      IPForeign;    // IP Address of Foreign Peer
        uint16_t      PortForeign;  // Port of Foreign Peer
        unsigned char Protocol;     // IP Protocol
        uint16_t      PortMapped;   // Locally Mapped TCP/UDP Port (router)
        void          *hProxyEntry; // Handle to Proxy Entry (if any)
        uint32_t      Timeout;      // Expiration time in SECONDS
        void          *pUserData;   // Pointer to proxy callback data
} NATINFO;

The individual fields are defined as follows:

The NAT information structure is of little importance when only port mapping is required. It is mostly for use in NAT proxy filters.

B.2.3 NAT API Functions

NatNew – Create a NAT Entry (for Port Mapping)

void *NatNew( uint32_t      IPLocal,
              uint16_t      PortLocal,
              uint32_t      IPForeign,
              uint16_t      PortForeign,
              unsigned char Protocol,
              uint16_t      PortMapped,
              uint32_t      Timeout );

Parameters

Return Value Handle to NAT entry, or NULL on error.

Description This function creates a NAT entry with the parameters as specified.

For example, to allow a host on a virtual IP address of 1.2.3.4 to run a Telnet server reachable via the router’s public (physical) IP address, a mapping would be installed to map TCP port 23 (telnet) to 1.2.3.4:23. If the connection were to be open to all foreign hosts, then IPForeign and PortForeign would be left NULL. The value of Timeout would also be NULL to make the entry STATIC.

hNatTelnet = NatNew( htonl(0x01020304), 23, 0, 0, IPPROTO_TCP, 23, 0 );

The function returns a handle to the NAT entry created. This handle should be freed with NatFree() when the mapping is no longer desired.

NatFree – Destroy a NAT Entry

void NatFree( void *hNat );

Parameters

Return Value None.

Description This function frees the supplied NAT entry. It is called to remove a STATIC NAT entry that is no longer required.

NatGetPNI – Get a Pointer to a NAT Entry’s NATINFO Structure

NATINFO NatGetPNI( void *hNat );

Parameters

Return Value Pointer to NATINFO structure or NULL on error.

Description This function returns a pointer to the NATINFO structure defined in Section B.2.2. It is used mainly by NAT proxy filter callback functions.

B.3 NAT Proxy Filters

NAT proxy filters allow NAT to operate correctly with network protocols that have addressing specific data in their packet payload data. This API (and NAT in general) is only used when the NDK is acting as an IP router, and when the IP network on one side of the router is using virtual IP addresses.

The functions described in this section illustrate how to install and remove port proxy filters and their associated callback functions. The functional operation of NAT and NAT Port Mapping, and NAT Proxy is discussed in more detail in Section B.2.2.

B.3.1 Function Overview

The following functions create and destroy proxy filters:

Function Description
ProxyNew() Create Proxy Filter for NAT entries
ProxyFree() Destroy a Proxy Filter declaration

The following function can be called from within a proxy filter callback function:

Function Description
ProxyPacketMod() Modify a packet being processed by NAT

B.3.2 NAT Proxy Filter Callback Functions

The proxy filter callback functions allow the proxy programmer to examine NAT entry properties as the entries are created, plus the examination of packet data as packets pass between the LAN and WAN. This section describes the syntax of the callback functions that are supplied to the proxy filter when it is first installed in the system.

ProxyEnableCallback – Proxy Enable Callback Function

int SampleProxyEnableCallback( NATINFO  *pNI,
                               uint32_t EnableFlag );

Parameters

Return Value 1 to allow normal operation, or NULL to abort new NAT entry.

Description This function is called when a NAT entry containing a proxy is created or destroyed. When the entry is created, the value of EnableFlag is 1. When the entry is being destroyed, the value of EnableFlag is zero.

When EnableFlag is set, the return value of this function determines if the NAT entry will be enabled. If this function returns NULL, the NAT entry is immediately destroyed (in this event, the callback is not called a second time for this destroy). This can be used to restrict peer connections.

ProxyTx/RxCallback – Proxy Tx/Rx Callback Functions

int SampleProxyTxCallback( NATINFO *pNI,
                           IPHDR   *pIpHdr );

int SampleProxyRxCallback( NATINFO *pNI,
                           IPHDR   *pIpHdr );

Parameters

Return Value 1 to allow normal operation, or NULL to abort the supplied packet.

Description This function is called when a packet is crossing the router from the WAN to the LAN (Rx callback) or from the LAN to the WAN (Tx callback). The NAT entry containing a proxy that matches the packet is described by the supplied NATINFO structure. This structure was described in Section B.2.2.

The purpose of the callback is to examine the packet and take appropriate action based on its contents. The packet payload can be easily modified by the ProxyPacketMod() function described later in this section. The translation of the IP address and port information cannot be altered by this callback; however, the callback can act as a packet filter and discard unwanted packets by returning a value of NULL.

B.3.3 NAT Proxy API Functions

ProxyNew – Create a New Proxy Filter for NAT Entries

void *ProxyNew( uint32_t      NatMode,
                unsigned char Protocol,
                uint16_t      Port,
                uint32_t      IPTarget,
                int (*pfnEnableCb)(NATINFO *, uint),
                int (*pfnTxCb)(NATINFO *, IPHDR *),
                int (*pfnRxCb)(NATINFO *, IPHDR *) );

Parameters

Return Value Handle to new proxy, or NULL on error.

Description This function creates a hook that is examined whenever a new NAT entry is created.

The calling parameter NatMode specifies the direction of the proxy (NAT_MODE_RX for servers behind the firewall, and NAT_MODE_TX for clients behind the firewall).

The Protocol and Port values are the IP protocol and well-known port of the protocol to proxy.

For example, if setting up a FTP client proxy, set:

NatMode = NAT_MODE_TX
Protocol = IPPROTO_TCP
Port = 21

IPTarget is used only in server proxies (when NatMode is set to NAT_MODE_RX). This specifies the machine behind the firewall that is actually providing the service.

The three pointers to callback functions correspond to the proxy filter callback functions described in the previous section.

The function returns a handle to the new proxy. Note that a proxy handle is not the same as (or compatible with) a NAT entry handle.

The proxy should be destroyed by calling ProxyFree() when it is no longer needed.

ProxyFree – Destroy a Proxy Filter Declaration

void ProxyFree( void *hProxy );

Parameters

Return Value None.

Description This function frees the supplied Proxy Filter entry. It is called to remove an entry that is no longer required.

ProxyPacketMod – Modify the Contents of a Packet

IPHDR *ProxyPacketMod( uint32_t      Offset,
                       uint32_t      OldSize,
                       uint32_t      NewSize,
                       unsigned char *pNewData );

Parameters

Return Value Pointer to new IP header of packet. This pointer is used for further modifications (if needed).

Description This function may only be called from a proxy filter callback function. Its purpose is to modify the contents of a TCP or UDP packet, and perform the necessary adjustments for packet size - including TCP sequencing adjustment.

C Point-to-Point Protocol

Point to point protocol (PPP) was originally designed as a replacement for SLIP (serial line IP) in sending IP packets via a serial line. In addition to its massive popularity in performing this function, PPP has also been increasingly used for the transmission of packets over other media. This is due to PPP’s inherent peer-to-peer nature, allowing for per-connection security and billing.

The NDK has built-in support for both PPP servers and clients. The PPP support API is designed to be shared by one or more physical devices. One obvious device that can be hooked to PPP is a serial line, but the stack also contains support for PPP over Ethernet (PPPoE). The low level PPP API as well as Serial HDLC and PPPoE are all discussed in this appendix.

C.1 Low Level PPP Support

This section describes the operation of the PPP support API included in the NDK.

NOTE: Unlike the HDLC and PPPoE APIs that are application callable, the low level PPP support API is designed to be called from the kernel layer only. You should be thoroughly familiar with the operation of the kernel and the llEnter()/llExit() functions before attempting to use the APIs described in this section.

C.1.1 PPP Operation

PPP is very much like Ethernet in that there is a defined packet format. The basic PPP packet is shown below. It consists of flag delimiters, address and control bytes, protocol field (similar to ether-type under Ethernet), and a two byte checksum.

Figure C-1 Standard PPP Frame Over Serial Line

Flag (7E) Addr (FF) Control (03) Protocol Payload CRC Flag (7E)
1 1 1 2 1500 2 1

To abstract out the actual processing of the PPP data from the processing of the PPP frame encoding, the PPP support included in the NDK expects a smaller frame, consisting of the protocol and payload fields only. This format is shown in Figure C-2.

Figure C-2 PPP Frame Processed by PPP API

Protocol Payload
2 Size specified by layer 2 (about 1500)

The abstraction of PPP from the layer 2 encoding allows PPP to be carried by a variety of physical devices. The programming interface to the PPP layer called by the application is actually exposed by the layer 2 encoder. This layer 2 encoder is referred to as a serial interface (SI), but does not have to be a serial port. This interoperation between PPP and the SI is shown in Figure C-3. The functions shown in the dotted rectangle are those provided by the serial interface software.

Figure C-3 Serial Interface (SI) Abstraction

As shown in Figure C-3, the SI interface has the responsibility of providing for connection control, a timer used by PPP for timeout, packet encoding and decoding, and a SI callback function for status messages and packet transmission. Note that the SI driver developer also defines the actual API used by the application software to establish and tear down PPP connection sessions. There is no specific requirements in specifying the session API for any particular PPP device, but the APIs defined for HDLC and PPPoE can be used as a guide.

C.1.2 Function Overview

The SI interface module is charged with communicating with both the hardware and the application program, but the PPP packets themselves are processed via the PPP support functions in the stack. The PPP support software provides the following functions for use by the SI module:

Function Description
pppNew() Create a new PPP connection instance
pppFree() Destroy an existing PPP connection instance
pppTimer() Inform PPP that a 1 second timer tick has expired
pppInput() Pass in a received PPP packet for processing

The formal declaration of these functions appear later in this section (see Section C.1.6).

NOTE: These functions can only be called in kernel mode. See Appendix A for programming in kernel mode.

C.1.3 Supported Protocols

In keeping with trying to maintain a small footprint, the PPP software supports a subset of the general PPP protocols. The following are supported:

C.1.4 SI Module Callback Function

The PPP support API is used for connection instance creation and destruction, and to pass received packets to the stack. To get information about PPP back from the stack, and to allow the stack to request the transmission of PPP packets, the SI module supplies a callback function. A pointer to this callback is passed to PPP as a parameter to pppNew().

NOTE: This function is called in kernel mode. See Appendix A for programming in kernel mode.

C.1.4.1 Function Declaration

The SI callback function is provided in the SI code module using the following definition:

SIControl – Notify the Serial Interface of a Change in Status, or when SI Needs to Transmit a Packet

void SIControl( void       *hSI,
                uint32_t   Message,
                uint32_t   Data,
                PBM_Handle hPkt );

Parameters

Return Value None.

Description This function is called when a PPP needs to notify the serial interface (SI) of a change in status, or when it needs SI to transmit a packet.

The hSI parameter is a handle (pointer to a void) that is originally passed to PPP via pppNew(). This value allows the SI module to know which of its own connection instances is in use. The PPP instance handle in use is not supplied, but rather should be obtained by reference from the supplied SI handle. If the programmer of the SI module does not wish to track handles, then this parameter may be NULL (always as originally supplied to pppNew()). This is NOT the handle to the PPP instance that is passed to other functions in the PPP API.

The purpose of the callback is determined by the value of the Message parameter. The following message values are defined for this parameter:

C.1.4.2 SI_MSG_CALLSTATUS Message

When this message value is set, the callback function was called by PPP to update the status of the connection instance. When the callback is called with this message, the value of Data contains additional information about the call. Data can be set to any of the following values:

If Data is set to any of disconnect messages, pppFree() should be called to destroy the connection instance. For all other status values, no action is required.

NOTE: It is always safe to assume that when the value of Data >= SI_CSTATUS_DISCONNECT, the message is some type of disconnect.

C.1.4.3 SI_MSG_ SENDPACKET Message

When this message value is set, the callback function was called by PPP to transmit a packet. The Data parameter is set to the 16 bit PPP protocol of the packet, and the hPkt parameter contains a handle to a packet (PKT) object that contains the packet payload. It is the job of the SI callback function to encode the packet and transmit it on the physical hardware.

C.1.4.4 SI_MSG_ PEERCMAP Message

Serial interfaces to PPP require a translation map for the first 32 character values. This map informs the packet encoded which characters must be escaped and which do not. The default value of the peer CMAP should be 0xffffffff, and updated only when this message is received. Whether or not PPP will attempt to exchange CMAP information with its peer, is determined by passing flags to pppNew() when the connection instance is created.

C.1.4.5 Example Callback Function Implementation

The following is an example of a SI module callback function from the HDLC module code in the example applications. The code illustrates the basic processing that must be done for the various SI callback messages. The function calls made in this example are described in Appendix A.

//--------------------------------------------------------------------
// SI Control Function
//--------------------------------------------------------------------
void hdlcSI( void *hSI, uint32_t Msg, uint32_t Aux, PBM_Handle hPkt )
{
    HDLC_INSTANCE  *pi = (HDLC_INSTANCE *)hSI;
    void *hTmp;
    uint32_t Offset,Size;
    unsigned char          *pBuf;

    switch(Msg)
    {
    case SI_MSG_CALLSTATUS:
        // Update Connection Status
        pi->Status = (uint)Aux;
        if( Aux >= SI_CSTATUS_DISCONNECT )
        {
            // Close PPP
            if( pi->hPPP )
            {
                hTmp = pi->hPPP;
                pi->hPPP = 0;
                pppFree( hTmp );
            }
        }
        break;

    case SI_MSG_PEERCMAP:
        // Update Out CMAP for Transmit
        pi->cmap_out = Aux;
        llSerialHDLCPeerMap( pi->DevSerial, Aux );
        break;

    case SI_MSG_SENDPACKET:
        if( !hPkt )
        {
            DbgPrintf( DBG_ERROR,"hdlcSI: No packet" );
            break;
        }

        Offset = PBM_getDataOffset( hPkt );
        Size = PBM_getValidLen( hPkt );

        // Make sure packet is valid, with room for protocol, room for checksum
        if((Offset<4) || ((Offset+Size+2)>PBM_getBufferLen(hPkt)))
        {
            DbgPrintf( DBG_ERROR,"hdlcSI: Bad packet" );
            PBM_free( hPkt );
            break;
        }

        // Add in 2 byte Protocol and 2 byte header. Also add in size for
        // 2 byte checksum. Note that the outgoing checksum is corrected
        // (calculated) by the serial driver.
        Offset -= 4;
        Size += 6;
        PBM_setDataOffset(hPkt, Offset);
        PBM_setValidLen(hPkt, Size);
        pBuf = PBM_getDataBuffer(hPkt)+Offset;
        *pBuf++ = 0xFF;
        *pBuf++ = 0x03;
        *pBuf++ = (unsigned char)(Aux/256);
        *pBuf = (unsigned char)(Aux%256);

        // Send the buffer to the serial driver
        llSerialSendPkt(pi->DevSerial, hPkt);
        break;
    }
}

C.1.5 Tips for Implementing a PPP Serial Interface (SI) Module Instance

C.1.5.1 Multiple Instances

PPP supports multiple instances, but the SI module implementation tracks multiple instances of itself. This is done in two ways. One method is for the SI module to have a locally global head pointer to its first instance, and an array or linked list for additional instances. Or, the instance can be bound to the next layer down. In the case of the HDLC module, one PPP instance is bound to one serial port driver instance. So the HDLC module does not need to track instances independently.

When a new PPP connection is established, a new SI module instance should be allocated and a handle to the new SI instance is passed to the pppNew() function. The handle that pppNew() returns must be associated with the handle to the SI instance. The PPP handle must be passed to all other PPP API functions, and PPP will pass back the SI instance handle to the SI callback function.

When new data arrives from the hardware, it is the responsibility of the SI module to associate that data with a specific SI instance. The SI instance can then be accessed to retrieve the handle to the PPP instance to use with any PPP function calls. In the case of HDLC, the SI instance is known because it is associated with a particular serial device instance.

C.1.5.2 Using the Timer Object

PPP requires that its pppTimer() function be called once every second. This can be PRD driven if necessary, but the timer callback cannot be called from a PRD because it must be called from within kernel mode (an llEnter()/llExit()) pairing.

C.1.5.3 Registering Packet Padding Requirements

Although a serial interface will probably not have any special requirements for packets from the stack, it must at least be able to construct valid packets to send to the pppInput() function. For a serial interface that does not use the packet buffer to physically send the packet, the size of the PPP header would be 4 bytes (2 byte HDLC header and 2 byte protocol field), and the padding would be 2 bytes (checksum).

C.1.6 PPP API Functions

The following is the full description of the PPP functions described in this section.

pppNew – Create a New PPP Connection Instance

void *pppNew( void     *hSI,
              uint32_t pppFlags,
              uint32_t mru,
              uint32_t IPServer,
              uint32_t IPMask,
              uint32_t IPClient,
              char     *Username,
              char     *Password,
              uint32_t cmap,
              void (*pfnSICtrl)(void *, uint, uint32_t, void *) );

Parameters

Return Value Handle to new PPP connection instance, or NULL on error.

Description This function is called to create a new PPP connection instance. The type of connection created is determined by the calling parameters.

When run in SERVER mode, the name of the PPP server defaults to DSPIP in CHAP authentication; however, this can be changed by using the CFGITEM_SYSINFO_REALMPPP configuration tag. For example:

>// Name our authentication group for PPP (Max size = 31)
// This is the authentication "realm" name returned by the PPP
// server when authentication is required.
// (Note the length "16" includes the NULL terminator)

CfgAddEntry( hCfg, CFGTAG_SYSINFO, CFGITEM_SYSINFO_REALMPPP,
    0, 16, (unsigned char *)"PPP_SAMPLE_NAME", 0 );

When successful, this function returns a handle to a new PPP instance. This handle is used by the caller when calling other functions in the PPP API.

pppFree – Destroy PPP Connection Instance

void pppFree( void *hPPP );

Parameters

Return Value None.

Description This function is called to close and destroy a PPP connection instance created with pppNew(). This function must be called to free the PPP handle, even if the PPP connection itself is already disconnected.

pppInput – Send a PPP Packet to PPP for Processing

void pppInput( void    *hPPP,
               PBM_Pkt *pPkt );

Parameters

Return Value None.

Description This function is called when a PPP packet is received on an active serial interface. The packet is data decoded into the PPP protocol and payload fields, and given to PPP as a packet object. The handle hPPP is the PPP connection instance returned from pppNew() for this connection, and pPkt is a packet object created by the packet buffer manager (PBM).

pppTimer – Notify PPP of One Second Tick

void pppTimer( void *hPPP );

Parameters

Return Value None.

Description This function is called on an active PPP instance to notify PPP that one second has elapsed. Because the PPP API is entirely stateless, it relies on the serial interface for time tick notification.

C.2 Serial HDLC Client and Server Support

This implementation of HDLC for the NDK library is included in the example applications. It interfaces to the serial port driver described in the HAL.

NOTE: The HDLC API is user-callable. Unlike the low level PPP support API, you should not use the llEnter()/llExit() functions when calling the functions described in this section.

C.2.1 Function Overview

Called by Application:

Function Description
hdlcNew() Create a Serial HDLC Client Session
hdlcFree() Destroy a Serial HDLC Client Session
hdlcGetStatus() Get the Call Status of a Serial HDLC Client Session
hdlcsNew() Create a Serial HDLC Server Session
hdlcsFree() Destroy a Serial HDLC Server Session
hdlcsGetStatus() Get the Call Status of a Server HDLC Client Session

Called by Serial Port Driver:

Function Description
hdlcInput() Send HDLC input buffer for processing

C.2.2 HDLC API Functions

hdlcNew – Create a Serial HDLC Client Session

void *hdlcNew( uint32_t Dev,
               uint32_t pppFlags,
               uint32_t cmap,
               char     *Username,
               char     *Password );

Parameters

Return Value If it succeeds, the function returns a handle to a HDLC client instance. Otherwise, it returns NULL.

Description This function is called to create a new serial HDLC client instance on the physical serial interface specified by the index Dev.

When successful, this function returns a handle to a new serial HDLC instance. The current status of the connection can be queried at any time by calling hdlcGetStatus().

hdlcFree – Destroy a Serial HDLC Client Session

void hdlcFree( void *hHDLC );

Parameters

Return Value None.

Description This function is called to close and destroy a serial HDLC client session that was created with hdlcNew(). This function is always called once for every HDLC instance handle. If the connection is no longer active, it frees the instance memory. If the connection is still active, it disconnects the call first.

hdlcGetStatus – Get the Status of a Serial HDLC Client Session

uint32_t hdlcGetStatus( void *hHDLC );

Parameters

Return Value This function returns a uint32_t that will be set to one of the following values:

Description This function is called to get the connection status of a serial HDLC client session using the HDLC instance handle returned from hdlcNew(). This function can be called any time after the handle is created with hdlcNew(), and before it is destroyed with hdlcFree().

hdlcsNew – Create a Serial HDLC Server Session

void *hdlcsNew( uint32_t Dev,
                uint32_t pppFlags,
                uint32_t cmap,
                uint32_t IPServer,
                uint32_t IPMask,
                uint32_t IPClient );

Parameters

Return Value If it succeeds, the function returns a handle to a serial HDLC server instance. Otherwise, it returns NULL.

Description This function is called to create a new serial HDLC server instance on the physical serial interface specified by the index Dev.

When successful, this function returns a handle to a new serial HDLC server instance. The current status of the connection can be queried at any time by calling hdlcsGetStatus().

The name of the PPP server defaults to DSPIP in CHAP authentication; however, this can be changed by using the CFGITEM_SYSINFO_REALMPPP configuration tag. For example:

// Name our authentication group for PPP (Max size = 31)
// This is the authentication "realm" name returned by the PPP
// server when authentication is required.
// (Note the length "16" includes the NULL terminator)

CfgAddEntry( hCfg, CFGTAG_SYSINFO, CFGITEM_SYSINFO_REALMPPP,
             0, 16, (unsigned char *)"PPP_SAMPLE_NAME", 0 );

hdlcsFree – Destroy a Serial HDLC Server Session

void hdlcsFree( void *hHDLC );

Parameters

Return Value None.

Description This function is called to close and destroy a serial HDLC server session that was created with hdlcsNew(). This function is always called once for every HDLC instance handle. If the connection is no longer active, it frees the instance memory. If the connection is still active, it disconnects the call first.

hdlcsGetStatus – Get the Status of a Serial HDLC Server Session

uint32_t hdlcsGetStatus( void *hHDLC );

Parameters

Return Value This function returns a uint32_t that will be set to one of the following values:

Description This function is called to get the connection status of a serial HDLC server session using the HDLC instance handle returned from hdlcsNew(). This function can be called any time after the handle is created with hdlcsNew(), and before it is destroyed with hdlcsFree().

C.3 PPPoE Client and Server Support

The PPPoE (PPP over Ethernet) specification allows for PPP packets to be transmitted in a peer to peer method over an Ethernet tunnel. The standard has gained in popularity because it allows for the use of multiple user accounts on a single Ethernet network.

The implementation of PPPoE supplied in the NDK library is built into the stack library code, and linked to the Ether object that handles packets from all Ethernet devices in the HAL layer. Thus, is it not necessary to access or alter the HAL to use PPPoE.

The software can be used as a PPP server or PPP client, but not both simultaneously. In both cases, PPPoE uses the PPP programming interfaces described earlier in this section. Thus, for server mode, the PPP server will use the same user account information as a serial based server.

NOTE: The PPPoE API is user callable. Unlike the low level PPP support API, you should not use the llEnter()/llExit() functions when calling the functions described in this section.

C.3.1 Function Overview

The PPPoE function API is short:

Function Description
pppoeNew() Create a PPPoE Client Session
pppoeFree() Destroy a PPPoE Client Session
pppoeGetStatus() Get the Call Status of a PPPoE Client Session
pppoesNew() Create a PPPoE Server Session
pppoesFree() Terminate a PPPoE Server Session

C.3.2 PPPoE API Functions

pppoeNew – Create a PPPoE Client Session

void *pppoeNew( void     *hEther,
                uint32_t pppFlags,
                char     *Username,
                char     *Password );

Parameters

Return Value If it succeeds, the function returns a handle to a PPPoE client instance. Otherwise, it returns NULL.

Description This function is called to create a new PPPoE client instance on the Ether type interface specified by the handle hEther.

When successful, this function returns a handle to a new PPPoE instance The current status of the PPPoE connection can be queried at any time by calling pppoeGetStatus().

pppoeFree – Destroy a PPPoE Client Session

void pppoeFree( void *hPPPOE );

Parameters

Return Value None.

Description This function is called to close and destroy a PPPoE client session that was created with pppoeNew(). This function is always called once for every PPPoE instance handle. If the connection is no longer active, it frees the instance memory. If the connection is still active, it first disconnects the call.

pppoeGetStatus – Get the Status of a PPPoE Client Session

uint32_t pppoeGetStatus( void *hPPPOE );

Parameters

Return Value This function returns a uint32_t that will be set to one of the following values:

Description This function is called to get the connection status of a PPPoE client session using the PPPoE instance handle returned from pppoeNew(). This function can be called any time after the handle is created with pppoeNew(), and before it is destroyed with pppoeFree().

pppoesNew – Create a PPPoE Server Session

void *pppoesNew( void     *hEther,
                 uint32_t pppFlags,
                 uint32_t SessionMax,
                 uint32_t IPServer,
                 uint32_t IPMask,
                 uint32_t IPClientBase,
                 char     *ServerName,
                 char     *ServiceName );

Parameters

Return Value If it succeeds, the function returns a handle to a PPPoE server instance. Otherwise, it returns NULL.

Description This function is called to create a new PPPoE server instance on the Ether type interface specified by the handle hEther.

The name of the PPP server defaults to DSPIP in CHAP authentication. This is independent of the PPPoE server name. However, the name can be changed by using the CFGITEM_SYSINFO_REALMPPP configuration tag. For example:

// Name our authentication group for PPP (Max size = 31)
// This is the authentication "realm" name returned by the PPP
// server when authentication is required.
// (Note the length "16" includes the NULL terminator)

CfgAddEntry( hCfg, CFGTAG_SYSINFO, CFGITEM_SYSINFO_REALMPPP,
             0, 16, (unsigned char *)"PPP_SAMPLE_NAME", 0 );

When successful, this function returns a handle to a new PPPoE server instance. The status of individual connections is not available to the caller, but tracked automatically by PPPoE. When sessions are added or destroyed, the IP address callback supplied to NC_NetStart() is called and connections can be tracked by the applications programmer via this function callback.

pppoesFree – Destroy a PPPoE Server Session

void pppoesFree( void *hPPPOES );

Parameters

Return Value None.

Description This function is called to close and destroy a PPPoE server session that was created with pppoesNew(). This function is always called once to shut down the PPPoE server. Any external client currently connected to the server is disconnected.

C.4 Creating PPP Server User Accounts

To use the PPP or PPPoE protocol in server mode, it advisable to protect access to the system through the use of a PPP authentication protocol. The PPP supplied in the stack library allows for the use of either PAP or CHAP in user authentication. The database of authorized users (name and password) is stored in the configuration system.

C.4.1 Adding and Reviewing User Accounts

The definition of the user account entry in the configuration system is defined in Section 4.3.6. Note in that section that the server channel flags PPPFLG_CH1 through PPPFLG_CH4 are duplicated in both the server flags and the client account flags. This allows the system programmer to allow different classes of services for different channels.

The methodology of adding, querying, and removing user accounts is the same for any other tag in the configuration system. Some simple examples follow. More example code can be found in the sample console program.

C.4.1.1 Adding a PPP User Account

The following code adds a PPP user account for the user supplied in name with a password supplied in password. Note that it also uses the AcctFind() function to verify that the account does not already exist.

void AcctAdd(char *name, char *password)
{
    CI_ACCT CA;
    void *hAcct;
    int rc;

    // Check string lengths for name and password
    if(strlen(name) >= CFG_ACCTSTR_MAX ||
        strlen(password) >= CFG_ACCTSTR_MAX)
    {
        printf("Name or password too long, %d character max\n\n",
                CFG_ACCTSTR_MAX-1);
        return;
    }

    // See if the account already exists
    hAcct = AcctFind(tok2);
    if(hAcct)
    {
        printf("Account exits - remove old account first\n\n");

        // We must de-reference the account we found
        CfgEntryDeRef(hAcct);
        return;
    }

    // Fill in the CA record
    strcpy(CA.Username, name);
    strcpy(CA.Password, password);

    // Give user access to all channels
    CA.Flags =
        CFG_ACCTFLG_CH1|CFG_ACCTFLG_CH2|CFG_ACCTFLG_CH3|CFG_ACCTFLG_CH4;

    // Add it to the configuration
    rc = CfgAddEntry(0, CFGTAG_ACCT, CFGITEM_ACCT_PPP,
                     CFG_ADDMODE_NOSAVE, sizeof(CI_ACCT), (unsigned char *)&CA, 0);

    if(rc < 0)
        printf("Error adding account\n");
    else
        printf("Account added\n");
    return;
}

C.4.1.2 Searching for a PPP User Account

The following code implements the AcctFind() function called in the previous example. Note that the same method could be used to print out a list of all accounts.

void *AcctFind(char *name)
{
    void *hAcct;
    CI_ACCT CA;
    int rc;
    int size;

    // Get the first user account
    rc = CfgGetEntry(0, CFGTAG_ACCT, CFGITEM_ACCT_PPP, 1, &hAcct);

    // If there are no accounts, then we did not find it
    if(rc <= 0)
        return(0);

    // Search until we run out of accounts or have a match
    while(1)
    {
        // Get the data for this entry into CA
        size = sizeof(CA);
        rc = CfgEntryGetData(hAcct, &size, (unsigned char *)&CA);
        if(rc <= 0)
        {
            // This is an unexpected error - deref the handle and abort
            CfgEntryDeRef(hAcct);
            return(0);
        }

    // See if the username matches the search name. If so, return
    // the referenced handle
    if(!strcmp(name, CA.Username))
        return(hAcct);

    // Since we did not match, get the next entry. If there is no
    // next entry, we are done searching.
    rc = CfgGetNextEntry(0, hAcct, &hAcct);
    if(rc <= 0)
        return(0);
    }
}

C.4.1.3 Removing a PPP User Account

Removing a specific user account is done by finding the account and removing the entry handle.

The following uses the AcctFind() function to find the target account.

void AcctDelete(char *name)
{
    void *hAcct;

    // Find the account to delete
    hAcct = AcctFind(name);

    // If we found the account, remove it
    if(hAcct)
    {
        CfgRemoveEntry(0, hAcct);
        printf("Account removed\n");
    }
}

D Hardware Adaptation Layer (HAL)

As discussed in the introduction, hardware devices are supported through a Hardware Adaptation Layer. This section describes the HAL API.

This section is required only for system programming that needs low level access to the hardware for configuration and monitoring. This API does not apply to sockets application programming.

D.1 Overview

The function of the HAL is to provide resources to the stack library functions and allow them to operate independently of the current run-time environment. The HAL contains the functionality required by the stack that depends directly on the hardware in a particular environment.

D.1.1 HAL Function Types

The HAL is interspersed with two different types of functions; those that are called at kernel level (inside an llEnter() / llExit() pairing), and those that are not. (For more information on the llEnter() and llExit() functions, see Section A.1.)

To distinguish kernel level functions from application support functions, both have been given a different naming conventions. Kernel level functions are named with an ll prefix, without a leading underscore, for example: llPacketSend(), while application functions have an underscore, for example: _llPacketInit().

D.1.2 External Calls from HAL Functions

Because HAL functions are called from the stack kernel, they are executing within an llEnter() / llExit() pair. These HAL functions can call the stack API directly, but should not call normal application functions.

If a HAL function must call an external application function, or if it is going to call a potentially blocking function, then it should first call llExit(). Then, when it has completed, it should call llEnter() before returning to the stack. It is important not to block while in an llEnter() / llExit() pair.

D.2 Low-Level LED Driver (llUserLed)

The User LED driver is not really a driver at all. It is a collection of functions to control (ON|OFF|TOGGLE) LED lights on a given hardware platform.

D.2.1 Function Overview

Application Functions:

Function Description
_llUserLedInit() Initialize the LED displays to their default state
_llUserLedShutdown() Shut down the LED environment
LED_ON() Turn on a LED
LED_OFF() Turn off a LED
LED_TOGGLE() Toggle the state of a LED

D.2.2 Low-Level LED API Functions

The following functions are required.

_llUserLedInit – Initialize the LED Displays to their Default State

void _llUserLedInit();

Return Value None.

Description This function initializes anything necessary to get the LED displays to their default state.

_llUserLedShutdown – Shutdown the LED Environment

void _llUserLedShutdown();

Return Value None.

Description This function is called when shutting down the system to shut down and clean up the LED environment. Typically, this is an empty function.

LED_ON – Turn On an LED

void LED_ON( uint32_t ledId );

Description This function turns on the specified LED in the calling argument.

LED_OFF – Turn Off an LED

void LED_OFF( uint32_t ledId );

Description This function turns off the LED specified in the calling argument.

LED_TOGGLE – Toggle the State of an LED

void LED_TOGGLE( uint32_t ledId );

Description This function toggles the on/off state of an LED specified in the calling argument.

D.3 Low-Level Timer Driver (llTimer)

The stack code requires a very basic simple time function. It consists of two parts: a function API, which can be called from the stack to get the current time, and a scheduler that sends timer event notifications every 100ms using the STKEVENT event object.

D.3.1 Function Overview

Application Functions:

Function Description
_llTimerInit() Initialize Timer Environment
_llTimerShutdown() Shutdown Timer Environment

Kernel Layer Functions:

Function Description
llTimerGetTime() Get the Current Time
llTimerGetStartTime() Get the Initial Startup Time

D.3.2 Low-Level Timer API Functions

The following functions are required.

_llTimerInit – Initialize Timer Environment

void _llTimerInit( STKEVENT_Handle hEvent,
                   uint32_t        ctime );

Return Value None.

Description This function is called to initialize the timer environment, and to set the initial time. The value of ctime is the number of seconds elapsed from a known reference. An initial value of zero is also acceptable. The stack software is only tracks relative time. Take care when setting this value because the stack does not manage the timer value wrapping. This occurs every 136 years, or in 2116 if time is based off of Jan 1, 1980.

Every 100mS, the timer driver will indicate a timer event to the event object specified by hEvent. This STKEVENT object is discussed in Section A.6.

_llTimerShutdown – Shutdown Timer Environment

void _llTimerShutdown();

Return Value None.

Description This function is called when shutting down the system, to shut down and clean up the timer environment.

llTimerGetTime – Get Current Time in Seconds and Milliseconds

uint32_t llTimerGetTime( uint32_t *pMSFrac );

Description Returns the number of seconds that have elapsed since the timer driver was started. If the pointer pMSFrac is non-zero, the function writes the fractional seconds (in milliseconds) to this location (0 to 999).

NOTE: Although the stack does not require real time, do not simply use a millisecond timer and divide by 1000, as the value will wrap every 50 days. Device drivers should attempt to provide a time value accurate down to millisecond granularity.

llTimerGetStartTime – Get the Initial Startup Time

uint32_t llTimerGetStartTime();

Return Value Initial start time in seconds.

Description Returns the initial start time that was passed to _llTimerOpen().

D.4 Low-Level Packet Driver (llPacket)

The stack code requires a very basic packet function library. Note that although the high level packet API is documented here, the HAL contains a generic packet driver that implements this API. It is more efficient to use the standard llPacket driver and provide a hardware specific mini-driver than to implement the llPacket API from scratch. The llPacket mini-driver is described in the support package documentation for your hardware platform.

D.4.1 Function Overview

Application Functions:

Function Description
_llPacketInit() Initialize Driver Environment and Enumerate Devices
_llPacketShutdown() Shutdown Driver Environment
_llPacketServiceCheck() Check for Packet Activity

Kernel Layer Functions:

Function Description
llPacketOpen() Open Driver and Bind Logical Ether Object to Device Id
llPacketClose() Close Driver and Unbind Logical Ether Object from Device Id
llPacketSetRxFilter() Set Packet Receive Filter
llPacketGetMacAddr() Get MAC address
llPacketGetMCastMax() Get the Maximum Number of Multicast Addresses
llPacketGetMCast() Get Multicast Address List
llPacketSetMCast() Set Multicast Address List
llPacketService() Service a Queued Packet
llPacketSend() Send a Packet
llPacketIoctl() Execute Driver Specific IOCTL command

D.4.2 Low-Level Packet API Functions

The low-level support layer must provide the following functions:

_llPacketInit – Initialize Driver Environment and Enumerate Devices

uint32_t _llPacketInit( STKEVENT_Handle hEvent );

Return Value Returns the number of physical packet devices.

Description This function is called by NETCTRL to initialize the packet driver environment. This function also enumerates all the physical packet devices in the system, and returns a device count. The stack will then call the llPacketOpen() function once for each physical device indicated.

The hEvent calling parameter is a handle to a STKEVENT object that must be signaled whenever a packet is received. This STKEVENT object is discussed in Section A.6.

_llPacketShutdown – Shutdown Driver Environment

void _llPacketShutdown();

Return Value None.

Description This function is called by NETCTRL to indicate a final shutdown of the packet driver environment. When called, there should be no currently open packet drivers, and _llPacketInit() will be called again before any call to llPacketOpen().

_llPacketServiceCheck – Check for Ethernet Packet Activity

void _llPacketServiceCheck( uint32_t fTimerTick );

Return Value None.

Description This function is called by NETCTRL to check if packets are available from the Ethernet device. In a polling system, this function is called continuously. In an interrupt driven semaphore system, it is called when packet activity is indicated via the STKEVENT object, and also by the scheduler at 100ms timer intervals for dead man polling checks.

In both polling and interrupt environments, the fTimerTick flag will be set whenever a 100ms timer tick has occurred.

If any new packets are detected from within this function, the packet driver should signal the STKEVENT object in the passive mode (do not set the fHwAsynch flag in the STKEVENT_signal() function). This only applies to new packet events detected from within this function. The STKEVENT object is discussed in Section A.6.

llPacketOpen – Open Driver and Bind Logical Ether Object to Device ID

uint32_t llPacketOpen( uint32_t dev,
                       void     *hEther );

Return Value This function should return 1 on success, and 0 on failure.

Description Opens the low level packet driver specified by the one’s based index dev. The maximum value of dev is the number of devices returned from the _llPacketInit() function. When opening the device, the packet driver should bind the physical index with the logical Ether object handle specified in hEther. This handle is used in receive indications to the stack.

llPacketClose – Close Driver and Unbind Logical Ether Object from Device ID

void llPacketClose( uint32_t dev );

Return Value None.

Description Closes the low level packet driver specified by the one’s based index dev. The maximum value of dev is the number of devices returned from the _llPacketInit() function. After this call, the packet driver should no longer attempt to indicate received packets to the stack.

llPacketSetRxFilter – Set Packet Receive Filter

void llPacketSetRxFilter( uint32_t dev,
                          uint32_t filter );

Return Value None.

Description Called to set the types of packets that should be received via the receive indication function. Each level of filter is inclusive of the previous level. They are:

llPacketGetMacAddr – Get MAC Address

void llPacketGetMacAddr( uint32_t      dev,
                         unsigned char *pbData );

Return Value None.

Description Copies the 6 byte MAC address of the physical device index dev into the supplied data buffer.

llPacketGetMCastMax – Get the Maximum Number of Multicast Addresses

uint32_t llPacketGetMCastMax( uint32_t dev );

Return Value The maximum number of 6 byte MAC addresses that can be supplied for llPacketSetMCast().

Description Called to get the maximum number of multicast addresses that can be supported on the physical packet device.

llPacketGetMCast – Get Multicast Address List

uint32_t llPacketGetMCast( uint32_t      dev,
                           uint32_t      maxaddr,
                           unsigned char *pbAddr );

Return Value The number of 6 byte MAC addresses written to pbAddr.

Description Called to get the current list of multicast addresses installed on the physical device. The maximum size of the list (supplied as an address count) is in maxaddr. The list is a contiguous stream of 6 byte addresses pointed to by pbAddr. The function returns the number of addresses in the list supplied.

llPacketSetMCast – Set Multicast Address List

void llPacketSetMCast( uint32_t      dev,
                       uint32_t      addrcnt,
                       unsigned char *pbAddr );

Return Value None.

Description Called to install a list of multicast addresses on the physical device. The size of the list (supplied as an address count) is in addrcnt. The list is a contiguous stream of 6 byte addresses pointed to by pbAddr. The new list preempts any previously installed list, and thus an address count of ZERO removes all multicast addresses.

llPacketService – Service a Queued Packet

void llPacketService();

Description This function is called to inform the driver that it may now indicate any queued packet buffers to the Ether object corresponding to the physical ingress device. Packet drivers must internally queue their own packets. Queued packets cause events to be sent to the scheduler that will in turn call this function.

Packets are passed to the Ether object via EtherRxPacket().

llPacketSend – Send a Packet

void llPacketSend( uint32_t   dev,
                   PBM_Handle hPkt );

Description Called to send a packet out the physical packet device indicated by dev. The information about the packet (size and location) is contained in the PBM packet buffer specified by the handle hPkt. Once the packet has been sent, the packet buffer must be freed by calling PBM_free().

The PBM packet buffer object is described in detail in Section A.3.

llPacketIoctl – Execute Driver Specific IOCTL Command

uint32_t llPacketIoctl( uint32_t dev,
                        uint32_t cmd,
                        void     *arg );

Return Value This function returns 1 for success.

Description Called to execute the driver specific IOCTL command. For detailed information about the set of commands specific to your device, check the NDK Support Package document of your hardware platform.

D.5 Low-Level Serial Port Driver (llSerial)

In the current directory structure, the serial port driver (llSerial) may or may not be part of the HAL directory (as it is an optional component). However, it is part of the HAL architecture, and should be programmed using the same guidelines used for the llTimer and llPacket drivers..

D.5.1 Function Overview

Application Functions:

Function Description
_llSerialInit() Initialize Driver Environment and Enumerate Devices
_llSerialShutdown() Shutdown Driver Environment
_llSerialServiceCheck() Check for packet activity
_llSerialSend() Send Raw Data to the Serial Port

Kernel Layer Functions:

Function Description
llSerialOpen() Open Driver in Character Mode
llSerialClose() Close Driver Character mode
llSerialOpenHDLC() Open Driver HDLC Session
llSerialCloseHDLC() Close Driver HDLC Session
llSerialConfig() Set Serial Port Configuration
llSerialHDLCPeerMap() Update the HDLC encoding peer CMAP
llSerialService() Service HDLC Packets
llSerialSendPkt() Send a Serial Data Packet

D.5.2 Low-Level Serial API Functions

The low level support layer must provide the following functions:

_llSerialInit – Initialize Driver Environment and Enumerate Devices

uint32_t _llSerialInit( STKEVENT_Handle hEvent );

Return Value Returns the number of physical serial devices.

Description This function is called by NETCTRL to initialize the system to use the serial port. It also enumerates all the physical devices in the system, and returns a device count. The stack will then call the llSerialOpen() function and/or the llSerialOpenHDLC() function for each physical device it requires.

The hEvent calling parameter is a handle to a STKEVENT object that must be signaled whenever a serial packet (or raw data) is received. This STKEVENT object is discussed in Section A.6.

_llSerialShutdown – Shutdown Driver Environment

void _llSerialShutdown();

Return Value None.

Description This function is called by NETCTRL to indicate a final shutdown of the serial driver environment. When called, there should be no currently open serial drivers, and _llSerialInit() will be called again before any call to llSerialOpen() or llSerialOpenHDLC().

_llSerialServiceCheck – Check for Serial Port Activity

uint32_t _llSerialServiceCheck( uint32_t fTimerTick );

Return Value None.

Description This function is called by NETCTRL to check if serial packets (or data) are available from the serial device. In a polling system, this function is called continuously. In an interrupt driven semaphore system, it is called when packet activity is indicated via the STKEVENT object, and also by the scheduler at 100mS timer intervals for dead man polling checks.

In both polling and interrupt environments, the fTimerTick flag will be set whenever a 100mS timer tick has occurred.

If any new serial packets are detected from within this function, the packet driver should signal the STKEVENT object in the passive mode (do not set the fHwAsynch flag in the STKEVENT_signal() function). This only applies to new packet events detected from within this function. The STKEVENT object is discussed in Section A.6.

Finally, if the driver is only open in character mode (not HDLC), and there are characters for the character mode device waiting, they are passed into the user application from this function by calling character mode input callback function passed to llSerialOpen().

_llSerialSend – Send Raw Data to the Serial Port

uint32_t _llSerialSend( uint32_t      dev,
                        unsigned char *pBuf,
                        uint32_t      len );

Return Value The number of bytes sent to the serial port.

Description This function is called by the application to send raw unpacketized serial data to the serial port. This function may only be called when the serial driver is not open for HDLC mode. The function returns the number of bytes sent, which will always be either the number of bytes it was told to send specified by the len parameter, or NULL on an error.

Note that this function is provided mainly for convenience to the application programmer. The implementation of this function is to packetize the data specified in the pBuf and len parameters into a PBM buffer, and then call SerialSendPkt().

llSerialOpen – Open Driver in Character Mode

uint32_t llSerialOpenCharmode( uint32_t dev,
                               void (*pCharmodeRxCb)(char c) );

Return Value This function should return 1 on success, and 0 on failure.

Description Opens the low level serial driver specified by the one’s based index dev in character mode. The maximum value of dev is the number of devices returned from the _llSerialInit() function.

Character mode input simply passes all characters received at the port to the character mode receiver.

When opening the device, the driver should save the callback function pointer pCharmodeRxCb. This function is called for each character received while in character mode when the _llSerialServiceCheck() function is called. Serial drivers queue up serial data, signaling an event to the STKEVENT object passed to _llSerialInit(), and then pass the serial data to the application callback function from within the _llSerialServiceCheck() function.

When the driver is opened in HDLC mode, no character mode input is received. When the HDLC mode is closed, the character mode becomes active again.

llSerialClose – Close Driver Character Mode

void llSerialClose(uint32_t dev);

Return Value None.

Description Closes the character mode of the low level serial driver specified by the one’s based index dev. Once called, the serial driver should not attempt to call any character mode callback function.

llSerialOpenHDLC – Open Driver HDLC Session

uint32_t llSerialOpenHDLC( uint32_t dev,
                           void     *hHDLC,
                           void (*cbTimer)(void *h),
                           void (*cbHDLCInput)(PBM_Handle hPkt));

Return Value This function should return 1 on success, and 0 on failure.

Description Opens the low level serial driver specified by the one’s based index dev in HDLC mode. The maximum value of dev is the number of devices returned from the _llSerialInit() function.

The hHDLC parameter is a handle to the HDLC device. Any HDLC packet received has its Rx interface in the PBM packet buffer set to this device handle.

The callback function cbTimer is called by the driver every second to drive any timeouts required by the caller. Note the serial driver calls cbTimer from kernel mode.

Serial drivers queue up HDLC packets. When a complete HDLC packet is ready, the driver signals an event to the STKEVENT object passed to _llSerialInit(), and then passes the HDLC packet (as a PBM packet buffer) to the application callback function cbHDLCInput from within the llSerialService() function.

This is similar to character mode operation, but different because the entire packet is passed over at one time, and it is done from the llSerialService() function, not from _llSerialServiceCheck() as with character mode data. The cbHDLCInput function is called from kernel mode while the character mode application callback is not.

When the driver is in HDLC mode, the driver receives serial data as HDLC packets, and creates a PBM packet buffer object to hold each HDLC frame. Note that the HDLC flag character (0x7E) is always removed from the HDLC packets. The HDLC packet passed to the cbHDLCInput function is formatted as follows:

Addr (FF) Control (03) Protocol Payload CRC
1 1 2 1500 2

The serial driver processes the HDLC packet data as it arrives to remove any escaped characters and to verify the CRC. When a HDLC packet is ready, the driver signals an event to the STKEVENT object.

llSerialCloseHDLC – Close Driver HDLC Session

void llSerialCloseHDLC( uint32_t dev );

Return Value None.

Description Closes the HDLC mode of the low level serial driver specified by the one’s based index dev. Once called, the serial driver should not attempt to indicate HDLC frame buffers to the scheduler or stack. Any queued buffers should be flushed.

llSerialConfig – Configure Serial Port

void llSerialConfig( uint32_t dev,
                     uint32_t baud,
                     uint32_t mode,
                     uint32_t flowctrl );

Return Value None.

Description This function is called to configure the serial port attributes for the indicated device.

The value of baud is the baud rate, and must be an even denominator of 230400, up to a maximum baud rate of 230400. For example: 230400, 115200, 57600, 38400, 28800, and 19200 are all legal values, while 56000 is not.

The value of mode indicates the mode of the device including data bits, parity, and stop bits. Only the two most commonly used modes are defined:

The value of flowctrl indicates the desired flow control operation. Legal values for this parameter are:

This function can be called before or after the device is opened.

llSerialHDLCPeerMap – Update the HDLC Encoding Peer CMAP

void llSerialHDLCPeerMap( uint32_t dev,
                          uint32_t peerMap );

Return Value None.

Description When in HDLC mode, the serial driver sends all serial data as HDLC frames. This requires it to add the frame flag characters, and do any character escaping necessary to encode the frame for transmission over the serial link. This includes escaping characters that appear in the peer’s character map (CMAP).

By default, the CMAP is set to 0xFFFFFFFF. For character codes 0 to 31, if the bit (1<<charval) is set in the CMAP, then the serial driver performs an HDLC escape sequence when sending the character in an HDLC frame.

This function allows the application to update the peer’s CMAP as it gets information from the peer allowing it to do so.

llSerialService – Service HDLC Packets

void llSerialService();

Return Value None.

Description This function is called to inform the driver that it may now indicate any queued HDLC buffers to the HDLC callback function corresponding to the serial port. Serial drivers internally queue a PBM packet buffer for each HDLC frame received. When a new packet is received, the driver signals the STKEVENT object, which will cause this function to be called by the network scheduler.

llSerialSendPkt – Send a Serial Data Packet

void llSerialSendPkt( uint32_t dev,
                      PBM_Handle hPkt );

Return Value None.

Description Called to send a serial data packet out the physical serial device indicated by dev. The information about the packet (size and location) is contained in the PBM packet buffer specified by the handle hPkt. Once the packet has been sent, the packet buffer must be freed by calling PBM_free().

The data is treated as raw bytes when the driver is not open in HDLC mode. When in HDLC mode, the data packet is an HDLC frame with the following format:

Addr (FF) Control (03) Protocol Payload CRC
1 1 2 1500 2

Note that the CRC on the packet does not need to be valid. The serial port driver will validate the CRC when the packet is sent. However, the 2 byte space-holder for the CRC must be present in the packet.

E Web Programming with the HTTP Server

NOTE: the HTTP Server is no longer provided in the NDK. The Network Services Component provides a more capable HTTP Server.

F BSD Sockets Support

The NDK no longer provides a Berkeley Software Distribution (BSD) API support layer. This layer is now provided by SlNetSock, which is part of the Network Services (NS) Component, typically distributed in your SDK.

Applications can access the BSD APIs via SlNetSock, but applications must not include both the BSD headers and the NDK headers in the same compilation unit.

F.1 Using BSD Sockets Provided by SlNetSock

NOTE: The NDK no longer provides a Berkeley Software Distribution (BSD) API support layer. This layer is now provided by NS.

Applications can access the BSD APIs via SlNetSock, but applications must not include both the BSD headers and the NDK headers in the same compilation unit.

To use BSD APIs, make the following changes to your application.

Update your compiler’s file search path to include the following directory within your SDK installation. This allows the #include statements in an existing BSD application to be resolved. For SimpleLink SDKs, for example, the following include path should be added:

<SIMPLELINK_SDK_INSTALL_DIR>/source/ti/net/bsd

Include the BSD socket header file.

#include <sys/socket.h>

Add a new C function that will be run as a thread. (The Task is created in the next step.) Copy the contents of your BSD sockets code into this new Task function.

Create a new thread that will be used to run your BSD sockets code. The Task thread can be created dynamically using native OS APIs, as described in the TI Network Developer’s Kit (NDK) User’s Guide (SPRU523). You must ensure that the Task cannot run until the stack is up and all IP addresses (IPv4 or IPv6) have been bound and are ready. This is usually achieved by using a semaphore to block at the beginning of the sockets Task, and posting the same semaphore from the NDK IP address hook function, which is called when the IPv4 address has been bound. This prevents the Task from running the sockets code until it unblocked. Another way to prevent the Task from running too early is to create the Task dynamically in the program at a time when the stack is ready. For example, the Task may be created in the Network IP address hook function, which is run when an IPv4 address is added or removed from the system. Refer to the section on “Creating a Task” in the NDK User’s Guide (SPRU523) for details.

F.2 Things to Remember About BSD Compatibility

Remember the following issues when integrating BSD sockets code into an NDK application.

Include statement: Most BSD sockets applications should have the following include statement:

#include <sys/socket.h>

File separation: In general, a BSD application should contain BSD-style sockets code in a separate C file. That is, BSD sockets code should not be mixed with NDK code, such as NDK sockets code or standard (non-BSD) NDK APIs. This must be done in order to avoid type and function name conflicts between standard NDK headers and BSD layer headers.

The file containing BSD-style code can include BSD header files (for example, sys/socket.h) and should not need to include any NDK header file found in ti/ndk/inc.

A good rule to follow is to organize BSD sockets code into a separate file that includes only BSD style header files along with OS related header files as needed. Code that performs NDK-specific functionality–for example, NDK network open or close hooks, IPv6 system initialization and deinitialization calls–should go a separate file or files that include the NDK header files found in ti/ndk/inc.

Types and domains: While BSD sockets (for example, in Linux) can support many different domains and types (such as “PF_APPLETALK” or “SOCK_SEQPACKET”), only existing NDK socket domains and types are supported. No support for new types or domains has been added. The domains and types supported in the BSD layer are AF_INET and AF_INET6.

IPv6 support: In order for IPv6 sockets code to work correctly, application code must be compiled with _INCLUDE_IPv6_CODE defined and with IPv6 enabled in XGCONF.

G IP Version 6 (IPv6) Stack API

This section discusses use of the API for the IPv6 stack.

G.1 Synopsis

The IPv6 stack is designed to be modular and coexist with the traditional IPv4 stack. The IPv6 stack can be easily built in or out of an application using the “_INCLUDE_IPv6_CODE” compilation flag. IPv6 stack is available only with NIMU enabled architectures. Default stack builds are provided with IPv6 stack enabled.

The IPv6 stack is similar in its architecture to the IPv4 stack with respect to the components it is made of. Figure G-1 shows the main building blocks of IPv6.

Figure G-1 NDK IPv6 Architectural Block Diagram

The IPv6 stack components and the RFCs it supports are as follows:

G.2 Initialization and Deinitialization

To enable IPv6 in your application, users must take the following steps:

The following example illustrates how the code to initialize the IPv6 stack can be done in the networkOpen function. Note the initialization does not have to be done here, but it serves as convenient place for this code:

#define IPv6_DEVICE_INDEX 1


/*
 *  ======== IPv6DADStatus ========
 *  IPv6 initialization callback function
 */
static void IPv6DADStatus(IP6N Address, unsigned short dev_index, unsigned char Status)
{
    char strIPAddress[40];

    /* Convert the IP Address to String Format. */
    IPv6IPAddressToString(Address, strIPAddress);

    /* Print the status of the address. */
    printf("IPv6 address: %s on device %d is %s\n", strIPAddress, dev_index,
        (Status == 1) ? "UNIQUE" : "DUPLICATE");

    return;
}


/*
 *  ======== networkOpen ========
 *  User defined function (hook) that's typically passed as an argument to the
 *  NC_NetStart() API. Here, it's used to initialize the IPv6 stack.
 */
static void networkOpen()
{
    int status = 0;


#ifdef _INCLUDE_IPv6_CODE
    /* Initialize IPv6 */
    llEnter ();
    /*
     * IPv6_DEVICE_INDEX should be set to the interface ID of the interface to
     * initialize IPv6 on. For most users this value should be set to 1. Also
     * note that this value should always be 1 or greater.
     *
     * IPv6DADStatus is a function callback that is called when the IPv6 DAD
     * (Duplicate address detection) process has completed on an address.
     */
    status = IPv6InterfaceInit(IPv6_DEVICE_INDEX, IPv6DADStatus);
    llExit ();

    if (status < 0) {
        /* Unable to initialize the IPv6 stack */
    }
    else {
        /* IPv6 stack has been initialized */
    }
#endif
}

Deinitialization follows a similar pattern. The following example uses the NetworkClose function to illustrate this:

/*
 *  ======== networkClose ========
 *  User defined function (hook) that's typically passed as an argument to the
 *  NC_NetStart() API. Here, it's used to deinitialize the IPv6 stack.
 */
static void networkClose()
{
    int status = 0;

#ifdef _INCLUDE_IPv6_CODE
    /* Enter the kernel Mode. */
    llEnter ();
    status = IPv6InterfaceDeInit(IPv6_DEVICE_INDEX);
    llExit ();

    /* Were we able to deinitialize the stack? */
    if (status < 0) {
        /* unable to deinitialize the IPv6 stack */
    }
    else {
        /* IPv6 stack deinitialized */
    }
#endif
}

G.3 API Functions and Data Structures

G.3.1 Socket Support for IPv6

The following is a list of Socket API that is supported:

The No-Copy variants for receive are not supported for IPv6 sockets.

In order for IPv6 sockets code to work correctly, application code must be compiled with _INCLUDE_IPv6_CODE defined and with IPv6 enabled in XGCONF.

G.3.2 Architecture

Figure G-2 illustrates the internal architecture block diagram for the socket layer:

Figure G-2 Internal Architecture Block Diagram for Socket Layer

When a socket is created, it is marked as either an IPv4 socket or an IPv6 socket, i.e., in the socket creation the socket family selected is either AF_INET (IPv4) or AF_INET6 (IPv6). This is then used as a de-multiplexing field to differentiate which socket implementation needs to be selected. This architecture reduces the impact on the existing IPv4 implementation of sockets.

G.3.3 Socket Options

The IPv6 socket layer supports all the standard socket properties except the following:

Of all the IPv6 specific options, the only one supported is the IPV6_UNICAST_HOPS, which allows the configuration of the Hop Limit in the packet.

G.3.4 Daemon6

NDK supports a module called Daemon, which is a single network task that monitors the socket status of multiple network servers. When activity is detected, the daemon creates a task thread specifically to handle the new activity. This is more efficient than having multiple servers, each with their own listening thread.

Since the Daemon operates on sockets, a new module called Daemon6 has been created that does the same functionality as Daemon except that it operates on V6 sockets.

The following snippet of code indicates how Echo Servers on UDP can be made to operate on both IPV4 and IPV6

hEchoUdp = DaemonNew( SOCK_DGRAM, 0, 7, dtask_udp_echo, OS_TASKPRINORM,
                      OS_TASKSTKNORM, 0, 1 );

#ifdef _INCLUDE_IPv6_CODE
hEchoUdp6 = Daemon6New (SOCK_DGRAM, IPV6_UNSPECIFIED_ADDRESS, 7,
                        dtask_udp_echo6, OS_TASKPRINORM,
                        OS_TASKSTKNORM, 0, 1);
#endif

G.3.5 Nettools Applications

The Net-Tools module in the NDK stack has multiple applications that are provided to System developers. These applications provide basic services such as Telnet, TFTP etc. This section documents the modifications in these applications to support IPv6.

G.3.5.1 Telnet

Telnet is implemented as a server daemon that resides on port 23 and waits for incoming connections. The Telnet protocol by itself is agnostic to the Layer3 implementation i.e. IPv4 or IPv6. To be able to support IPv6 the following code needs to be added in the application startup code:

hTelnet6 = Daemon6New (SOCK_STREAM, IPV6_UNSPECIFIED_ADDRESS, 23,
                              (int(*)(SOCKET,uint32_t))telnetClientProcess,
                              OS_TASKPRINORM, OS_TASKSTKLOW,
                              (uint32_t)ConsoleOpen, 2);

The code uses the Daemon6 API described above and starts the Telnet Daemon that opens an IPv6 socket on port 23. There is no conflict since the IPv4 Telnet daemon has also opened port 23 since the socket library for IPv4 and IPv6 are different.

With these modifications the Telnet daemon works over IPv6. There is one minor change in the Telnet code base that needs to be addressed. This is a display issue; after performing the telnet the server displays the Peer IP Address and Port Information.

For example, on IPv4 the display is as follows:

TCP/IP Stack Example Client
Welcome connection : 192.168.1.2:3881


Welcome to the console program.
Enter '?' or 'help' for a list of commands.
>

On IPv6, the display code needs to be modified as follows to display the IPv6 address of the peer:

TCP/IP Stack Example Client
Welcome connection : fe80::a00:9ff:fedc:fbdc:1061


Welcome to the console program.
Enter '?' or 'help' for a list of commands.
>

G.3.5.2 TFTP

The TFTP is an IPv6 client that implements the Trivial File Transfer Protocol over IPv6. The TFTP protocol uses the IP Address and Port information to ensure that data packets being received match the peer server and port information; this is typically implemented by most TFTP implementations for a more secure file transfer.

To achieve modularity, a new module TFTP6 has been created, which was based on the TFTP module. Modifications have been done to ensure that the TFTP6 module uses the AF_INET6 family for socket creation and the security checks are done with respect to the IPv6 addresses.

The following new API has been published to be able to retrieve a file over an IPv6 network through TFTP:

int Nt6TftpRecv (IP6N TftpIP, char *szFileName, char *FileBuffer,
                        uint32_t *FileSize, uint16_t *pErrorCode );

G.3.5.3 DNS Client

The existent DNS Client in NDK is capable of doing IPv4 forward and reverse name resolutions [2]. This is extended to do IPv6 forward and reverse AAAA (Quad-A) type DNS lookups as described in RFC 3596 [2] over IPv4 network. There is no support for the DNS client to communicate with an IPv6 DNS server; i.e., the DNS client is only capable of doing name resolutions by communicating with an IPv4 DNS server.

To support IPv6 name resolution, the following changes have been made to the existing implementation of DNS:

/**
 * @brief
 * The structure describes the Host Name - IP Address record
 *
 * @details
 * The HOSTENT structure holds information such as IPv4/v6
 * address, host name mappings for a given host. It is used
 * by the DNS resolver in conveying such HostName - IP Address
 * mappings to a user application.
 */
struct _hostent {
    /**
     * @brief    This is the official name / Fully Qualified Domain Name
     * (FQDN) of the host.
     */
    char *h_name;

    /**
     * @brief    This indicates the address family of the IP address that
     * maps to the given hostname. The values it takes are AF_INET (v4) /
     * AF_INET6 (v6).
     */
    int h_addrtype;

    /**
     * @brief    This indicates the length (in bytes) of the IP address that follows.
     * For IPv4 address it is set to 4, and for IPv6 address set to 16 bytes.
     */
    int h_length;

    /**
     * @brief    This is the number of IP addresses returned for the given
     * hostname.
     */
    int h_addrcnt;

#ifndef _INCLUDE_IPv6_CODE

    /**
     * @brief    List of up to MAXIPADDR IPv4 addresses (Network format) that map
     * to the given hostname.
     */
    uint32_t h_addr[MAXIPADDR];

#else
    /**
     * @brief    List of up to MAXIPADDR IPv4/IPv6 addresses that map to given hostname.
     */
    char* h_addr_list[MAXIPADDR];
#endif
}

NOTE: The field h_addr_list is an array of strings that holds the IPv4/v6 addresses as pointers to IPN/IP6N, respectively. The following is an example illustration of how one could use the h_addr_list field to access IPv4/IPv6 address from the HOSTENT structure.

IPv4 Illustration:

uint32_t IPTmp;
retcode = DNSGetHostByXXX( arg1, ... argn );

for( retcode = 0; retcode < phe->h_addrcnt; retcode++ )
{
    IPTmp = (uint32_t)RdNet32(phe->h_addr_list[retcode]);
    ConPrintf("IPAddr = ");
    ConPrintIPN(IPTmp);
    ConPrintf("\n");
}

IPv6 Illustration:

IP6N IPv6Tmp;
retcode = DNSGetHostByXXX( arg1, ... argn );

for( retcode = 0; retcode < phe->h_addrcnt; retcode++ )
{
    IPv6Tmp = *(IP6N *)phe->h_addr_list[retcode];
    ConPrintf("IPv6 Addr = ");
    ConIPv6DisplayIPAddress(IPv6Tmp);
    ConPrintf("\n");
}

IPv6 Address to Hostname Resolution API (reverse DNS lookup for IPv6):

/**
 * @b Description
 * @n
 *      This function does reverse DNS lookup on the supplied
 *      IPv6 Address. On a successful return, pScrapBuf can be
 *      treated as a HOSTENT structure. The size of the scrap
 *      buffer (size) must be greater than the size of the structure
 *      as the structure will contain pointers into the scrap
 *      buffer, and the scrap buffer is also used for temporary
 *      name storage. 512 bytes of scrap buffer memory should be
 *      sufficient for most requests.
 *
 * @param[in] IPAddr
 *      The IPv6 address that needs to be resolved in IP6N format.
 *
 * @param[out] pScrapBuf
 *      Scrap buffer area to hold the results on a successful
 *      DNS resolution.
 *
 * @param[in] size
 *      Size of the scrap buffer available.
 *
 * @retval
 *      Success - 0
 *
 * @retval
 *      Error - >0, error code to determine the error.
 */
int DNSGetHostByAddr2(IP6N IPAddr, void *pScrapBuf, int size);

Hostname to IPv6 Address Resolution API (forward DNS lookup for IPv6):

/**
 * @b Description
 * @n
 *      This function does DNS lookup on the supplied hostname.
 *      On a successful return, the pScrapBuf can be treated as a
 *      HOSTENT structure. The size of the scrap buffer (size)
 *      must be greater than the size of the structure as the
 *      structure will contain pointers into the scrap buffer, and
 *      the scrap buffer is also used for temporary name storage.
 *      512 bytes should be sufficient for most DNS requests.
 *
 *      If the host name "Name" is terminated with a dot ('.'), the
 *      dot is removed. If the name contains a dot anywhere, it is
 *      used unmodified for an initial lookup. If the lookup fails -
 *      the appropriate DNS error code is returned. No default
 *      domain lookups are performed for IPv6, so if the hostname
 *      provided by user does not contain a dot, implying no
 *      domain name is provided, this function returns a format error.
 *
 * @param[in] Name
 *      The hostname to be resolved supplied by the user.
 *
 * @param[in] af_family
 *      The family (AF_INET/AF_INET6) of the IP address to which the
 *      query needs to be resolved to. If AF_INET is provided as the
 *      argument, then DNSGetHostByName is called in turn for IPv4
 *      lookup.
 *
 * @param[out] pScrapBuf
 *      Scrap buffer area to hold the results on a successful
 *      DNS resolution.
 *
 * @param[in] size
 *      Size of the scrap buffer available.
 *
 * @retval
 *      Success - 0
 *
 * @retval
 *      Error - >0, error code to determine the error.
 */
int DNSGetHostByName2(char *Name, unsigned char af_family, void *pScrapBuf, int size);

G.3.6 Configuring the IPv6 Stack

One of the key differences between IPv6 and IPv4 is that the IPv6 stack needs to be instantiated at run time. This is because all IPv6 enabled interfaces automatically get a link local address, which is identified by the unique MAC address. Once an IPv6 address is assigned, it is mandatory that the IPv6 stack perform the Duplicate Address Detection process to ensure address uniqueness.

This implies that unlike IPv4 addresses cannot be assigned to the interface before the Ethernet Link is up because this will imply that the DAD packets never get transmitted out hence defeating the overall purpose.

RFC 2462, Section 5.3, states the following:

A node forms a link-local address whenever an interface becomes enabled. An interface may become enabled after any of the following events:

In order to support IPv6 all Platform Support Packages should be modified such that the above mentioned API’s are added in the Ethernet Link Change code. This is outside the context of the document and should be addressed in the NDK Support Package documentation.

Initialization and de-Initialization of the IPv6 stack is the responsibility of the system application and should be used in conjunction with the Link change events described above. Figure G-3 showcases the various entities in the system:

Figure G-3 IPv6 Stack Instantiation Placement

The IPv6 stack provides the necessary APIs, which are responsible for the initialization and cleanup of the IPv6 stack instance on the interface.

For user convenience, a sample command prompt demonstration has been provided with the IPv6 stack to initialize and attach the IPv6 stack to a desired interface, and to demonstrate the use of various IPv6 utilities. For more details, see the IPv6 Stack Testing section of the TI Network Developer’s Kit (NDK) v2.21 User’s Guide (SPRU523) document.

Related Documentation From Texas Instruments

Additional information about the NDK can be found in the NDK Users Guide. If you have questions, you can ask them on the forum for your target device in TI’s E2E community.

If you have questions, you can ask them on the forum for the SDK that contains your NDK in TI’s E2E community.