TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
unix interprocess communication
1. POSIX IPC
Following three IPC mechanism are collectively known as posix ipc
o POSIX Message queue
o POSIX Semaphores
o POSIX Shared memory
They share some similarity in the functions that access them, and in the information that
describes them.
Following are the common properties
The pathnames used for identification
The flags specified when opening or creating
Access permissions
Message Semaphores Shared
Queue Memory
Header <mqueue.h> <semaphore.h> <sys/mman.h>
Functions to mq_open sem_open shm_open
create, open, or mq_close sem_close shm_unlink
delete mq_unlink sem_unlink
sem_init
sem_destroy
Functions for mq_getattr ftruncate
control mq_setattr fstat
operations
Functions for mq_send sem_wait mmap
IPC operations mq_receive sem_trywait munmap
mq_notify sem_post
sem_getvalue
POSIX IPC functions
IPC Names: The first argument to open functions above three PIC technique must be an
identification name, which may or mayn’t be real path name in a file system.
Creating and Opening IPC channels
The three functions that create or open an IPC object, mq_open, sem_open and
shm_open, all take a second argument named oflag that specifies how to open the
requested object.
The various constants that can be used to form this argument are:
Description mq_open sem_open shm_open
Read-only O_RDONLY O_RDONLY
Write-only O_WRONLY
Read- write O_RDWR O_RDWR
1
2. Create if it does not already O_CREAT O_CREAT O_CREAT
exist O_EXCL O_EXCL O_EXCL
exclusive create
Nonblocking mode O_NONBLOCK
Truncate if it already exists O_TRUNC
A message queue can be opened in any of the three modes: read-only, write-only and
read-write.
Read and write access both are required for any semaphore operation so it is default for
semaphore.
A shared memory object cannot be write only.
Remaining flags are optional
O_CREAT – Creates the message queue, semaphore, or shared memory object if it
doesnot already exist.
O_EXCL – if this flag and O_CREAT are both specified, then the function creates a new
message queue, semaphore, or shared memory object only if it doesnot already exist. If it
already exist, an error of EEXIST is returned.
Note: The check for the existence of an IPC and its creation must be an atomic operation.
O_NONBLOCK- This flag makes a message queue nonblocking with regard to a read on
an empty queue or a write to a full queue.
O_TRUC- if an existing shared memory object is opened read-write, this flag specifies
that the object be truncated to 0 length.
2
3. System tables full?
New object
is created
does object O_CREAT set?
already exist
Are both O_CREAT
and O_EXEL set?
Existin
object
is ref.
Are the access
permission OK?
IPC Permissions
When creating new message queue, semaphore, or shared memory object at least one
additional argument is required, called mode.
This argument specifies the permission bits and is formed as the bitwise – OR of the
constants.
Constant Description
S_ISUSR User read
S_IWUSR User write
S_IRGRP Group read
S_IWGRP Group write
S_IROTH Other read
S_IWOTH Other write
These constants are defined in <sys/stat.h> header.
Whenever an access to IPC object is requested following testing is performed
• If the effective user ID of the process is 0, access is allowed.
3
4. • If the effective user ID of the process equals the owner id of the IPC object: if the
appropriate user access permission bit it set, access is allowed, else access is
denied.
• If the effective group ID of the process or one of the supplemenatary group IDs of
the process equals the group ID of the IPC object: if the appropriate group access
permission bit is set, access is allowed, else permission is denied.
• If the appropriate other access permission bit is set, access is allowed, else
permission is denied.
These four steps are tried in sequence in the order listed. Therefore, if the process
own the IPC object, then access is granted or denied based only on the user access
permissions – the group permissions are never considered. Similarly, if the process
does not own the IPC object, but the process belongs to an appropriate group, then
access is granted or denied based only on the group access permissions – the other
permissions are not considered.
POSIX Message queue
A message queue canbe thought of as a linked list of messages. Thread with adequate
permission can put messages onto the queue, and threads with adequate permission
can remove message from the queue. Each message is assigned some priority.
No requirement exist that someone be waiting for a message to arrive on a queue
before some process writes a message to that queue. This is in contrast to Pipes and
FIFOs.
A process can write some messages to a queue, terminate and have the messages read
by another process at a later time.
Message queues have kernel persistence in contrast to pipes and fifo, which are only
process persistent. Any data remaining in a pipe or FIFO when the last close of the
pipe or FIFO takes place, is discarded.
POSIX MQ vs System V MQ
A read on a Posix message queue always returns the oldest message of the highest
priority, where as a read on a system V message queue can return a message of any
desired priority.
Posix message queue allow the generation of a signal or the notification of a thread
when a message is placed onto an empty queue, whereas nothing similar is provided
by system V message queue.
Every message on a message queue has following attributes
• A unsigned integer priority
• The length of the data portion of the message
• The data itself
The POSIX message queue functions are:
4
5. mq_open() – Creates a new message queue or opens an existing message queue.
#include<mqueue.h>
mqd_t mq_open(const char *name, int oflag, . . .
/* mode_t mode, struct mq_attr *attr */ );
name – Posix IPC name usually unix path name.
oflag – one or more flag constants described before.
mode & attr – required when new queue is created.
mq_close() -- Ends the connection to an open message queue.
mq_unlink() -- Ends the connection to an open message queue and causes the queue
to be removed when the last process closes it.
Message queue have a reference count of how many times they are currently open.
mq_unlink call decrements ref count by 1 and causes a queue to be removed from the
system when this ref count becomes zero.
mq_send() -- Places a message in the queue.
mq_receive() -- Receives (removes) the oldest, highest priority message from the
queue.
mq_notify() -- Notifies a process or thread that a message is available in the queue.
mq_setattr() -- Set or get message queue attributes.
IPC: Semaphore
Semaphores are a programming construct designed by E. W. Dijkstra in the late 1960s.
A semaphore appears to be a simple integer. A process (or a thread) waits for permission
to proceed by waiting for the integer to become 0. The signal if it proceeds signals that
this by performing incrementing the integer by 1. When it is finished, the process changes
the semaphore's value by subtracting one from it.
5
6. Posix semaphores need not to be maintained in the kernel. Also, Posix semaphores are
identified by names that might correspond to pathnames in the file system.
PROCESS A PROCESS B
Function to
create, wait and
process
post to
semaphore
kernel
Binary semaphore is a
File system
file whose contents are
0/1
Three basic operations that a process can perform on a semaphore
Create a semaphore - This also requires a caller to specify the initial value which for
a binary semaphore is often 1 but can be 0.
Wait for a semaphore – This tests the value of the semaphore, waits if the value is less
than or equal to 0, and then decrements the semaphore value once it is greater than 0.
This can be summarized by the pseudocode.
while(semaphore_value<=0)
; /* wait; i.e. block the tread or process*/
semaphore_value--;
Post to a semaphore. This increments the value of the semaphore and can be
summarized by the pseudocode
semaphore_value ++;
if any processes are blocked, waiting for this semaphores value to become greater
than 0, one of the processes can now be awoken.
There are other common names for this operation: up, unlock and signal.
Common Posix Semaphore functions
sem_open() -- Connects to, and optionally creates, a named semaphore
sem_close() -- Ends the connection to an open semaphore.
6
7. sem_unlink() -- Ends the connection to an open semaphore and causes the
semaphore to be removed when the last process closes it.
sem_destroy() -- Initializes a semaphore structure (internal to the calling program,
so not a named semaphore).
sem_wait(), sem_trywait() -- Blocks while the semaphore is held by other
processes or returns an error if the semaphore is held by another process.
sem_post() -- Increments the count of the semaphore.
IPC: Message Queue
The basic idea of a message queue is a simple one.
two (or more) processes can exchange information via access to a common system
message queue. The sending process places via some (OS) message-passing module a
message onto a queue which can be read by another process (Figure 24.1). Each message
is given an identification or type so that processes can select the appropriate message.
POSIX messages
The POSIX message queue functions are:
mq_open() -- Connects to, and optionally creates, a named message queue.
mq_close() -- Ends the connection to an open message queue.
mq_unlink() -- Ends the connection to an open message queue and causes the queue
to be removed when the last process closes it.
mq_send() -- Places a message in the queue.
mq_receive() -- Receives (removes) the oldest, highest priority message from the
queue.
mq_notify() -- Notifies a process or thread that a message is available in the queue.
mq_setattr() -- Set or get message queue attributes.
7
8. Shared Memory IPC
Shared is fastest form of IPC available. Once the memory is mapped into the address
space of the processes that are sharing the memory region, no kernel involvement occurs
in passing data between the processes.
Synchronization is required between processes between processes that are storing and
fetching information to and from the shared memory region.
we discussed various form of synchronization : mutexes, condition variables, locks and
semaphores.
Common steps in using shared memory
The server gets access to a shared memory object using a semaphore.
The server reads from the input file into the shared memory object. The second
argument to the read, the address space of the data buffer, points to the shared
memory object.
When the read is complete, the server notifies the client, using a semaphore.
The client writes the data from shared memory object to the o/p file.
Common function for shared memory IPC
shm_open – specifies a name argument (unix pathname) , to either create a new shared
memory object or to open an existing shared memory object.
mmap – maps either a file or a Posix shared memory object into the address space of a
process. It is defined in <sys/mman.h>
munmap – removes a mapping from the address space of the process.
msync – the kernel’s virtual memory algorithm keeps the memory mapped file (on disk)
synchronized with the memory mapped region in memory. This function makes certain
that both copies synchronized.
shm_unlink – removes the name of a shared memory object.
ftruncate – to specify the size of a newly created shared memory object or to change the
size of an existing object.
fstat - when we open an existing shared memory object,we call fstat to obtain
information about the object.
System V IPC
8
9. Three types of IPC
• System V message queue
• System V semaphores
• System V shared memory
are collectively known as “System V IPC”. They share many similarities in the functions
that access them, and in the information that the kernel maintains on them.
Message Semaphores Shared
Queue memory
Header <sys/msg.h> <sys/sem.h> <sys/shm.h>
Function to create or msgget semget shmget
open
Functions for msgctl semctl shmctl
control operation
Functions for IPC msgsnd semop shmat
operations msgrcv shmdt
System V IPC Name:
The there types of IPC are noted as using key_t values for their names. The header
<sys/types.h> defines the key_t datatype, as an integer, normally at least a 32-bit integer.
These integer values are normally assigned by the ftok function.
The function ftok converts an existing pathname and an integer identifier into a
key_t value.
#include<sys/ipc.h>
key_t ftok (const char *pathname, int id);
returns: IPC key if OK, -1 on error
This function takes information derived from the pathname and the low order 8 bits of id
and combines them into an integer IPC key.
Here, the id argument is used to create multiple IPC channels of same path name. For
example, if client and server need two IPC channels say one from client to server and one
from server to client, then one channel can use an id of one and another can use id of two.
• Typical implementations of ftok call the stat function and then combines
• Information about the file system on which pathname resides
• The file’i-node number within the file system
• The low order 8 bits of the id.
The combination of these three values normally produces a 32-bit key.
ipc_perm structure
The kernel maintains a structure of information for each IPC object, similar to the
information it maintains for files
#include<sys/ipc.h>
struct ipc_perm{
uid_t uid; /* owner’s user id */
gid_t gid; /* owner’s group id */
uid_t cuid; /* creator’s user id */
9
10. gid_t cgid; /* creator’s group id */
mode_t mode; /* read-write perm */
ulong_t seq; /* slot usage seq number */
key_t key; /* IPC key */
};
Creating and Opening IPC channels
The three getXXX functions (msgget, semget and shmget etc.) that create or open an IPC
object all takes an IPC key value whose type is key_t and return an integer identifier.
If IPC key isn’t already available, pass IPC_PRIVATE as key to getXXX function, a
unique IPC object is created.
Specifying a key of IPC_PRIVATE guarantees that a unique IPC object is created. No
combinations of pathname and id exist that cause ftok to generate a key value of
IPC_PRIVATE.
All three getXXX functions also take an oflag argument that specifies the read-write
permission bits for the IPC object and whether a new IPC object is being created or an
existing one is being referenced.
We obtain an IPC identifier from one of the get function: msgget, semget, shmget. These
identifier are integers, but their meaning applies to all processes unlike file descriptors. If
two unrelated processes, a client and a server, use a single message queue identifier
returned by megget function must be the same integer in order to access the same
message queue.
This feature means that a rogue process could try a read a message from some other’s
application’s message queue by trying different small integer identifier, hoping find one
that is currently in use that allows world read access. If the potential values of these were
small integers then the probability of finding a valid identifier would be about 1 in 50.
To avoid this problem, the designers of these IPC facilities decided to increase the
possible range of identifier to include all integers, not just small integers.
System V message Queue
System V message Queue are identified by a message queue identifier. Any process with
adequate privileges can place a message onto a given queue or read a message from a
given queue.
msgget function – A new message queue is created, or an existing message queue is
accessed .
msgsnd function – put a message string pointed by its 2nd argument on to the queue.
msgrcv function – a message is read from a message queue using the msgrcv function.
msgctl function- provides a variety of control operations on message queue.
Like removing a queue from the system, setting and reading permission bits and mode.
Multiplexing messages
Two features are provided by the type field that is associated with each message on a
queue:
1
11. The type field can be used to identify the message, allowing multiple processes to
multiplex messages onto a single queue. One value of the type field is used for messages
from the clients to the server, and a different value that is unique for each client is used
form messages from the server to the clients. Naturally, the process ID of the client can
be used as the type field that is unique for each client.
The type field can be used as a priority field. This lets the receiver read the messages in
an order other than FIFO.
System V shared memory
Similar in concept to Posix shared memory instead of calling shm_open followed by
mmap, we call shget followed by shmat.
Common functions
shmget function- a shared memory segment is created or an existing one is accessed, by
the shmget function. The return value is an integer called the shared memory identifier.
That is used with the three other shmXXX functions to refer to this segment.
#include<sys/shm.h>
int shm_get(key_t key, size_t size, int oflags);
key can be either a value returned by ftok or the constant IPC_PRIVATE.
size – specifies the size of the segment, in bytes.
oflag – combination of read-write permissions.
shmat function –
After a shared memory segment has been created or opened by shmget, we attach it to
our address space by calling shmat.
shmdt function –
when a process is finished with a shared memory segment, it detaches the segment by
calling shmdt.
shmctl – provides a variety of control operation on a shared memory segment. like
removing shared memory segment, setting permission bits and mode.
System V semaphore
The function semget() initializes or gains access to a semaphore. It is prototyped by:
int semget(key_t key, int nsems, int semflg);
When the call succeeds, it returns the semaphore ID (semid).
The key argument is a access value associated with the semaphore ID
semctl() changes permissions and other characteristics of a semaphore set.
semop - operations are performed on one or more of the semaphore in the set using this
function.
1
12. Doors
Door calls are synchronous. Within a process, doors are identified by descriptors.
Externally, doors may be identified by pathnames in the file system.
A server creates a door by calling door_create, whose argument is a pointer to the
procedure that will be associated with this door, and whose return value is a descriptor for
the newly created door.
The server then associates a pathname with the door descriptor by calling fattach.
A client opens a door by calling open, whose argument is the pathname that the server
associated with the door, and whose return value is the client’s descriptor for this door.
The client then calls server procedure by calling door_call.
A server for one door could be a client for another door.
Door calls are synchronous: when the client calls door_call, this function does not return
until the server procedure returns.
When a server procedure is called, both data and descriptors can be passed form the
client to the server. Both data and descriptor can also be passed back from the server to
the client. Descriptor passing is inherent to doors.
Since doors are identified by descriptors, this allows a process to pass a door to other
process.
Basic API functions:
door_call function – is called by a client and it calls a server procedure that is executing
in the address space of the server process.
#include <door.h>
int door_call(int fd,door_arg_t *arg);
returns: 0 if ok , -1 on error
The descriptor fd is normally returned by open.
The pathname opened by the client for identifier fd, identifies the server procedure to be
called by door_call.
The 2nd argument argp points to a structure describing the arguments and the buffer to be
used to hold the return values.
door_create function – A server process establishes a server procedure by calling
door_create.
First arg to door_create function is the address of the server procedure that will be
associated with the door descriptor.
door_return – when a server procedure is done it returns by calling door_return. This
causes the associated door_call in the client to return.
1
13. Remote Procedure call
RPC provides an alternative way to write distributed application.
It allows different pieces of an application to execute on different host and provides
communication between them by calling procedure of one another.
So in RPC, we code our application using the familiar procedure call( like local procedure
call ), but the calling process(the client) and the process containing the procedure
being called(server) can be executing on different host.
Steps in writing RPC application
Write RPC specification file has .x extension, define server procedure along with their
arguments and results.
struct square_in{ /* input(argument) */
long arg1;
};
struct square_out{/* output(argument) */
long res1;
};
program SQUARE_PROG{
version SQUARE_VERS{
square_out SQUAREPROC(square_in) = 1;/*
procedure number=1*/
}=1; /* version number */
}=0x31230000;
Step 2: Compiling RPC specification file (square.x file) using rpcgen program. It
generates following files
A header file that you will include in your programs: square.h
•
A server program which will call actual server procedure when request is
•
received): square_svc.c also called server skeleton
A client stub (that client program can use to send an RPC)
•
square_clnt.c
Internal function to convert the procedure parameters into network
•
messages and vice-versa: square_xdr.c. xdr(external data
representation) must be shared by both server and client.
Step 3: Write client program.
Include header generated by rpcgen.
Declaring and obtaining client handler using clnt_create function.
#include<rpc/rpc.h>
CLIENT *clnt_create(const char *host,unsigned long prognum, unsigned long versnum,
const char *protocol);
Returns: nonnull client handle if OK, NULL on error.
host: hostname or ip address of the host running our server.
1
14. prognum: program name.
versnum: version number.
protocol: either TCP or UDP.
Typical code on client side main function:
CLIENT *cl;// declaring client handle.
square_in in;
square_out *outp;
cl=clnt_create(“192.168.1.1”,0x31230000,1,”TCP”);//obtaining client handle
outp=squareproc_1(&in,cl);// calling server procedure
Step 4: Call remote procedure and print result.
We call procedure and first argument is a pointer to the input structure struct_in and the
second argument is the client handle.
The return value is a pointer to the result structure struct_out.
In our specification file, we named our procedure SQUAREPROC, but from the client we
call squareproc_1. The convension is that the name in the .x file is converted to
lowercase and an underscore is appended, followed by the version number.
Step 5: writing server procedure
on the server side, all we write is our server procedure. rpcgen automatically
generates the server main function.
name of the server must be _svc appended following the version number. This allows
two ANSI C function prototypes in the generated .h header file, one for the
function called by the client and one for the actual server function.
Server procedure has two argument:
First is pointer to struct_in structure and
Second argument is a structure passed by the RPC runtime that contains information about
this invocation.
Typical code for server procedure
square_out squareproc_1_svc(square_in *inp, struct svc_req *rqstp)
{ static square_out out;
out.res1= inp->arg1 * inp->arg1;
return(&out);
}
RPC files
To write a minimalist RPC program, we must write
1. A C procedure to be remotely called: remoteproc.c
2. A specification of the procedure: remoteproc.x
1
15. 3. A client program to request the procedure: client.c
Based on specification file(remoteproc.x), rpcgen program generates:
1) A header file that you will include in your programs: remoteproc.h
2) A server program which will call actual server procedure when request is
received): remoteproc_svc.c also called server skeleton
3) A client stub (that client program can use to send an RPC)
remoteproc_clnt.c
4) Internal function to convert the procedure parameters into network
messages and vice-versa: remoteproc_xdr.c
xdr(external data representation) must be shared by both server and
client.
Client needs with following files to compile:
1. client.c – client code, containing calling statement.
2. procedure_clnt.c – client stub(generated)
3. procedure_xdr.c – handles parameter and return value marshalling or
conversion
4. procedure.h – containing prototype of procedure that is called by client.
Server needs with following files to compile:
1) serverprocedure.c – contain actual remote procedure code.
2) procedur_xdr.c – shared by both client and server
3) procedure_svc.c – server stub
1
16. XTI –x/open transport interface
XTI uses the term communication provider to describe the protocol implementation. The
commonly available communication provider are the internet protocols, that is, TCP or
UDP.
The term communication end point refers to an object that is created and maintained by a
communication provider and then used by an application.
These end point are referred to by file descriptor.
All Xti function begins with t_. The header that the application includes to obtain all the
Xti definition is <xti.h>. some internet-specific definitions are obtained by including
<xti_inet.h>.
t_open function
#include<xti.h>
#include<fcntl.h>
int t_open(const char *pathname, int oflag, struct t_info *info);
establishes a communication end-point is to open the UNIX device that identifies the
particular communication provider.
This function returns a descriptor that is used by other XTI functions.
The actual pathname to use depends on the implementation typical values for TCP/IP
endpoints are /dev/tcp, /dev/udp or /dev/icmp.
1
17. Oflag argument specifies the open flags. Its value is O_RDWR. For a non-blocking end
point the flag O_NONBLOCK is logically OR’ed with O_RDWR.
The tinfo structure is a collection of integer values that describe the protocol dependent
features of the provider.
struct t_info{
t_scalar_t addr;
t_scalar_t options;
t_scalar_t tsdu;
t_scalar_t etsdu;
t_scalar_t connect;
t_scalar_t discon;
t_scalar_t servtype;
t_scalar_t flags;
};
addr – This specifies the max size in bytes of a protocol specific address. A value of -1
indicates that there is no limit to the size. For TCP or UDP is size of sockaddr_in
structure.
Options – Size in bytes of protocol specific functions.
tsdu – stands for “transport service data unit”. This variable specifies the max size in
bytes of record whose boundaries are preserved from one endpoint to other. A value of 0
indicates that the communications provider doesnot support the concept of a TSDU.
For tcp value is always 0, since TCP provides a byte stream service without any record
boundary.
etsdu – ETSDU stands for “expedited transport service data unit” and this variable
specifies the max size in bytes of an ETSDU. This is what we call out of band data.
connect – some connection oriented protocols support the transfer of user data along with
a connection request. This variable specifies the maximum amount of this data.
TCP doesnot support this feature so its value is always -2. and udp is not connection
oriented protocol, its value is also -2.
discon – size of the data that can be passed along with a dis-connection request.
servtype – This specifies the type of service provided by the communication provider .
T_COTS – connection oriented service, without orderly release ex. TCP
T_COTS_ORD - connection oriented service, with orderly release
T_CLTS – connectionless service
flags – additional flag for communication provider
T_SENDZERO – provider supports 0-length writes
T_ORDRELDATA – provider supports orderly release data
TCP doesn’t support 0-length writes but UDP does.
t_error and t_strerror functions
1
18. The xti functions normally return -1 on an error and set the variable t_errno to provide
additional information about the error.
t_errno is similar to errno in that it is set only when an error occurs and it is not cleared
on successful calls.
All error constants are defined <xti.h> and begins with T.
One special error constant is TSYSERR – when it is returned in t_errno, it tells the
application to look at the value in errno for the system error indication.
Functions to format error messages
t_error and t_strerror
#include<xti.h>
int t_error(const char *msg); returns:0
produces a message on the standard error output. This message consist of the string
pointed to by msg followed by a colon and a space, followed by a message string
corresponding to the current value of t_errno. If t_errno equals T_SYSERR, then a
message is also output corresponding to the current value of errno.
const char *t_strerror(int errno); returns: pointer to message
returns a string describing the value of errnum, which is assumed to be one of the
possible t_errno values. Unlike t_error, t_strerror does nothing special if this value is
TSYSERR.
Netbuf structures and XTI structures
XTI defines seven structures that are used to pass information between the application
and the XTI functions.
One of these is t_info described earlier.
The remaining six each contain between one and three netbuf structures. The netbuf
structure defines a buffer that is used to pass data from the application to the XTI
function and vice-versa.
struct netbuf{
unsigned int maxlen;/* max size of buf*/
unsigned int len; /* actual amount of data in buf */
void *buf; /* data (char* before posix.1g )*/
};
following table shows six structures that contain one or more netbuf structures, and the
various other members of the XTI structure.
DataType t_bind t_call t_discon t_optmgmt t_uderr t_unitdata
struct netbuf addr addr addr addr
struct netbuf opt opt opt opt
struct netbuf udata udata udata
t_scalar_t error
t_scalar_t flags
unsigned int glen
1
19. int
int reason
sequence sequence
Six XTI structure and their members.
Since the address of the netbuf structure is always passed to an Xti function and since the
structure contains both the size of the buffer (maxlen) and the amount of data actually
stored in the buffer (len), there is no need in XTI for all the value arguments used in the
socket.
t_bind function
This function assigns the local address to an endpoint and activates the endpoint. In the
case of TCP or UDP the local address is an IP address and a port.
#include<xti.h>
int t_bind(int fd,const struct t_bind *request, struct t_bind *return);
returns : 0 if OK, -1 on error.
The second and third argument point to t_bind structure
struct t_bind{
struct netbuf addr;/* protocol specific address */
unsigned int len; /*max# of outstanding connection (if server)*/
};
The endpoint is specified by fd. There are three cases to consider for the request
argument.
request = = NULL
the caller doesnot care what local address gets assigned to the endpoint. The provider
selects an address. The value of glen argument is assumed to be zero.
request !=null but request->addr.len == 0
The caller doesn’t care what protocol address get assigned to the endpoint and again the
provider selects an address. Unlike the previous case, the caller can now specify a
nonzero value for the glen member of the request structure.
request !=null and request->addr.len > 0
The caller specifies a local address for the communication provider to assign to the
communication endpoint.
The value of glen has meaning only for connection oriented server: it specifies the
maximum number of connection to queue for this endpoint.
A connection oriented XTI client must call t_bind before calling t_connect. This differs
from connect, which calls bind internally, if the socket has not bound.
t_connect function
A connection oriented client initiates a connection with a server by calling t_connect. The
client specifies the server’s protocol address.
#include<xti.h>
int t_connect(int fd,const struct t_call *sendcall, struct t_call *recvcall);
The second and third arguments points to a t_call structure.
1
20. struct t_call{
struct netbuf addr; /* protocol specific address*/
struct netbuf opt; /* protocol specific address */
struct netbuf udata; /* user data to accompany conn. request */
int sequence; /* for t_listen and t_accept function */
};
addr: specifies the server’s address.
opt: specifies the protocol specific options desired by the caller.
udata: Any user data to be transferred to the server during connection establishment.
sequence: It has no significance for this function but is used when this structure is used
with the t_accept function.
On return from this structure, the t_call structure pointed by the recvcall argument
contains information associated with the connection that is returned by the
communication provider to the caller. The addr structure contains the address of the peer
process, opt contains any protocol dependent options associated with the connection and
udata contains any user data returned by the peers during connection establishment.
Sequence number has no meaning.
By default, this function does not return until the connection is completed or an error
occurs.
When an error occurs during connection establishment t_connect returns -1, but t_errno is
set to TLOOK, requiring more code to determine exact reason.
t_rcv and t_snd function
By default, XTI applications can’t call normal read and write functions. Instead XTI
applications must call t_rcv and t_snd.
#include<xti.h>
int t_rcv (int fd, void *buff, unsigned int nbytes, int *flags);
int t_snd(int fd, const void *buff, unsigned int nbytes, int flags);
both return: no of bytes read or written of OK, -1 on error.
The first three argument are similar to first three argument to read or write: descriptor,
buffer pointer and number of bytes to read or write.
The flags argument to t_snd is either zero, or some combination of the following
constants
T_EXPEDITED – send or receive expedited (out of band) data.
T_MORE – there is no more data to send or receive. This flag is provided so that multiple
t_rcv or t_snd function calls can read or write what the protocol considers a logical
record.
XTI response to FIN or RST segment
• When a TCP FIN is received for an XTI endpoint, t_rcv returns -1 with t_errno
set to TLOOK. The XTI function t_look must then be called, and it returns
T_ORDREL. This is called an orderly release indication.
2
21. • When a TCP RST is received for an XTI endpoint, t_rcv returns -1 with t_errno
set to TLOOK. The XTI function t_look must then be called and it returns
T_DISCONNECT. This is called a disconnect or an abortive release.
t_look function
Various events can occur for an XTI endpoint and these events can occur
asynchronously. By that we mean that the application can be performing some task when
an related event occurs on the endpoint. Many of such event indicate with TLOOK error
constant assigned to t_errno. And to get information about event occurred, one has to call
t_look on that endpoint.
#include<xti.h>
int t_look(int fd);
return: event if OK, -1 on error.
The integer value returned by this function is one of the nine events shown here.
Event Description
T_CONNECT Connection confirmation received
T_DATA Normal data received
T_DISCONNECT Disconnect received
T_EXDATA Expedited data received
T_GODATA Flow control restriction on normal data lifted
T_GOEXDATA Flow control restriction on expedited data lifted
T_LISTEN Connection indication received
T_ORDREL Orderly release indication received
T_UDERR Error in previously sent datagram.
t_sndrel and t_rcvrel function
XTI supports two ways of releasing a connection: an orderly release and an abortive
release. The differences are that an abortive release does not guarantee the delivery of
any outstanding data, while the orderly release guarantee this.
We can send and receive an orderly release with the following functions:
#include<xti.h>
int t_sndrei(int fd);
int t_rcvrel(int fd);
both return:0 if OK, -1 on error.
A process issues an orderly release by calling t_sndrel. This tells the provider that the
application has no more data to send on this endpoint. For a TCP endpoint, TCP sends a
FIN to the peer. The process that calls t_sndrel can continue to receive data, but it can no
longer write to the descriptor.
2
22. A process acknowledges the receipt of a connection release by calling the t_rcvrel
function. This process can still write to the descriptor but it can no longer read from the
descriptor.
t_snddis and t_rcvdis function
The following two functions handle an abortive release
#include<xti.h>
int t_snddis(int fd, const struct t_call *call);
int t_rcvdis(int fd, struct t_discon *discon);
Both return: 0 if ok , -1 on error.
The t_snddis function is used for two different purposes:
• To perform an abortive release of an existing connection, which in terms of TCP
causes an RST to be sent , and
• To reject a connection request.
For an abortive release of an existing connection, call argument can be a null pointer.
When a T_DISCONNECT event occurs on an XTI endpoint (e.g. an RST is received on
TCP), the application must receive the abortive release by calling t_rcvdis. If the discon
argument is a nonnull pointer, a t_discon structure is filled in with the reason for the
abortive release.
struct t_discon{
struct netbuf udata; /*user data*/
int reason;/* protocol specific reason code */
int sequence; /*applicable only for server receiving connection */
};
XTI and Socket interoperability
The interoperability is provided by the internet protocol suite and has nothing to do with
socket or XTI.A client written using TCP or UDP interoperates with a server using the
same transport protocol if the client and server speak the same application protocol.
Regardless of what API is used to write either the client or the server. It is the protocol
that determine the interoperability not the API we use to write the server and client.
XTI Name and address conversion functions
The network services library provides numerous functions to read the netconfig file.
The setnetconfig function opens the file and the function getnetconfig then read the next
entry in the file. endnetconfig closes the file and releases any memory that was allocated.
#include <netconfig.h>
void *setnetconfig(void);
returns- nonnull pointer if OK, NULL on error
struct netconfig *getnetconfig(void *handle);
returns: nonnull pointer if OK, NULL on end of file.
int endnetconfig(void *handle);
return: 0 if OK, -1 on error.
The pointer returned by setnetconfig is then used as the argument to the remaining two
functions. Each entry in the file is returned as a netconfig structure.
struct netconfig{
char nc_netid; /*”tcp”,”udp”, etc. */
2
23. unsigned long nc_semantics; /*NC_TPI_CLTS, etc. */
unsigned long nc_flag;/* NC_VISIBLE, etc. */
char *nc_protofmly;/*”inet”, “loopback”, etc. */
char *nc_proto;/*”tcp”,”udp”, etc. */
char *nc_device;/*device name for network id */
unsigned long nc_nlookups; /* # of entries in nc_lookups */
char nc_lookups; /* list of lookup libraries */
unsigned long nc_unused[8];
};
Example using netconfig
void *handle;
struct netconfig *nc;
handle = setnetconfig();
while((nc = getnetconfig(handle))!=NULL)
/* print netconfig structure */
}
endnetconfig(handle);
NETPATH variable and netpath Functions
The getnetconfig function returns the next entry in the file, letting us go through the
entire file line by line. But for interactive programs(typically) we want the searching of
the file limited only to the protocols that the user interested in.
This is done by allowing the user to set an environment variable named NETPATH and
then using the following functions instead of the netconfig functions described in the
process section.
#include<netconfig.h>
void *setnetconfig(void);
Returns: nonnull pointer if OK, NULL on error
struct netconfig *getnetpath(void *handle);
Returns: nonnull pointer if OK, NULL on end of file.
int endconfig(void *handle);
Returns: 0 if OK, -1 on error
For example, we could set the environment variable with the shell as
export NETPATH= udp:tcp;
with this setting, if we coded our program as shown
void *handle;
struct netconfig *nc;
handle = setnetconfig();
while(nc = getnetconfig(handle))!=NULL){
/* print net config structure */
}
only two entries would be printed, one for UDP followed by one for TCP. The order of
the two structure returned now corresponds to the order of the protocols in the
environment variable, and not to the order in the netconfig file.
If the NETPATH environment variable is not set, all visible entries are
returned, in the order in the netconfig file.
2
24. Netdir Functions
The netconfig and netpath functions let us find a desired protocol. We also need to look
up a hostname and a service name, based on the protocol that we choose with the
netconfig and netpath functions. This is provided by the netdir_getbyname function.
#include<netdir.h>
int netdir_getbyname(const struct netconfig *ncp,
const struct nd_hostsery *hsp,
struct nd_addrlist **alpp);
returns: 0 if OK, non-zero on error
void netdir_free(void *ptr, int type);
we have to pass host name and service name in the nd_hostsery structure as second
argument.
struct nd_hostery{
char *h_host;
char *h_serv;
};
The third argument points to a pointer to an nd_addrlist structure and on
success *alpp contains a pointer to one of these structures:
struct nd_addrlist{
int n_cnt;/* no of netbuffs */
struct netbuf n_addrs; / array of netbuffs containing the addrs */
};
this nd_addrlist structure points to an array of one or more netbuf structures, each of
which contains one of the host address and are dynamically allocated.
To this dynamically allocated array of nd_addrlist structure, we call netdir_free with ptr
pointing to the nd_addrlist structure and type set to ND_ADDRLIST.
The reverse conversion- given a netbuff structure containing an address, return
the host name and service name – is provided through netdir_getbyaddr.
#include<netdir.h>
int netdir_getbyaddr( const struct netconfig *ncp,
struct nd_hostservlist *hslpp,
const struct netbuf *addr);
Returns: 0 if OK, nonzero on error
The result is a pointer to an nd_hostservlist structure and this pointer is stored in *hslpp.
Struct nd_hostservlist{
int h_cnt;/* number of hostservs */
struct nd_hostsery h_hostservs; /* the hostname/service name pairs */
};
this structures is allocated dynamically and must be freed by calling netdir_free with the
type of ND_HOSTSERVLIST.
t_alloc and t_free function
As we have seen previously with XTI there are six structures each of which contains one
ore more netbuf structures. The netbuf structures points to a buffer whose size depends
on the size of the protocol address. To simplify the dynamica memory allocation of these
XTI structures and the netbuf structures that they contain, t_alloc and t_free are provided.
2
25. #include<xti.h>
void *t_alloc(int fd, int structtype, int fields);
returns: nonnull pointer if OK, NULL on error
int t_free(void *ptr, int structtype);
returns: 0 if OK, -1 on error.
The struct type argument specifies which of the seven XTI structures is to be allocated
or freed and must be one of the constants shown here
The fields argument let us specify that space for one or more netbuf strucutures should
alos be allocated and initialized.
fields is the bitwise OR of the constants shown here
The intent of XTI model is to allow the transport layer to tell the server process when a
SYN arrives from a client.
Struct type Type of structure
T_BIND Struct t_bind
T_CALL struct t_call
T_DIS struct t_dis
T_INFO struct t_info
T_OPTMGMT struct t_optmgmt
T_UDERROR struct t_uderr
T_UNITDATA struct t_unitdata
Fields Allocate and initialize
T_ALL All relevant fields of the structure
T_ADDR addr field of t_bind, t_call
T_OPT opt field of t_optmgmt, t_call
T_UDATA udata field of t_call, t_discon or t_unitdata.
t_getprotaddr function
returns both the local and foreign protocol address associated with an endpoint.
#include<xti.h>
int t_getprotaddr(int fd, struct t_bind *localaddr, struct t_bind *peeraddr);
returns: 0 if OK, -1 on error.
The address is returned in addr member ( netbuf structure) of two t_bind structure.
TCP Server using XTI
XTI model allows the transport layer to tell the server process when a SYN arrives,
passing client protocol address. The server process is then allowed to send either accept
or reject the request. The server's TCP would not send its SYN/ACK or RST until the
server process tells it what to do.
2
26. Notice the server' function calls:
t_bind – indicates that end point will be accepting connection
t_listen – returns when connection is available.
t_accept – to accept the connection
t_snddis – to reject the request.
t_listen function
#include<xti.h>
int t_listen(int fd, struct t_call *call);
returns: 0 if OK, -1 on error
struct t_call{
struct netbuf addr;
struct netbuf opt;
struct netbuf udata;
int sequence;
};
The structure returned through the call pointer contains relevant parameters of the
connection:
addr: contains the protocol address of the client.
Opt: contains any protocol specific address.
Udata: contains any user data sent along with connection request.
Sequence: contains unique value that identifies this connection request.this value will be
used when we call t_accept or t_snddis to identifiy which connection to accept or reject.
t_accept function
Once the t_listen function indicates that a connection has arrived, we choose whether to
accept the request or not. To accept the request the t_accept is called
#include<xti.h>
int t_accept ( int listenfd, int connfd, struct t_call *call);
returns: 0 if OK, -1 on error.
2