11

Integrating a New Network Service



11.1    Introduction

A network service, such as a network protocol, is an implementation of the network and transport layers of the OSI network model. As shown in Figure 10-1, network services communicate with the data link layer through the MUX interface. Everything specific to the network interface is handled in the drivers of the data link layer, which are described in 10. Integrating a New Network Interface Driver.



11.2    Writing a Network Service Sublayer

A network service sublayer allows a network service to send and receive packets through the MUX. It may be written as part of a network service, or as a separate element that the service uses. The minimum requirements of a network service sublayer are an initialization routine and routines that support packet transfer and error reporting. Support for flow control is optional.

11.2.1   Interface Initialization

When the system constructs the network stack, it activates network interfaces that include a network service and a network driver. The activation routine that you provide in your network service sublayer, by convention, is named fooAttach( ), where foo is replaced by an abbreviation for the network service. This naming convention is a generalization based on the name of ipAttach( ). Your fooAttach( ) function typically allocates and initializes data structures that represent the interface being attached to, determines the network driver paradigm (END or NPT), and binds to the driver interface through the MUX.

Determining the Driver Paradigm

To determine a driver's operating paradigm, use the muxTkDrvCheck( ) function (see B.2.17 muxTkDrvCheck( ), p.281). If your network service supports only NPT devices, its fooAttach( ) routine should return an error if muxTkDrvCheck( ) returns FALSE.

The Bind Phase

The network service must bind to a driver before it can send and receive packets through it. Binding to a network driver is accomplished by calling the muxTkBind( ) function (see B.2.16 muxTkBind( ), p.280).1

The protocol type supplied to the bind function is used by the MUX to prioritize the services, and determines which service sees which packets. A MUX_PROTO_SNARF type service sees all the packets that are processed by any driver to which it is bound. A MUX_PROTO_PROMISC type service sees a packet only after all other services bound to a driver have had a chance to consume it. A MUX_PROTO_OUTPUT type service sees outgoing rather than incoming packets. Any other type value configures the service to see only packets of the specified type.

After the bind operation has complete successfully, the sublayer should determine which (if any) registered service address mapping functions are relevant to the network interface. The sublayer should obtain references to those that apply and retain these references for later use within the interface.

Network Address Resolution Function

The address resolution function translates a network service address to a network driver (physical layer) address. To find the address resolution function that applies to a specific network service/network driver pair, use the muxAddrResFuncGet( ) function (see B.2.3 muxAddrResFuncGet( ), p.274).

Typically, the network service performs address resolution when it sends a packet to the network driver. The sublayer uses the address resolution function obtained in this step to resolve the address. If an address resolution function does not exist for this combination of service and driver, the network service is not expected to initiate the address resolution. This allows flexibility for those services that prefer to have the address resolution performed by the network driver.

If the address resolution mechanism qualifies as an address resolution protocol (for instance, ARP) or network service in itself, it should bind to the MUX as a distinct service. Typically, it would bind itself to the same interface to which the corresponding network service is bound. The address resolution mechanism could even share the same callback functions of the network service if it knows how to distinguish between the two services.

The network address resolution function also supports multicast mapping, and maps a network service multicast address to a network driver (physical layer) multicast address.

11.2.2   Data Structures and Resources

The network service sublayer may allocate buffer pools in the form of mBlk clusters for receiving and sending packets. The network buffer management library netBufLib facilitates the implementation of an effective scheme for this purpose. The sublayer may also make use of the system mBlk pools. See 10.1.5 Managing Memory for Network Drivers and Services, p.186, and A. Using netBufLib for more information on network buffer management.

Other resources commonly used by network services include receive/transmit queues and data structures that represent each active network interface controlled by the sublayer. Data that should be collected and maintained for each interface includes:

  • the network driver's paradigm
  • the state of the interface (for instance: attached, stopped, flow-controlled)
  • the cookie supplied by the MUX, this cookie identifies the binding instance
  • references to the service address mapping functions for the interface

11.2.3   Sublayer Routines

The subsections provides an overview of how a network service sublayer handles:

  • sending packets
  • receiving packets
  • shutting down an interface
  • reporting errors
  • flow control
  • device control

Sending Packets

Network layer packets are sent down through the MUX by using the muxTkSend( ) routine (see B.2.21 muxTkSend( ), p.284). Data to be sent arrives from an upper layer in the form of an mBlk chain, and is modified by your network service before being sent.

The muxTkSend( ) routine may return an error indicating that the driver is out of resources and cannot transmit the packet. A network service can use this error to establish a flow control mechanism (see Flow Control, p.230).

Sending Packets through an END

Sending packets through an END requires that the mBlk chain being sent contains fully formed physical layer frames. If necessary, a protocol must use the address resolution function registered for the interface to determine the destination address, and then use the muxAddressForm( ) routine to add the necessary frame header to the packet.

Sending Packets through an NPT driver

When sending packets through an NPT driver, if the interface over which packets are being sent has a registered address resolution function, it should be called at send-time and the resolved address should be passed into muxTkSend( ).

Receiving Packets

The MUX forwards incoming packets to the appropriate network service by invoking the stackRcvRtn( ) callback that was installed by the sublayer during the bind phase (see stackRcvRtn( ), p.231).

If a service binds to a network interface using muxTkBind( ), it typically receives packets (not frames) from the MUX. This is true whether the network interface is managed using an END or an NPT driver. If a service needs to receive both packets as well as the physical layer header, it can use the optional "piggy-back" facility provided for in the stackRcvRtn( ) argument list. The only absolute exception to this behavior occurs when the service binds to the MUX as a SNARF protocol. Such a service always receives frames not packets. If the service binds as a PROMISC protocol, it typically sees frames. However, it can see a packet if some other service that registered for packets does not consume its packet. If a service binds to an END using muxBind( ), the service always receives frames not packets.

If your stackRcvRtn( ) returns TRUE (except if your service is of type MUX_PROTO_PROMISC), the packet is consumed and will not be available to lower priority services listening to the same driver. If, on the other hand, your routine returns FALSE (or is of type MUX_PROTO_PROMISC), the packet will remain available to other services.

If your service has been registered as of type MUX_PROTO_OUTPUT, its stackRcvRtn( ) callback routine will be called for all packets going out over the driver to which your service is bound. If the stackRcvRtn( ) of such an output protocol returns TRUE, the packet is consumed by the protocol and will not go out over the driver. If it returns FALSE, the outgoing packet will continue on to the driver. Only one service at a time may bind with type MUX_PROTO_OUTPUT to any given driver.

Shutting Down an Interface

The MUX initiates a shutdown in response to a muxDevUnload( ) call from the system. Before unloading the network driver, the MUX issues a shutdown message to every network service bound to that driver by calling the stackShutdownRtn( ) callbacks that were registered for those driver/service interfaces at bind time (see stackShutdownRtn( ), p.231).

Within this shutdown routine, the network service must take the necessary steps to close the interface, including a call to muxUnbind( ) to unbind the network service from the device (see B.2.23 muxUnbind( ), p.285).

Error Reporting

Your network service may want to be notified of errors encountered in lower layers of the stack. Error conditions encountered by a network driver are passed up to the MUX. The MUX forwards each error to your network service sublayer if a stackErrorRtn( ) callback is registered at bind time (see stackErrorRtn( ), p.232).

Flow Control

The muxTkSend( ) routine may return an error, END_ERR_BLOCK, indicating that the network driver has insufficient resources to transmit data2 . The network service sublayer can use this feedback to establish a flow control mechanism by holding off on making any further calls to muxTkSend( ) until the device is ready to restart transmission. At that time, the MUX calls the stackRestartRtn( ) that you registered for the interface at bind time (see stackRestartRtn( ), p.232).

Device Control

A driver may be written to respond to specific ioctl commands. These commands can be issued from your network service by calling muxIoctl( ) (see B.2.12 muxIoctl( ), p.278).



11.3    Interfacing with the MUX

When a network service registers with the MUX, it must provide references to functions that the MUX can call to handle the following:

  • shutting down the network service
  • passing a packet into the service
  • passing a error message into the service
  • restarting the service after a pause

The prototypes of the functions you specify to handle these functions differ depending on whether you use muxTkBind( ) or muxBind( ) to bind the service to a network interface in the MUX. If you are implementing a new network service, you should use the muxTkBind( ) interface. This chapter includes the older muxBind( ) interface solely to assist people maintaining network services that were designed to work with ENDs before the development of the NPT.

11.3.1   Service Functions Registered Using muxTkBind( )

This section describes the four service functions referenced in a muxTkBind( ) call.

stackShutdownRtn( )

This routine is typically called by the MUX when it has received a call to muxDevUnload( ) for a specific network device. Before unloading the driver, every network service bound to that device is issued a shutdown message by calling the stackShutdownRtn( ) function registered for that service.

Within this routine, the network service must call muxUnbind( ) to release itself from the device, and it should bring itself to an orderly halt.

The stackShutdownRtn( ) prototype is:

STATUS stackShutdownRtn 
(
void * netCallbackId /* the handle/ID installed at bind time */
)
stackRcvRtn( )

The MUX forwards packets received by the driver to the protocol layer by using the stackRcvRtn( ) callback that was installed with muxTkBind( ). This routine receives:

  • a pointer to a mBlk chain containing the incoming packet

  • the callback ID specific to the binding instance of the service/driver pair

  • a network service type

  • a pointer to additional data, the format of which, and the need for which, depends on the requirements of the driver.

The stackRcvRtn( ) prototype is:

BOOL stackRcvRtn 
(
void * netCallbackId, /* the handle/ID installed at bind time */
long type, /* network service type */
M_BLK_ID pNetBuf, /* network service datagram */
void * pSpareData /* pointer to optional data from driver */
)

If a network protocol accepts the frame by returning TRUE, it must free the given mBlk chain when processing is complete.

stackErrorRtn( )

Error conditions encountered by an driver are passed to the network service when the MUX calls stackErrorRtn( ). It is up to the network service to take the necessary action upon receiving the error.

This function takes two arguments: the callback identifier supplied to the service at bind-time, and a pointer to an END_ERR structure that describes the error (see B.3.2 END_ERR, p.287).

The stackErrorRtn( ) prototype is:

void stackErrorRtn 
(
void * netCallbackId, /* the handle/ID installed at bind time */
END_ERR * pError /* pointer to structure containing error */
)
stackRestartRtn( )

This routine is called by the MUX to restart network services that had previously stopped, perhaps because muxTkSend( ) had returned an error indicating that the network service should wait before transmitting more packets.

When the device has determined that it has enough resources to resume transmission, it will indicate this to the MUX, which will then call stackRestartRtn( ).

This function takes a single argument: the identifier specific to the service/driver pair that was supplied at bind-time.

The stackRestartRtn( ) prototype is:

STATUS stackRestartRtn 
(
void * netCallbackId /* the handle/ID installed at bind time */
)

11.3.2   Service Functions Registered Using muxBind( )

These function prototypes are included in this chapter to help people maintaining network services that were designed to work with muxBind( ), which predated the NPT. If you are designing a new network service, implement the routines associated with muxTkBind( ).

stackENDShutdownRtn( )

This routine is typically called by the MUX when it has received a call to muxDevUnload( ) for the specified network interface. Before unloading the END, every network service bound to that END's device is issued a shutdown message by calling the stackENDShutdownRtn( ) function registered for the interface at bind time.

Within this routine, the network service must call muxUnbind( ) to release itself from the device, and it should bring itself to an orderly halt.

The stackENDShutdownRtn( ) prototype is:

STATUS stackENDShutdownRtn 
(
void * pEND,    /* END_OBJ from the driver's load routine */
void * pSpare /* defined on a per-service basis */
)
stackENDRcvRtn( )

The MUX forwards packets received by the END to the protocol layer by using the stackENDRcvRtn( ) callback that is installed with muxBind( ). The pNetBuff contains the entire driver-level frame, and pLinkHdr contains information about offsets to the network payload in the frame.

The stackENDRcvRtn( ) prototype is:

BOOL stackENDRcvRtn 
(
void * pCookie,  /* returned by muxBind() */
long type,  /* from RFC1700, or user-defined */
M_BLK_ID pNetBuff,  /* packet with link-level info */
LL_HDR_INFO * pLinkHdr,  /* link-level header info structure */
void * pCallbackId /* registered by the network svc with MUX */
)

If a network protocol accepts the frame by returning TRUE, it must free the given mBlk chain when processing is complete.

stackENDErrorRtn( )

Error conditions encountered by an END are passed to the network service when the MUX calls stackENDErrorRtn( ). It is up to the network service to take the necessary action upon receiving the error.

This function takes three arguments: the callback identifier supplied to the service at bind-time, a pointer to an END_ERR structure, and the spare data, if any, defined for the service during the bind phase.

The stackENDErrorRtn( ) prototype is:

void stackENDErrorRtn 
(
void *    pEND,    /* END_OBJ passed to the MUX by the driver */
END_ERR * pError, /* holds error information */
void * pSpare /* defined on a per-service basis */
)
stackENDRestartRtn( )

This routine is called by the MUX to restart network services that had previously stopped, perhaps because muxTkSend( ) had returned an error indicating that the network service should wait before transmitting more packets.

When the device has determined that it has enough resources to resume transmission, it will indicate this to the MUX, which will then call stackENDRestartRtn( ).

This function takes a single argument: the identifier specific to the service/driver pair that was supplied at bind-time.

The stackENDRestartRtn( ) prototype is:

STATUS stackENDRestartRtn 
(
void * pEND,    /* END_OBJ passed to the MUX by the driver */
void * pSpare,   /* defined on a per-service basis */
)


11.4    Adding a Socket Interface to Your Service

One way to give applications easy access to your network service is to add socket support to the service. In order to make it easier for you to write a network service that includes sockets support, the VxWorks stack includes a standard socket interface.

With the standard socket interface, you can add new socket back ends to access the network stack through your protocol layer implementation. This allows developers who are already familiar with the standard socket API to more easily use your service.

The standard socket interface is designed so that you can use socket back ends simultaneously for multiple protocol layer implementations. A layered architecture makes this possible. The Wind River standard socket interface is a layer above your back end socket layer, as shown in Figure 11-1.

This chapter introduces the process of implementing a socket back end.

Figure 11-1 :   The Standard Socket Interface

Process Overview

Socket calls made by an application are directed to the correct underlying socket back end based on the domain parameter that is passed to the socket( ) call when the application creates the socket. If this parameter matches the domainMap parameter that you use when you add your new socket back end with the sockLibAdd( ) routine, the socket calls are directed to your back end.

When you register your socket back end, you give the system a table that is filled with references to socket functions that you have created to support your implementation (see The Socket Functional Interface, p.236). The system is then able to support standard socket calls that are made using your back end.

11.4.1   Implementing a Socket Back End

To provide a socket back end requires that you implement socket functionality for your service and that you make the system aware of this new sockets implementation. The following sections show how this is done.

The Socket Functional Interface

The socket functional interface is the set of implementations of standard socket functions that are supported by a particular socket back end. There are two steps involved in creating a socket functional interface, both of which must be completed before the network is initialized.

The first step in creating a socket functional interface is to create a unique constant identifying the back end (for example, the BSD-specific INET back end is identified by the constant AF_INET_BSD). Add this constant to the list found in /target/h/sys/socket.h.

Then, create an initialization function that returns a reference to a SOCK_FUNC table filled with references to all of the functions that your socket back end will support, and call sockLibAdd( ) to have this function invoked by the system. (For details about this initialization function, see usrSockLibInit( ), p.239).


*      
NOTE: You are not required to support all of the possible socket functions. A call made by a user application to a non-supported function in your socket back end (indicated by a NULL pointer in the SOCK_FUNC table) results in an error returned to the calling application, with errno set to ENOTSUP.

Populating the SOCK_FUNC Table

The SOCK_FUNC table is a structure containing references to 19 implementations of functions common to sockets -- functions such as bind( ), recvfrom( ), and setsockopt( ) (see 11.4.3 Implementing Socket Functions, p.238). A new network service that wants to be socket-accessible should implement service-specific versions of as many of these functions as it intends to support. To support the registration of these functions, the service should set up its usrSockLibInit( ) routine to return a SOCK_FUNC table that is populated with references to these functions.

Adding the Socket Library

Use the sockLibAdd( ) function to add new socket functionality to the system's list of socket implementations. As input, this function expects a reference to the usrSockLibInit( ) function and the domain and service for which this socket implementation is to be registered.

The sockLibAdd( ) Function

The sockLibAdd( ) function is used to make available a specific implementation of sockets for a particular domain. This function takes three parameters:

sockLibInitRtn

domainMap

domainReal

The following is an example of how sockLibAdd( ) might be called to add the BSD sockets back end:

sockLibAdd ((FUNCPTR) bsdSockLibInit, AF_INET_BSD, AF_INET);

The sockLibAdd( ) function is defined as follows:

STATUS sockLibAdd 
(
FUNCPTR sockLibInitRtn, /* back end's initialization routine */
int domainMap,       /* AF_FOO_BAR, identifying # of back end */
int domainReal,      /* AF_FOO, identifying # of domain */
)

The function returns OK, or ERROR if the socket back end could not be added.

11.4.2   Enabling Zbuf Support Within a Socket Back End

Zbufs (zero-copy buffers) are an enhancement that reduces the overhead involved in copying data between buffers as it passes through the layers of a network stack. A socket back end does not have to support zbufs, but may achieve significant performance gains by doing so.

The Wind River implementation of zbufs relies on the flags parameter in the socket send and receive routines. One of the flags that may be set in this parameter is MSG_MBUF. If this flag is set, this indicates that the data is in zbuf format -- in other words, the buffer is not a true buffer, but a pointer to an mbuf chain.


*      
WARNING: Zbuf sockets can only operate within a single protection domain. You may not pass data from one protection domain to another using zbuf sockets.

Your socket back end must implement the function usrSockZbufRtn( ) which indicates whether the back end supports zbufs.

If your socket back end has been written to support zbufs, it should check for the MSG_MBUF flag, and if it is present, it should treat the buffers as mbuf chains. See the zbuf section of 7. Sockets under VxWorks for a more complete description of zero-copy buffers.

If your socket back end has not been written to support zbufs, then applications that try to use zbufs with your socket back end will fail with errno set to ENOTSUP. However, these applications will be able to use the sockets interface without zbufs.

11.4.3   Implementing Socket Functions

This subsection provides implementation recommendations of the functions referenced in a SOCK_FUNC table followed by implementation recommendations to the functions referenced in a iosDrvInstall( ) call.

Implementation Recommendations for the Elements of a SOCK_FUNC Table

This subsection provides implementation details for the functions you must supply in a SOCK_FUNC structure:

typedef struct sockFunc                 /* SOCK_FUNC */ 
{
    FUNCPTR     libInitRtn;             /* sockLibInit()        */
    FUNCPTR     acceptRtn;              /* accept()             */
    FUNCPTR     bindRtn;                /* bind()               */
    FUNCPTR     connectRtn;             /* connect()            */
    FUNCPTR     connectWithTimeoutRtn;  /* connectWithTimeout() */
    FUNCPTR     getpeernameRtn;         /* getpeername()        */
    FUNCPTR     getsocknameRtn;         /* getsockname()        */
    FUNCPTR     listenRtn;              /* listen()             */
    FUNCPTR     recvRtn;                /* recv()               */
    FUNCPTR     recvfromRtn;            /* recvfrom()           */
    FUNCPTR     recvmsgRtn;             /* recvmsg()            */
    FUNCPTR     sendRtn;                /* send()               */
    FUNCPTR     sendtoRtn;              /* sendto()             */
    FUNCPTR     sendmsgRtn;             /* sendmsg()            */
    FUNCPTR     shutdownRtn;            /* shutdown()           */
    FUNCPTR     socketRtn;              /* socket()             */
    FUNCPTR     getsockoptRtn;          /* getsockopt()         */
    FUNCPTR     setsockoptRtn;          /* setsockopt()         */
    FUNCPTR     zbufRtn;                /* ZBUF support         */
    } SOCK_FUNC;
usrSockLibInit( )

The usrSockLibInit( ) function should install the socket back end as a driver within the VxWorks I/O system by calling iosDrvInstall( ), and then should return a pointer to a SOCK_FUNC structure.

The iosDrvInstall( ) routine takes pointers to seven I/O functions, four of which must be supported by a socket back end: usrSockClose( ), usrSockRead( ), usrSockWrite( ) and usrSockIoctl( )3 . This routine returns a driver number, which should be stored by the socket back end.

Your usrSockLibInit( ) routine, which should be declared public, is based on the following skeleton:

SOCK_FUNC * usrSockLibInit (void) 
{
/* install driver for socket */
int driverNum = iosDrvInstall( (FUNCPTR) NULL, (FUNCPTR) NULL,
                   (FUNCPTR) NULL, (FUNCPTR) usrSockClose,
                   (FUNCPTR) usrSockRead, (FUNCPTR) usrSockWrite),
                   (FUNCPTR) usrSockIoctl) );
if( driverNum == ERROR ) return( (SOCK_FUNC *) NULL );
/* Store driverNum somewhere convnient for future reference */
/* Initialize SOCK_FUNC table */
SOCK_FUNC * usrSockFuncs = (SOCK_FUNC *) malloc(sizeof(SOCK_FUNC));
if( !usrSockFuncs )
{
   errno = ENOMEM;
   return( (SOCK_FUNC *) NULL );
}
usrSockFuncs->libInitRtn    = (FUNCPTR) usrSockLibInit;
usrSockFuncs->acceptRtn     = (FUNCPTR) usrSockAccept;
/* and so forth... */
usrSockFuncs->setsockoptRtn = (FUNCPTR) usrSockSetSockOpt;
usrSockFuncs->zbufRtn       = (FUNCPTR) usrSockZbufRtn;
return (usrSockFuncs);
}
usrSocket( )

When a socket( ) call is issued, the standard socket interface searches for a back end that corresponds to the domain parameter passed to the socket( ) routine. This domain parameter may be the actual domain name, domainReal. Alternatively, it could be a domain name that maps to the actual domain, domainMap. A back end is registered (using sockLibAdd( )) both with its actual domain name (domainReal) and with domainMap.

If a back end is found for the domain, the usrSocket( ) function from the SOCK_FUNC structure that was registered for that domain is called. This function is called with the real socket domain (domainReal) passed as the domain parameter, regardless of whether the domainReal or domainMap parameters was passed to the original socket( ) call. The type and protocol entries are passed unchanged.

The usrSocket( ) routine should create a socket structure and then generate a new file descriptor representing the socket by calling iosFdNew( ) with the address of the new socket structure. Other back end functions will receive this file descriptor as a reference, and will use it to retrieve the associated socket structure by calling iosFdValue( ) with the file descriptor as an argument.

The usrSocket( ) routine is of the form:

int usrSocket 
(
int domain, /* socket domain or address family number */
int type, /* used to further define socket's nature */
int protocol /* the protocol variety of the socket */
)

The domain argument refers to the socket domain or address family the socket belongs to (a particular back end may potentially be invoked for more than one domain). The type argument can be used to further define the nature of the socket (examples of types in the AF_INET domain include SOCK_STREAM, SOCK_RAW and SOCK_DGRAM). The protocol argument refers to the protocol variety of the socket (in the AF_INET_BSD back end, an example of a protocol variety is IPPROTO_TCP).

The usrSocket( ) function returns the file descriptor that was generated for the socket, or ERROR if it was unable to open a socket.

usrSockAccept( )

This routine accepts a connection on a socket and returns a file descriptor representing the new socket created for the connection. Typically for this function to succeed, the socket represented by fd must have been previously bound to an address with usrSockBind( ) and enabled for connections by a call to usrSockListen( ). When usrSockAccept( ) is called, addr should be an available buffer, and addrlen should indicate the size of the buffer.

The usrSockAccept( ) function will block the caller until a connection is present, unless the socket has been explicitly marked as non-blocking.

The usrSockAccept( ) routine is of the form:

int usrSockAccept 
(
int fd, /* file descriptor for socket */
struct sockaddr * addr, /* network address */
int * addrlen /* length of address structure */
)

This function returns a file descriptor representing a new socket with the same properties as the one represented by fd (or ERROR if the accept fails). In addition, on a successful return, addr should be filled with the address of the machine making the connection, and addrlen should be set to the length of that address.

usrSockBind( )

This routine associates a network address (referred to by name) with a specified socket so that other processes can connect or send to it.

The usrSockBind( ) routine is of the form:

STATUS usrSockBind 
(
int fd, /* file descriptor representing socket */
struct sockaddr * name, /* network address */
int namelen /* length of network address */
)

This function returns OK, or ERROR if the bind fails.

usrSockConnect( )

This routine initiates a connection between a socket, fd, and another socket which is specified by name.

The usrSockConnect( ) routine is of the form:

STATUS usrSockConnect 
(
int fd, /* file descriptor representing socket */
struct sockaddr * name, /* network address */
int namelen /* length of network address */
)

This function returns OK, or ERROR if the connection fails.

usrSockConnectWithTimeout( )

This routine attempts to initiate a connection between a socket, fd, and another socket specified by name, for a duration specified by timeout, reporting an error if it cannot do so in the time required. If timeout is NULL, this function should act exactly like usrSockConnect( ).

The usrSockConnectWithTimeout( ) routine is of the form:

STATUS usrSockConnectWithTimeout 
(
int fd, /* file descriptor representing socket */
struct sockadr * name, /* network address */
int namelen, /* length of address */
struct timeval * timeout /* maximum duration of connect attempt */
)

This function returns OK, or ERROR if it cannot make the connection in the specified time.

usrSockGetpeername( )

This routine gets the name of the peer connected to the socket fd, placing this name in the sockaddr structure of length namelen that was passed in.

The usrSockGetpeername( ) routine is of the form:

STATUS usrSockGetpeername 
(
int fd, /* file descriptor representing socket */
struct sockaddr * name, /* structure to hold peer name */
int * namelen /* length of returned peer name */
)

This function should place the name of the peer in name and set namelen to the size of this name in bytes, then return OK, or ERROR if a name could not be retrieved for the specified socket.

usrSockGetsockname( )

This routine gets the current name for the socket fd, placing this name in the available sockaddr structure of size namelen that was passed in.

The usrSockGetsockname( ) routine is of the form:

STATUS usrSockGetsockname 
(
int fd, /* file descriptor representing socket */
struct sockaddr * name, /* structure to hold socket name */
int * namelen /* length of returned socket name */
)

This function places the name of the socket in name and set namelen to the size of this name in bytes, then returns OK, or ERROR if a name could not be retrieved for the specified socket.

usrSockListen( )

This routine enables connections to a socket. The backlog parameter specifies the maximum number of unaccepted connections that can be pending at any given time. After enabling connections with usrSockListen( ), connections are actually accepted by usrSockAccept( ).

The usrSockListen( ) routine is of the form:

STATUS usrSockListen 
(
int fd, /* file descriptor representing the socket */
int backlog /* max number of unaccepted pending connections */
)

This function returns OK, or ERROR if the listen request fails.

usrSockRecv( )

This routine receives data from a connection-based (stream) socket. How the flags parameter should be set depends on the nature of the sockets involved and the requirements of the connection. This will differ for different socket and service implementations.

The usrSockRecv( ) routine is of the form:

int usrSockRecv 
(
int     fd, /* file descriptor represents the socket */
char *  buf, /* buffer holding the received data */
int     bufLen, /* length of the buffer */
int     flags, /* flags describe the nature of the data */
)


*      
NOTE: If the MSG_MBUF flag is set in the flags parameter, this means that a zero-copy buffer (zbuf) is being received. In this case, the buf parameter is a pointer to a NULL mbuf pointer and not the char * specified in the parameter list. In other words, *buf == (struct mbuf *) NULL. The usrSockRecv( ) routine should set buf to point to the mbuf chain holding the incoming data.

This function returns the number of bytes received, or ERROR if the receive fails.

usrSockRecvFrom( )

This routine receives data from a datagram socket, regardless of whether it is connected. When this function is called, from will either be NULL or will be an available buffer of size pFromLen designed to hold the address from which the data is coming. How the flags parameter should be set depends on the nature of the sockets involved and the requirements of the connection. This will differ for different socket and service implementations.

The usrSockRecvFrom( ) routine is of the form:

int usrSockRecvFrom 
(
int fd, /* file descriptor represents the socket */
char * buf, /* buffer holding the received data */
int bufLen, /* length of the buffer */
int flags, /* flags describe the nature of the data */
struct sockaddr * from, /* address of the sending agent */
int * pFromLen /* length of the from structure */
)


*      
NOTE: If the MSG_MBUF flag is set in the flags parameter, this means that a zero-copy buffer (zbuf) is being received. In this case, the buf parameter is a pointer to a NULL mbuf pointer and not the char * specified in the parameter list. In other words, *buf == (struct mbuf *) NULL. The usrSockRecvFrom( ) routine should set buf to point to the mbuf chain holding the incoming data.

If from is not NULL, the address of the sending socket is copied into it, and pFromLen is set to the length of this address. This function returns the number of bytes received, or ERROR if the receive fails.

usrSockRecvMsg( )

This routine receives a message from a datagram socket. It may be used in place of usrSockRecvFrom( ) to decrease the overhead of breaking down the message-header structure in each message. How the flags parameter should be set depends on the nature of the sockets involved and the requirements of the connection. This will differ for different socket and service implementations.

The usrSockRecvMsg( ) routine is of the form:

int usrSockRecvMsg 
(
int fd, /* file descriptor for the socket */
struct msghdr * pMsgHdr, /* the message header */
int flags /* flags describing nature of the data */
)

This function returns the number of bytes received, or ERROR if the receive fails.

usrSockSend( )

This routine transmits data from the socket fd to a previously-established connection-based (stream) socket. How the flags parameter should be set depends on the nature of the sockets involved and the requirements of the connection. This will differ for different socket and service implementations.

The usrSockSend( ) routine is of the form:

int usrSockSend 
(
int fd, /* file descriptor representing the socket */
char * buf, /* buffer containing data to be sent */
int bufLen, /* length of the buffer */
int flags /* flags describing the nature of the data */
)


*      
NOTE: If the MSG_MBUF flag is set in the flags parameter, this means that a zero-copy buffer (zbuf) is being sent. In this case, the buf parameter is a pointer to a NULL mbuf pointer and not the char * specified in the parameter list. In other words, *buf == (struct mbuf *) NULL. The usrSockSend( ) routine should set buf to point to the mbuf chain holding the data.

This function returns the number of bytes sent, or ERROR if the send fails.

usrSockSendto( )

This routine transmits data from the socket specified by fd to the datagram socket specified by to. How the flags parameter should be set depends on the nature of the sockets involved and the requirements of the connection. This will differ for different socket and service implementations.

The usrSockSendto( ) routine is of the form:

int usrSockSendto 
(
int fd, /* file descriptor representing the socket */
caddr_t buf, /* buffer containing message to be sent */
int bufLen, /* length of this buffer */
int flags, /* flags describing the nature of the data */
struct sockaddr * to, /* recipient's address */
int tolen /* length of the to structure */
)


*      
NOTE: If the MSG_MBUF flag is set in the flags parameter, this means that a zero-copy buffer (zbuf) is being sent. In this case, the buf parameter is a pointer to a NULL mbuf pointer and not the char * specified in the parameter list. In other words, *buf == (struct mbuf *) NULL. The usrSockSendto( ) routine should set buf to point to the mbuf chain holding the data.

This function returns the number of bytes sent, or ERROR if the send fails.

usrSockSendMsg( )

This routine transmits a message to a datagram socket specified by fd. It may be used in place of usrSockSendto( ) to decrease the overhead of reconstructing the message header structure for each message. How the flags parameter should be set depends on the nature of the sockets involved and the requirements of the connection. This will differ for different socket and service implementations.

The usrSockSendMsg( ) routine is of the form:

int usrSockSendMsg 
(
int fd, /* file descriptor for the socket */
struct msghdr * pMsgHdr, /* message header */
int flags /* flags describing nature of the data */
)

This function returns the number of bytes sent, or ERROR if the send fails.

usrSockShutdown( )

This routine shuts down all, or part, of the connection-based socket fd. The how value allows for some control over how this shutdown takes place if sends and receives are still pending.

The usrSockShutdown( ) routine is of the form:

STATUS usrSockShutdown 
(
int fd, /* file descriptor representing the socket */
int how /* directs how shutdown proceeds when activity is pending */
)

This function returns OK, or ERROR if the specified socket was invalid or could not be shut down.

usrGetSockOpt( )

This routine retrieves socket option values4 associated with a specified socket. To find options set at the socket level, level is set to SOL_SOCKET. To find options set for a particular service, level is set to the identifying number of that service. The optval parameter points to an available buffer of size optlen. The buffer itself, although passed in as a char *, is treated as a pointer to whatever data type or structure is appropriate to the option being referenced.

The usrGetSockOpt( ) routine is of the form:

STATUS usrGetSockOpt 
(
int fd, /* file descriptor representing the socket */
int level, /* scope of option being retrieved */
int optname, /* name of option being retrieved */
char * optval, /* holds the value of the option */
int * optlen /* indicates the length of optval */
)

This function fills optval with the setting of the specified option, and sets optlen to the actual size of this value. The function returns OK, or ERROR if it was unable to retrieve a value for this option given these parameters.

usrSetSockOpt( )

This routine sets the options associated with a socket5 . To manipulate options at the socket level, level is set to SOL_SOCKET. Otherwise, level is set to the service number of the service for which the option is being set.

The usrSetSockOpt( ) routine is of the form:

STATUS usrSetSockOpt 
(
int fd, /* file descriptor representing the socket */
int level, /* scope of option being set */
int optname, /* name of option being set */
char * optval, /* value the option is being set to */
int optlen /* length of the value field */
)

This function returns OK, or ERROR if the request to set the socket option for the specified socket fails.

usrSockZbufRtn( )

This routine returns TRUE if the back end supports the zero-copy interface (zbufs), otherwise it returns FALSE. This function is of the form:

STATUS usrSockZbufRtn()

Socket Functions Passed to iosDrvInstall( )

An iosDrvInstall( ) call expects pointers to function to handle:

  • closing the socket
  • reading the socket
  • writing to the socket
  • I/O control for the socket
usrSockClose( )

This routine is called by the I/O system to close a socket.

The usrSockClose( ) routine is of the form:

int usrSockClose 
(
[socket structure] * so /* socket being closed */
)

The socket structure corresponds to whatever data structure describes the socket object that was returned from iosFdValue( ). This might be a struct socket, or it may be some other structure.

This function returns 0 on success, or -1 on failure.

usrSockRead( )

The usrSockRead( ) routine is of the form:

int usrSockRead 
(
[socket structure] *  so, /* socket being read from */
char * buf, /* buffer to contain incoming data */
int bufLen /* length of this buffer */
)

The socket structure corresponds to whatever data structure describes the socket object that was returned from iosFdValue( ). This might be a struct socket, or it may be some other structure.

This function returns the number of bytes read, or -1 if the read fails.

usrSockWrite( )

The usrSockWrite( ) routine is of the form:

int usrSockWrite 
(
[socket structure] *  so, /* socket being written to */
char * buf, /* buffer containing outgoing data */
int bufLen /* length of this buffer */
)

The socket structure corresponds to whatever data structure describes the socket object that was returned from iosFdValue( ). This might be a struct socket, or it may be some other structure.

This function returns the number of bytes written, or -1 if the write fails.

usrSockIoctl( )

The usrSockIoctl( ) routine is of the form:

int usrSockIoctl 
(
int fd, /* file descriptor representing the socket */
int function, /* the ioctl function being called */
int arg /* the argument to this ioctl function */
)

This function returns a positive number whose value depends on the ioctl function being invoked, or -1 in the case of an error.


1:  The muxBind( ) function may also be used, but only with ENDs. The muxBind( ) function also requires you to implement a slightly different set of network service sublayer functions than those that are used with muxTkBind( ).

2:  Some less-carefully written drivers may simply return ERROR in this case, and it would not be possible for your service to determine whether this was due to a temporary problem such as insufficient resources or a more serious problem.

3:  The others may be skipped as NULL pointers. For more information on these essential functions, see Socket Functions Passed to iosDrvInstall( ), p.248.

4:  For instance, in TCP, socket options include SO_KEEPALIVE, SO_LINGER and TCP_NODELAY.

5:  For instance, in UDP, socket options include SO_BROADCAST, IP_ADD_MEMBERSHIP and IP_MULTICAST_IF.