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 #include #include 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 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 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]