CIP Working Group					    Charles Lynn
INTERNET-DRAFT					 Bolt Beranek and Newman
							   12 March 1992


		   Notes for Application Implementors on
		            an ST-II Socket API


Status of this Memo

This draft document will be submitted to the RFC editor as an 
experimental specification. This is a working document only, it should
neither be cited not quoted in any formal document. This document will
expire before 12 September 1992. Distribution of this memo is
unlimited. Please send comments to Topolcic@NRI.Reston.VA.US.

Abstract

This memo presents the current specification of the Application
Programming Interface used to access the services provided by the BBN
implementation of the ST-II Protocol (RFC 1190).  It is being circulated
as a basis for discussion of a generic API.  A generic API will allow
application developers to implement applications capable of running on
any ST-II implementation that conforms to the API.  Comments and
suggestions for the generic API are solicited from the community by the
CIP Working Group.  Some comments that have already been received are
included in the Appendix.





























							       [Page  1]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


Contents

  1.        Overview   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  3
  1.1.          What ST is and What it Provides   .  .  .  .  .  .  .  3

  2.        Two-party Overview  .  .  .  .  .  .  .  .  .  .  .  .  .  4
  2.1.          Unidirectional Communication   .  .  .  .  .  .  .  .  4
  2.2.          Bidirectional Communication .  .  .  .  .  .  .  .  .  6
  2.2.1.            Two (Simplex) Streams   .  .  .  .  .  .  .  .  .  6
  2.2.2.            Single (Full-duplex) Stream   .  .  .  .  .  .  .  6

  3.        Multiple-party Overview   .  .  .  .  .  .  .  .  .  .  .  8
  3.1.          Full-Duplex Variation .  .  .  .  .  .  .  .  .  .  .  8

  4.        Example .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  9
  4.1.          socket .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  9
  4.2.          bind   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  . 10
  4.3.          listen .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  . 10
  4.4.          connect   .  .  .  .  .  .  .  .  .  .  .  .  .  .  . 11
  4.5.          accept .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  . 12
  4.6.          recvmsg to obtain new connection parameters   .  .  . 12
  4.7.          sendmsg to refuse a connection .  .  .  .  .  .  .  . 12
  4.8.          sendmsg to accept a connection .  .  .  .  .  .  .  . 13
  4.9.          recvmsg at origin to receive control messages .  .  . 13
  4.10.         sending data .  .  .  .  .  .  .  .  .  .  .  .  .  . 14
  4.11.         recvmsg at targets to receive data or control messages
                                                           .  .  .  . 15
  4.12.         sendmsg at origin to disconnect targets .  .  .  .  . 16
  4.13.         close  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  . 16
  4.14.         select .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  . 16

  Appendix  Previously received comments .  .  .  .  .  .  .  .  .  . 17





















							       [Page  2]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


1.  Overview

	This document is intended to provide application programmers
    with an overview of the services provided by the ST-II protocol (or
    just ST for simplicity) and how an application may access those 
    services in a BSD socket environment.  A working knowledge of BSD 
    sockets is assumed.  A detailed knowledge of the ST protocol 
    defined in RFC 1190 is not assumed, but references are made to that 
    document so that the detailed descriptions of parameters and their
    elements that it contains do not have to be repeated here.

	The main goals of the experimental API described here are to
    function with a SunOS 4.1 or later kernel without modifications
    to the standard socket interface.  This requirement means not only
    that only standard system calls are used, but also that they are
    used in a manner consistent with their normal semantics and that
    they are used in a way that does not provoke any bugs in their
    implementation.


    1.1.  What ST is and What it Provides

	    ST is a protocol at the same layer as IP and has been
	developed to support efficient delivery of streams of packets
	to either single or multiple destinations in applications
	requiring guaranteed data rates and controlled delay
	characteristics.  Consequently, ST has quantitative
	Type-of-Service (TOS) parameters and requires that the
	intermediate ("gateway") agents reserve resources for each
	stream of data flowing through them, both within the agents
	and the interconnecting networks.  Resources are negotiated
	and reserved during a setup phase before data is transferred;
	there is also a teardown phase during which resources are
	released.


	... for more details, see RFC 1190 Sections:
	2 Introduction (page 7), 
	2.2 Concepts and Terminology (page 9), 
	2.3 Relationship Between Applications and ST (page 11), and
	2.5 Flow Specifications (page 14).


	ST requires several complex parameters to be communicated
    between an application program and the ST layer; see Sections
    4.2.2.* in RFC 1190.  In general, these parameters are passed as a
    list in the cmsghdr control structure of sendmsg()/recvmsg()
    ("accrights" argument) or getsockopt()/setsockopt() ("optval"
    argument) functions, or in a sockaddr_st2 structure when
    appropriate.  To meet the goals stated in Section 1, the size of
    the sockaddr_st2 structure is limited to MAX_ST_NAM bytes (the
    data area in a simple mbuf) and the size of the cmsghdr structure

							       [Page  3]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


    is limited to MAX_ST_CTL bytes.  If the list of parameters to be
    passed between the application program and ST exceeds these limits,
    the parameters must be partitioned into two or more sublists and all
    but the last sublist passed to the ST layer using the setsockopt()
    function calls before issuing the desired system call, e.g.,
    connect(), with the last sublist.  Note that control information
    is never placed into a "data" buffer, as it is expected that the
    data buffers may be directly connected to hardware that might be
    confused by control data.

	ST permits and sometimes requires more control interaction
    with an application program than many other protocols.  In general,
    this interaction is asynchronous with the data stream.  Consequently,
    the application's state machine may be more complex and the
    application must be prepared to receive and process the control
    information throughout the lifetime of a socket.  When an
    application does not require complex interactions with the ST
    layer, the simpler interactions used by other network applications
    should suffice.

	ST does not address the issue of how application instances
    become aware of each other's identity.  Within ST, the IP address
    of an application's host (IPAdr), the number identifying the
    protocol layer above ST (NextPcol), and the Service Access Point
    (SAP, aka "port") are used to identify an application.


2.  Two-party Overview

	In general, ST assumes multiple-party (more than two)
    communications.  However, if an application knows a priori that a
    stream will never have more than two participants and so informs
    ST by setting the Point-to-Point ("STOptPBit") flag in the
    st_options field of the sockaddr_st2 structure, ST can then make
    simplifying assumptions during stream setup and management.


    2.1.  Unidirectional Communication

	    Each ST stream passes data from a single origin to the
	stream's target (only one for two-party communication).  The
	target does:

	T1)  socket()  to create a raw ST-II socket,

	T2)  bind()    to specify the protocol, SAP length, and SAP,

	T3)  listen()  to prepare for receipt of a STReqConnect, and

	T4)  accept()  to create a new socket for each STReqConnect
		       received.


							       [Page  4]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


	The accept() will block until a STReqConnect request is
	received.

	    The origin does:

	O1)  socket()  to create a raw ST-II socket,

	O2)  bind()    to specify the protocol, SAP length, and SAP,

	O3)  connect() to specify the required resources (TOS), identify
		       the target, and send a STReqConnect to it, and

	O4)  recvmsg() to see if the connect() was successful.

	The (O4) recvmsg() will block until either an error or an
	accept or refuse reply is received.  Currently, the (O2)
	bind() is not optional as the protocol must be specified.
	[The (T1 and O1) socket() call(s) cannot be used to specify a
	protocol as none are currently provided above ST in the
	kernel.]  The bind() is also required if a SAP length other
	than two bytes (i.e., sixteen bits) is desired or the local
	IPAdr is to be specified.

	    When the SCMP CONNECT message created by the (O3)
	connect() arrives at the target, the application is notified
	of pending input: the application's (T4) accept() will
	complete and return a new socket for exclusive use by the
	stream.  The application then does a

	T5)  recvmsg()

	on the new socket to receive the STReqConnect request and the
	associated flowspec (TOS) parameters.

	    The application then examines the parameters to decide
	whether to accept or refuse the connection.  The decision is
	passed to ST via an STReqAccept or STReqRefuse request using

	T6)  sendmsg().

	Assuming the target chooses to accept the connection, the
	target then does a

	T7)  recvmsg() to receive data (or control, e.g.,
		       STReqDisconnect).

	The (T7) recvmsg() will block until the data or some control
	message is received.  Otherwise, the socket for the refused
	connection should be closed using the

	T9)  close() function.


							       [Page  5]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


	    ST sends the STReqAccept or STReqRefuse decision back to
	the application at the origin.  Assuming the connection was
	accepted, the origin's (O4) recvmsg() will unblock and return
	the actual flowspec (TOS) parameters that describe the
	resources actually reserved in the networks for the stream.
	The origin may begin to send data to the target using any of
	the output functions, e.g.,

	O5a) write(),
	O5b) send(),
	O5c) sendto(), or
	O5d) sendmsg().

	    At any time, either the origin or target may issue a
	STReqDisconnect request using (O5d or T8) sendmsg() to
	gracefully tear down the stream and then (O6 or T9) close()
	the socket.


    2.2.  Bidirectional Communication

	    In the case of bidirectional data flow, two methods are
	permitted: two (simplex) streams (each as described in
	Section 2.1), or a single (full-duplex) stream.  When a single
	full-duplex stream is used, both data streams will pass
	through the same set of intervening ST Agents and networks.
	When two simplex streams are used, the data flowing in one
	stream may traverse a different set of intervening ST Agents
	or networks than does data flowing in the reverse stream.


	2.2.1.  Two (Simplex) Streams

		This method of providing bidirectional communication
	permits the data flowing in opposite directions to traverse
	different ST Agents.  For example, different routes would be
	selected if the resource requirements for the two directions
	are significantly different.  This method also permits the
	SAPs used by the two streams to be distinct.  Each participant
	performs the roles of origin and target on different sockets,
	as described under unidirectional communication in Section
	2.1.


	2.2.2.  Single (Full-duplex) Stream

		Since two-party bidirectional communication is such a
	    common occurrence, ST provides a way to create two streams
	    at once.  The sequence of system calls described in
	    Section 2.1 is used; the only difference is that the
	    parameters for the connect() function contain flowspec
	    (TOS) parameters for both directions.  In this case, the

							       [Page  6]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


	    "server" application performs the role of a target, as
	    described above:

	    S1)  socket()  to create a raw ST-II socket,

	    S2)  bind()    to specify the protocol, SAP length, and
			   SAP,

	    S3)  listen()  to prepare for receipt of a STReqConnect,
			   and

	    S4)  accept()  to create a new socket for each STReqConnect
			   received.

	    The accept() will block until a STReqConnect is received.


		The "client" application does a

	    C1)  socket()  to create a raw ST-II socket,

	    C2)  bind()    to specify the protocol, SAP length, and
			   SAP,

	    C3)  connect() to specify the required resources (TOS)
			   for each direction (an STpFlowSpec and
			   STpRFlowSpec parameter), identify the
			   target, and send an SCMP CONNECT to it, and

	    C4)  recvmsg() to see if the connect() was successful.

		When the SCMP CONNECT message created by the (C3)
	    connect() arrives at the server, the application is
	    notified of pending input: the application's (S4) accept()
	    will complete and return a new socket for exclusive use by
	    the stream.  The server application then does a

	    S5)  recvmsg()

	    on the new socket to receive the STReqConnect request
	    that contains flowspecs for both the client-to-server and
	    server-to-client directions.  The server then accepts (or
	    refuses) the connection using

	    S6)  sendmsg()

	    with an STReqAccept, which is returned to the client.
	    Each participant then has a single socket that may be
	    used for bidirectional communication.

	    C5)  sendmsg()		S5)  recvmsg()

	    S6)  sendmsg()		C4)  recvmsg()
							       [Page  7]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


		Either the client or server may issue a
	    STReqDisconnect by:

	    C5)  sendmsg()	or	S6)  sendmsg().

	    The other end will receive notification via the (C4 or S5)
	    recvmsg().  Both ends should then close() the socket:

	    C6)  close()	and	S7)  close().


3.  Multiple-party Overview

	ST permits multiple-party (more than two) communications,
    wherein each stream has a single origin that sources all data and
    one or more targets that receive the data.  Recall however that
    ST is not a reliable protocol; if reliability is required by an
    application, it must be implemented by a protocol layer above ST.
    Some implementations may support a multiple-party full-duplex
    (star) variation that can be useful in certain applications.

	In the unidirectional case, each of the targets follows the
    procedures described above.  The origin simply includes
    information about each target in the STpTargetList parameter in
    the connect() call.  To support full intercommunication between N
    participants, N streams are created.  Each participant performs
    the roles of both a target (which will result in N-1 new sockets
    being accept()ed), and that of an origin specifying the other N-1
    participants as targets.

	Each participant then does N-1 accept() calls to obtain a
    socket per participant.  For each new socket, a recvmsg() is used
    to receive the parameters associated with a STReqConnect and a
    STReqAccept is sent to accept the connection using sendmsg().
    When responses from the other N-1 participants have been received
    on the socket used for the connect(), a participant may safely
    begin sending data to the others.


    3.1.  Full-Duplex Variation

	    It is envisioned that a full-duplex multiple-party star
	form of communication might be useful.  One such scenario is
	televised instruction, where audio and video from a central
	classroom is sent to each remote classroom, and separate
	return audio and video data is returned back to the central
	site for audio and video mixing or switching.  However,
	support for such a variation is implementation dependent.





							       [Page  8]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


4.  Example

	All ST definitions required by an application program are
    defined in the netinet/st2_api.h include file.  In particular, a set
    of macros is defined that allow ST data structures and parameter
    lists to be customized by an application.  Each macro has a name
    of the form "Instxxx(args)" where xxx is the name of the structure
    or parameter of interest.  The first argument is the symbolic
    variable name of the struct being defined.  Other optional
    arguments are used to specify the desired size of variable length
    elements or embedded structures.  Other macros, e.g., Initxxxx,
    may be used to initialize the Instxxx structures.  The macros are
    only appropriate, however, for passing fixed-format information to
    ST.  Variable format parameters must be constructed dynamically.
    Information passed by ST to the application must be parsed by the
    application since the application cannot predict which parameters
    will be present or their relative order.


    4.1.  socket

	The socket() function is used to obtain an ST socket by
    specifying protocol family PF_COIP.  Currently, only protocol type
    SOCK_RAW is supported; other protocols usually implemented in the
    kernel, e.g., UDP or TCP, have not yet been modified for use over
    ST.  Consequently, the "prot" argument should be specified as 0.


	#include <sys/types.h>
	#include <sys/socket.h>
	#include <netinet/st2_api.h>

	int s;

	s = socket( PF_COIP, SOCK_RAW, /*prot*/0 );
	if ( s == -1 )
	    { error processing; }
















							       [Page  9]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


    4.2.  bind

	The bind() function may be used to specify the local host's IP
    address "IPAdr" (if the host has more than one), the number
    "NextPcol" (an STPROTO_xxx) identifying the application-layer
    implementation of the protocol above ST (when "prot" was zero in the
    socket call), the length of the SAP, and/or the SAP for the local
    socket.  The sockaddr_st2 structure in the bind() call should
    specify AF_COIP and contain an aApplEntity structure. [Support for a 
    sockaddr_in with AF_INET is not yet available.]  Use INADDR_ANY to 
    leave unspecified the local host address, i.e., any of the host's 
    network addresses.  Zero (0) to leave unspecified the protocol above 
    ST, i.e., if it was specified in the socket call.  An application 
    that desires to receive all SAPs of a given length should specify 
    the SAP length "SAPBytes" and set all bytes in the SAP to binary 
    zeros.  If an application is to receive all SAPs of any length, the 
    SAPBytes value should be zero. The SAP matching criteria first checks 
    for an exact match of length and value, then checks for a match on 
    length with a zero value, and finally for a zero length.  The 
    st_request field should be STReqBind.  The opt_xxx field is used to
    specify the "OR" of the desired options: STOptPBit for point-to-point
    (two-party) streams, STOptSBit for the no recovery option, etc.


	#define	SAPLEN 2		/* Length of our SAPs */
	#define Bytesof(v) Bytes2(v)	/* !@#$ cpp lacks an "eval" */
	#define opt_xxx STOptSBit|STOptPBit	/* Desired options */
	#define STPROTO_xxx 63
	#define sap_xxx IPPORT_DISCARD

	int error;

	Instsockaddr_st2(local,InstaApplEntity(here,SAPLEN,);) = {
	    Initsockaddr_st2(STReqBind,opt_xxx),
	    InitaApplEntity(STLclAppEnt,local.here,
		INADDR_ANY,STPROTO_xxx,SAPLEN,Bytesof(sap_xxx),)
	};

	error = bind( s, (struct sockaddr *) &local, sizeof( local ) );
	if ( error == -1 )
	    { error processing; }


    4.3.  listen

	Targets should then do a listen() and an accept() that will
    block until a STReqConnect request arrives.


	error = listen( s, /*backlog*/ 5 );
	if ( error == -1 )
	    { error processing; }

							       [Page 10]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


    4.4. connect

	The origin should use the connect() function to create a stream.
    All parameters the SCMP CONNECT requires must be specified before or
    at the time of the connect(), e.g., protocol (via an STLclAppEnt in
    a bind()), FlowSpec (STpFlowSpec/struct aFlowSpec3), TargetList 
    (STpTargetList/struct aTargetList), and any optional parameters the
    application requires.  Parameters may be specified prior to the 
    connect() using setsockopt() function call(s). In particular, the
    setsockopt() call(s) must be used when the size of the sockaddr_st2 
    structure containing all the necessary parameters exceeds the maxi-
    mum permitted (MAX_ST_NAM) for the name argument of the connect().


	Instsockaddr_st2(remote,
	    InstaFlowSpec3(tos);
	    InstaTargetList(targlst,
		InstaTarget(targ1,SAPLEN,);
		InstaTarget(targ2,SAPLEN,
		    InstaSrcRut(theway,1);
		);
	    );
	) = {
	    Initsockaddr_st2(STReqConnect,opt_xxx),
		InitaFlowSpec3(STpFlowSpec,
		    /*min*/  128/*bytes*/,  2/*pps*/,128*2/*bandwidth*/,
		    /*des*/ 1024/*bytes*/, 10/*pps*/),
		InitaTargetList(remote.targlst,2/*targets*/,
		    InitaTarget(remote.targlst.targ1,
			    INADDR_ANY,SAPLEN,Bytesof(sap_xxx),)
		    InitaTarget(remote.targlst.targ2,
			    INADDR_ANY,SAPLEN,Bytesof(sap_xxx),
			InitaSrcRut(STpSTLSrcRut,
			    remote.targlst.targ2.theway,INADDR_ANY)
		    )
		)
	};

	SetIPAdr(&remote.targlst.targ1,,targ1_ip_address);
	SetIPAdr(&remote.targlst.targ2,,targ2_ip_address);
	SetIPAdr(&remote.targlst.targ2.theway,[0],targ2_gateway_address);
	error = connect( s, (struct sockaddr *) &remote, sizeof( remote ) );
	if ( error == -1 ) { error processing; }


	Addition of targets to a stream after the initial connect are
    effected using a sendmsg whose sockaddr_st2 st_request field
    contains STReqConnect and whose st_parms list contains the
    STpTargetList/struct aTargetList specifying the targets to be
    added.  Note that routing optimizations that are possible when
    the targets are specified prior to the (first) connect will not be
    performed if the targets are specified one at a time using
    multiple connects (sendmsg[setsockopt]/STReqConnect).
							       [Page 11]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


    4.5.  accept

	When a target receives an SCMP CONNECT message, a new socket
    for the connection is obtained using the accept() function.


	char namebuf[ MAX_ST_NAM ];
	int namelen;
	int s2;
	struct sockaddr_st2 *namep;

	namelen = sizeof( namebuf );
	s2 = accept( s, (struct sockaddr *) &namebuf, &namelen );
	if ( s2 == -1 ) { error processing; }
	namep = (struct sockaddr_st2 *) namebuf;
	if ( namep->st_request != STReqConnect ) { error processing; }


    4.6.  recvmsg to obtain new connection parameters

	After accept has returned a new socket for a connection, the
    recvmsg() function is used to read the parameters for the connection.


	char ctrlbuf[ MAX_ST_CTL ];
	struct msghdr msg;
	struct cmsghdr *ctrlp;

	InitMsg(&msg,namebuf,sizeof(namebuf),0,0,ctrlbuf,sizeof(ctrlbuf));
	error = recvmsg( s2, &msg, 0/*flags*/ );
	if ( error == -1 ) { error processing; }
	ctrlp = (struct cmsghdr *) ctrlbuf;
	if (   (ctrlp->cmsg_level != SOL_STII)
	    || (ctrlp->cmsg_type != STReqConnect) )
	    { error processing; }


    4.7.  sendmsg to refuse a connection

	The connection can be refused by calling sendmsg() with a
    STReqRefuse request and an STReasonCode in the cmsghdr structure.


	Instcmsghdr(refuse,InstaReasonCode(why);) = {
	    Initcmsghdr(refuse,STReqRefuse),
	    InitaReasonCode(ApplDisconnect)
	};

	namep->st_request = STReqRefuse;
	msg.msg_accrights = &refuse; /* replace with refuse struct */
	msg.msg_accrightslen = sizeof( refuse );
	error = sendmsg( s2, &msg, 0/*flags*/ );
	close( s2 );
							       [Page 12]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


    4.8.  sendmsg to accept a connection

	The connection can be accepted by a target using the sendmsg()
    STReqAccept request.  A STpFlowSpec parameter is only necessary when
    the target wishes to modify, e.g., reduce, the amount of resources 
    that were obtained along the path or when the application wants to 
    send a UserData Parameter. For example, if reverse charging (collect
    calls) is not to be accepted, the target can clear the STFSRevChrg 
    flag in the received flowspec before accepting the connection.


	struct aParameter *parmp;
	struct aFlowSpec3 *fsp;

	parmp = FindParm(STpFlowSpec, ctrlp->cmsg_len,
			 (struct aParameter *) ctrlp->cmsg_data);
	if ( parmp == (struct aParameter *) NULL )
	    { inconsistency processing; }
	fsp = (struct aFlowSpec3 *) parmp;
	if ( fsp->FlowVer != STFSVer3 ) { no support processing; }
	fsp->Tradeoffs &= ~ STFSRevChrg; /* no reverse charging */

	namep->st_request = STReqAccept; /* accept the connection */
	/* just return all parameters EXCEPT UserData, let ST extract
	   the necessary parameters for the ACCEPT */
	error = sendmsg( s2, &msg, 0/*flags*/ );
	if ( error == -1 ) { error processing; }


    4.9.  recvmsg at origin to receive control messages

	The origin is notified of each target's acceptance or refusal
    through a cmsghdr structure in a recvmsg().  The acceptance
    (STReqAccept) includes the FlowSpec (STpFlowSpec) that the target
    accepted; a refusal (STReqRefuse) includes the ReasonCode
    (STReasonCode).  Either may contain a UserData Parameter.


	InitMsg(&msg,namebuf,sizeof(namebuf),0,0,ctrlbuf,sizeof(ctrlbuf));
	error = recvmsg( s, &msg, 0/*flags*/ );
	if ( error == -1 ) { error processing; }
	ctrlp = (struct cmsghdr *) ctrlbuf;
	if ( ctrlp->cmsg_level != SOL_STII )
	    { error processing; }
	switch ( ctrlp->cmsg_type ) {
	default:
	case STReqRefuse:
	    close( s );
	    exit( 1 );
	    break;
	case STReqConnect:
	    break;
	}
							       [Page 13]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


    4.10.  sending data

	The origin may send data to the targets using any of the
    "write" functions: write(), send(), sendto(), or sendmsg().


	char data[] = "I'm alive!";

	error = write( s, data, strlen( data ) );
	if ( error != strlen( data ) )
	    { error processing; }

	error = send( s, data, strlen( data ), 0/*flags*/ );
	if ( error != strlen( data ) )
	    { error processing; }

	namep->st_request = STReqUnSpec;	/* not control */
	error = sendto( s, data, strlen( data ), 0/*flags*/,
			(struct sockaddr *) namep, namelen );
	if ( error != strlen( data ) )
	    { error processing; }


	#include <sys/uio.h>
	struct iovec datavec;

	datavec.iov_base = (caddr_t) data;
	datavec.iov_len = strlen( data );	/* namep/namelen */
	InitMsg(&msg,namep,namelen,&datavec,1,0,0);  /* optional */
	namep->st_request = STReqUnSpec;	/* not control */
	error = sendmsg( s, &msg, 0/*flags*/ );
	if ( error != strlen( data ) )
	    { error processing; }


	The origin should also be prepared to receive notifications,
    such as a disconnect request or error condition, while it is
    sending data, see 4.9.  Use of select() is suggested.















							       [Page 14]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


    4.11.  recvmsg at targets to receive data or control messages

	The target enters a loop to receive data and control messages
    using recvmsg().  The recvmsg() function must be used since the
    cmsghdr structure in the "accrights" argument is used to pass
    control information to the application;  read()/recv()/recvfrom()
    must not be used.  Consequently, a return from recvmsg() with no
    data may not imply EOF or that the connection has been closed; the
    application must check the sockaddr_st2 st_request field to
    determine what action is required.


	char databuf[ MAX_DATAGRAM ];
	int disconnected = FALSE;
	struct iovec datavec;

	while ( ! disconnected ) {
	    datavec.iov_base = (caddr_t) databuf;
	    datavec.iov_len = sizeof( databuf );
	    InitMsg(&msg, namebuf,sizeof(namebuf), &datavec,1,
		    ctlbuf,sizeof(ctlbuf));
	    error = recvmsg( s2, &msg, 0/*flags*/ );
	    if ( error == -1 )
		{ error processing; break; }
	    namep = (struct sockaddr_st2 *) namebuf;
	    namelen = msg.msg_namelen;
	    if ( msg.msg_accrightslen > 0 ) {
		ctlp = (struct cmsghdr *) ctlbuf;
		if ( ctlp->cmsg_level != SOL_STII )
		    { non ST-II protocol processing; }
		parmp = (struct aParameter *) ctlp->cmsg_data;
		/* ctlp->cmsg_type is identical to namep->st_request */
		switch ( namep->st_request )
		{   case STReqXxx:
			process control;
			break;
		    case STReqDisconnect:
			process control;
			disconnected = TRUE;
		}
	    }
	    else if ( datavec.iov_len > 0 ) {
		process data;
	    }
	} /* end of while not disconnected */








							       [Page 15]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


    4.12.  sendmsg at origin to disconnect targets

	The origin can disconnect one or all targets from a connection
    gracefully.  Individual targets are disconnected by supplying a
    TargetList parameter containing the target(s) to be disconnected.
    The sendmsg() function is used.


	Instcmsghdr(disconn,InstaReasonCode(why);) = {
	    Initcmsghdr(disconn,STReqRefuse),
	    InitaReasonCode(ApplDisconnect)
	    /* No target list means ALL targets */
	};

	namep->st_request = STReqDisconnect;
	InitMsg(&msg,namebuf,namelen,0,0,&disconn,sizeof(disconn));
	error = sendmsg( s /* s2 at target */, &msg, 0/*flags*/ );
	if ( error == -1 )
	    { error processing; }


    4.13.  close

	The close() function is used to abort (ApplAbort) an
    application/ST interaction.


	close( s2 );
	close( s );


    4.14.  select

	The select() function may be used in the usual manner to
    be signaled of the receipt of control or data messages instead
    of blocking in accept() or recvmsg().  Non-blocking I/O may
    also be used when polling is desired.
















							       [Page 16]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


Appendix:  Previously received comments

Subject: API draft
From: Craig Partridge <craig@sics.se>
Date: Wed, 08 May 91 09:45:39 +0200
    
    Sorry to take so long to reply -- I've been trying to find a way to
constructively express my concerns rather than just express differences
and that's meant I had to think about my concerns more deeply. 

    My first, and probably most important concern, since I think it is
the cause of many problems, is that the API uses RAW sockets.  RAW
sockets typically imply that all the kernel does is put the packets you
give them out onto the wire.  So as an application writer of a RAW ST-2
socket, I wouldn't expect the kernel to do more than pass my ST-2
datagrams out onto the network, and to give me all ST-2 datagrams
received. 

    But ST-2 has a lot of state that it needs to keep.  So if the kernel
doesn't keep state (and it shouldn't for a RAW socket), then the
application does.  Reading the API, I suspect that in fact the kernel is
keeping state for the application (rexmiting SCMP datagrams, etc.).  I
view that as surprising.  If I've misunderstood and the kernel is not
keeping state, then I don't see any point in using socket -- the
application will have to do 90% of the ST-2 protocol processing and
could just as easily send over a RAW Ethernet socket as send via the RAW
ST-2 socket. 

    In short, I think the RAW interface is fundamentally wrong.  Either
way it gets used appears broken.  I'd suggest instead the use of a
regular socket, just as SUN did for their X.25 interface (and indeed, as
we here have done for our ST-2 implementation). 

    By the way, what's the contents of a struct sockaddr_st2? My view is
that it ought to be just an ST-2 address, viz:

    struct sockaddr_st2 {
	u_short ss2_family;	/* address family == AF_INET */
	struct in_addr ss2_addr;	/* IP address of target */
	u_char ss2_sap[106];	/* as much SAP as fits into an mbuf */
    };

The sockaddr is not the way to convey options (which unfortunately
appears to be the way the API does things).  That's a role for the
[sg]etsockopt() routines.  In other words, an application should call
socket() and then set whatever options are required before calling
connect().  Some reasonable defaults should be built into the kernel. 
(For example, our system, if you call socket(), the stream, including
flow spec, is by default configured for voice traffic). 

    As for refusing and accepting connections -- the BSD interface
already supports that.  There's the accept() call and close().  This

							       [Page 17]

INTERNET DRAFT    Notes for Application Implementors on       March 1992
		           an ST-II Socket API


interface isn't perfect -- accept will accept a connection before the
application can examine the flow spec -- but that's OK, the application
can close in those cases it doesn't like the flow spec.  Close causes a
REFUSE to be sent.  On the sending side it is a little tricker, because
one wants to be able to close selected targets -- that's why we proposed
a disconnect() system call.  If one really wants to send the reason
code, one can use setsockopt to set the reason code before calling
close()/disconnect(). 

    Another problem with RAW sockets is that recvmsg has to be used to
get control information.  I believe control information should rarely
leave the kernel and that the preferred reading routine is recv() or
read().  The ST-2 in the kernel should negotiate control information,
not higher layers. 

    One thing that really makes me feel strongly that RAW is not the way
to go is that the API programming interface feels like unpleasant C code
to me.  I expect it to be possible to write applications of the form:

    if ((s = socket(AF_INET,SOCK_STREAM,IPPROTO_ST2)) < 0)
    {
	perrors("socket");
	exit(1);
    }

    sst2.ss2_family = AF_INET;
    sst2.ss2_addr = inet_addr(argv[1]);
    (void)bcopy(sst2.ss2_sap,argv[2]);

    if (connect(s,(struct sockaddr *)&sst2,sizeof(sst2)) != 0)
    {
	perror("connect"):
	exit(2);
    }

    /* something like this line, repeated multiple times  */
    if (send(s,data,datalen,0) != datalen)
    {
	perror("send");
	exit(3);
    }

    (void) close(s);

The API as it currently stands requires lots of header parsing, and
subroutine calls to configure a sockaddr.  That just feels uncomfortable
to me. 

Hope this is helpful.

Craig


							       [Page 18]