Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Networking With Open Transport / Part 1 - Open Transport Essentials
Chapter 11 - TCP/IP Services


Using TCP/IP Services

This section describes how to use the Open Transport RawIP interface, how to implement IP multicasting, and how to use a variety of Open Transport endpoint and mapper functions with the TCP/IP protocols. TCP/IP options are described in "Options".

Setting Options When Configuring a TCP/IP Provider

When you open a TCP/IP provider, you must pass a pointer to a configuration string. If you want to set an option as part of the configuration string, you should translate the option's constant name, given in the header files, into a string that the configuration functions can parse. For the TCP/IP options, Table 11-2 provides the constant name and the value to use in the configuration string.
Configuration strings for TCP/IP options
Constant nameConfiguration string value
IP_OPTIONS"Options"
IP_TOS"TOS"
IP_TTL"TTL"
IP_RCVDSTADDR"RcvDestAddr"
IP_RCVIFADDR"RcvIFAddr"
IP_RCVOPTS"RcvOpts"
IP_REUSEADDR"ReuseAddr"
IP_DONTROUTE"DontRoute"
IP_BROADCAST"Broadcast"
IP_HDRINCL"HdrIncl"
IP_MULTICAST_IP"MulticastIF"
IP_MULTICAST_TTL"MulticastTTL"
IP_MULTICAST_LOOP"MulticastLoop"
IP_ADD_MEMBERSHIP"AddMembership"
IP_DROP_MEMBERSHIP"DropMembership"
IP_BROADCAST_IF"BroadcastIF"
UDP_CHECKSUM"Checksum"
UDP_RX_ICMP"RxICMP"
TCP_NODELAY"NoDelay"
TCP_OOBINLINE"OOBInline"
TCB_MAXSEG"MaxSeg"
TCP_NOTIFY_THRESHOLD"NotifyThreshold"
TCP_ABORT_THRESHOLD"AbortThreshold"
TCP_CONN_NOTIFY_THRESHOLD  "ConnNotifyThreshold"
TCP_CONN_ABORT_THRESHOLD"ConnAbortThreshold"
TCP_KEEPALIVE"KeepAlive"

The network configuration structure and OTCreateConfiguration function are described in the chapter "Getting Started" in this book.

Using RawIP

The Open Transport TCP/IP software modules provide a RawIP interface to the IP protocol. RawIP behaves for the most part identically to UDP, as a connectionless transactionless interface, but there are a few unique differences.

You can receive RawIP datagrams using a RawIP endpoint. You can create a RawIP endpoint by passing kRawIPName to OTCreateConfiguration and passing that configuration to the OTOpenEndpoint or OTOpenEndpointAsync function.

The RawIP interface facilitates the implementation of new protocols that use IP for datagram delivery. Therefore, in order to use a RawIP endpoint, you must specify a value for the protocol field in the IP datagram header. RawIP endpoints default to receiving ICMP (protocol 1) packets. You can change this by setting the generic XTI option XTI_PROTOTYPE, described in the chapter "Option Management" in this book. The option is a longword that is the IP protocol number to be used by the RawIP endpoint.

The data delivered to a RawIP endpoint includes the full IP header, which is 20 bytes long if it includes no IP options.

WARNING
If you open a RawIP endpoint, you are responsible for implementing the protocol that is a client of IP running over that endpoint. Because an improperly implemented protocol can cause the host to crash or cause the loss of data on the network, you should exercise caution when using Raw IP.

Receiving RawIP Datagrams

Normally, connectionless transactionless endpoints only support binding one endpoint to any given protocol address. RawIP is different in that it allows multiple endpoints to be bound to the same protocol address.

With one important exception, each RawIP endpoint bound to a specific protocol receives a copy of any inbound packets destined for that protocol. For example, if several "ping" programs are using ICMP on the same host, each would receive a copy of all inbound ICMP echo datagrams. The exception is that RawIP endpoints do not receive copies of packets addressed to IP protocols TCP (protocol 6) or UDP (protocol 17). This restriction optimizes the delivery of such packets to their corresponding high-level protocols.

One unusual behavior of RawIP endpoints is that the delivered packets have their Total Length field modified. The RawIP module subtracts the length of the IP header from the Total Length field. This behavior brings Open Transport's STREAMS RawIP more in line with RawIP under BSD UNIX. Therefore, you should not rely on the value of the Total Length field. If you need to know the total length of the packet, use the length as returned in the TNetBuf structure returned by the OTRcvUData function.

Sending RawIP Datagrams

You can also send RawIP datagrams using a RawIP endpoint. For sending, RawIP endpoints have two modes: a mode in which the RawIP interface generates the header for you and a mode in which you set the header yourself. The header-generated mode is the default, and it is useful if you are only interested in the payload of the RawIP packets you send.

For applications such as ping (ICMP), you can let the RawIP interface generate the headers using the RawIP endpoint default behavior, such as sending ICMP packets (protocol 1). In this case, you can change the protocol and IP options (such as IP_OPTIONS and IP_TTL) using option management functions, as described in the chapter "Option Management" in this book.

Manually Setting the IP Header

At times, however, the level of control provided by the IP level options is not enough. If you need to set a field in the IP header that is not handled by a defined option, you can do this by switching the RawIP endpoint to what is referred to as the header-included mode and setting up the IP header manually.

Internally, the RawIP module maintains a state that determines whether it should add an IP header to any outgoing packets. If the state is false (0), RawIP will automatically generate an IP header for any outgoing packets. If the state is true (1), RawIP expects the data you provide it to contain the IP header. (By default the state is false and RawIP generate headers for you automatically.)

You can change this bit explicitly using option management. Simply set the IP option IP_HDRINCL to a 4-byte integer containing either 0 or 1. The IP options are listed in "IP Options".

You can also change this state by changing the IP protocol (using the generic XTI option XTI_PROTOTYPE option) for the endpoint. If you set the IP protocol to IPPROTO_RAW (255) or IPPROTO_IGMP (2), the IP optionIP_HDRINCL state will be set to true. If you change the IP protocol to any other value, the IP option IP_HDRINCL state defaults to false.

IP protocol information sources
The IP Protocol option values are defined in Internet Standard 1 "Assigned Numbers," which can be found at ftp://ds.internic.net/std/std1.txt. The fields in the IP header are those defined in Internet Standard 5, which can be found at ftp://ds.internic.net/std/std5.txt.

Limitations of the Header-Included Mode

If you use the header-included mode, you need to be aware of some of its limitations. A number of the fields in the IP header are automatically modified by Open Transport, regardless of what values you set them to. These field names include:

Flags. The More Fragments (MF) bit and the reserved bit are cleared. The Do Not Fragment (DF) bit is set on all outgoing IP packets. OT uses the DF bit to implement its dynamic path MTU discovery. Because this behavior is implemented below the IP layer, you cannot change this behavior using the RawIP endpoint

You need to be careful when setting your own IP header. Even though some fields are automatically "corrected" by Open Transport, it is still possible to generate improperly formatted IP packets using a RawIP endpoint, which can result in loss of network data.

Note

Path MTU is described in RFC1191 (ftp://ds.internic.net/rfcl/rfc1191.txt).

Using IP Multicasting

Open Transport TCP/IP provides IP multicasting level 2, as described in RFC 1112. This feature is only relevant for RawIP and UDP endpoints.

To join a multicast group, use the IP_ADD_MEMBERSHIP option , passing in a TIPAddMulticast structure to specify the address and network interface of the group you wish to join. For a multihomed system, you can use the value kOTAnyInetAddress for the interface address to use the default multicast interface.

The time-to-live value for outbound multicast data defaults to 1; you can use the IP_MULTICAST_TTL option to set a different value. The time-to-live value is a hop count: each router that processes the datagram decrements the time-to-live and discards the datagram if the value reaches 0. Because every router that receives a multicast packet forwards it, a high time-to-live value for a multicast packet can cause the packet to propagate widely throughout the Internet. Therefore, keep this value as low as possible.

By default, Open Transport IP loops back multicast datagrams to any member of the group on the sending machine. Pass a value of T_NO to the option IP_MULTICAST_LOOP to turn off loopbacks.

Querying DNS Servers

In addition to the explicit simplified functions that are provided for the most commonly made queries such as name-to-address, address-to-name, system CPU and OS, and mail exchange queries, there is a generic query function, OTInetQuery, that you can use for any DNS query.

The OTInetQuery function allows you to use the Domain Name Resolver (DNR) for generic domain name service (DNS) queries. You can ask for any query type and class, and in response, Open Transport returns as many DNSQueryInfo structures as it can fit in the buffer you provide.

There are three types of responses: answers, authority responses, and additional information, and there are typically several of each type. Each response has its own DNSQueryInfo structure, with all the answers first, then all the authority records, then all the additional information. Authority responses refer you to DNS servers and other sources that may have helpful information for this answer and additional information responses provide address data for the servers and sources referred to in the authority records.

If, for example, you use the OTInetQuery function to find out the IP addresses for a name, you might get back 13 DNSQueryInfo structures in your answer buffer. Each DNS Query Information structure might then contain 2 IP address structures, 4 authority responses, and 7 additional information responses.

To help you parse this huge answer buffer, Open Transport provides two optional parameters for the OTInetQuery function, argv and argvlen, that create an array of pointers to the individual responses.

Avoiding Delay When Rebinding to TCP Connections

When a connection closes, TCP imposes a two-minute timeout on binding before the same port can be bound to again. This prevents stale data from corrupting a new connection. This is in strict compliance with the TCP standard.

You can work around this by using the IP_REUSEADDR option with the OTOptionManagement function. If you set this option on all of your listening endpoints before you bind, the limitation should disappear. The IP_REUSEADDR option allows you to bind multiple connected or closing endpoints to addresses with the same port number.

IMPORTANT
Note that even using the IP_REUSEADDR option, you can only bind a single endpoint in a state less than connected (that is, listening or unbound endpoints) to the same port at a given time. You can, however, bind any number of connected or closing endpoints.
The sample code shown in Listing 11-2 sets an option and assigns it to the location referenced by value. The endpoint is assumed to be in synchronous mode. If an error occurs, the function returns a negative result. If the option could not be read, a positive result (either T_FAILURE, T_PARTSUCCESS, or TREADONLY, or T_NOTSUPPORT) is returned.

Listing 11-2 Setting an option value

static OTResult SetFourByteOption(EndpointRef ep,
                           OTXTILevel level,
                           OTXTIName  name,
                           UInt32   value)
{
   OTResult err;
   TOption  option;
   TOptMgmt request;
   TOptMgmt result;
   
   /* Set up the option buffer to specify the option and value to
         set. */
   option.len  = kOTFourByteOptionSize;
   option.level= level;
   option.name = name;
   option.status = 0;
   option.value[0] = value;

   /* Set up request parameter for OTOptionManagement */
   request.opt.buf= (UInt8 *) &option;
   request.opt.len= sizeof(option);
   request.flags  = T_NEGOTIATE;

   /* Set up reply parameter for OTOptionManagement. */
   result.opt.buf  = (UInt8 *) &option;
   result.opt.maxlen  = sizeof(option);

   
   err = OTOptionManagement(ep, &request, &result);

   if (err == noErr) {
      if (option.status != T_SUCCESS) 
         err = option.status;
   }
            
   return (err);
}
In the body of the function, we use a TOption structure to represent the option buffer and initialize its fields to specify the option and value we want to set. Next, we initialize the request parameter of the OTOptionManagement function to reference the option buffer we just initialized. The length field is set to the size of the option buffer and the flags field is set to T_NEGOTIATE to specify that we want to set the option value specified in the option buffer.

You could invoke this function and set the IP_REUSEADDR option as follows:

err = SetFourByteOption(ep, INET_IP, IP_REUSEADDR, true);

Using General Open Transport Functions With TCP/IP

This section describes special considerations you must take into account for Open Transport functions when you use them with the Open Transport TCP/IP implementation. You should be familiar with the function descriptions in the chapters ""Endpoints Reference and "Mappers Reference"in this book before reading this section.

Obtaining Endpoint Data With TCP/IP

The following values can be returned by the info parameter to the OTOpenEndpoint, OTAsyncOpenEndpoint, and OTGetEndpointInfo functions when used with TCP/IP protocols.

IMPORTANT
The preceding table shows only what values are possible for each protocol. Be sure to to use the OTOpenEndpoint, OTAsyncOpenEndpoint, or OTGetEndpointInfo functions to obtain the current values for these parameters.
These fields and the significance of their values are described in more detail in "Endpoints Reference".

Using Endpoint Functions With TCP/IP

This section describes protocol-specific information about functions described in the chapter "Endpoints Reference". The functions are listed in the same order that they appear in that chapter.

OTBind

The OTBind function associates a local protocol address with the endpoint you specify. Use this function with the TCP and UDP protocols.

The addr field of the TBind structure refers to the local endpoint and so must specifically include a port number. Use an InetAddress structure, described in "Internet Address Structure", to specify this address.

Because the architecture of Open Transport TCP/IP provides for multihoming (although this feature has not yet been implemented), you can specify an IP address of kOTAnyInetAddress for the addr field to indicate that your application or process will accept packets from any TCP/IP interface that the user has configured in the TCP/IP control panel.

If you bind to an address of kOTAnyInetAddress, then the OTGetProtAddress function always returns an IP address of 0. In that case, you must use the OTInetGetInterfaceInfo function to determine the IP address of a running IP interface. However, if you pass in a valid address with a port number of kOTAnyInetAddress, the TCP/IP service provider assigns a port for you and the OTGetProtAddress function returns the assigned port number and the IP address.

You can use the OTInetGetInterfaceInfo function to get the IP addresses of all currently configured IP interfaces. Then, if you wish to receive packets from only a single interface, you can bind the endpoint to the address for that interface.

OTLook

The OTLook function checks for asynchronous events such as incoming data or connection requests. Use this function with the TCP protocol.

As soon as a segment with the TCP urgent pointer set (that is, expedited data) enters the TCP receive buffer, TCP posts the T_EXDATA event. The T_EXDATA event remains posted until you have retrieved all data up to the byte pointed to by the TCP urgent pointer.

OTGetProtAddress

You use this function with the TCP and UDP protocols.

If you bind an endpoint to an IP address of kOTAnyInetAddress in order to accept packets from any valid TCP/IP interface, then the OTGetProtAddress function always returns an IP address of 0. This is because in a multihomed machine, there is a separate IP address for each interface, and there's no way for Open Transport to know which one you want. In that case, you must use the OTInetGetInterfaceInfo function to determine the IP address of a running IP interface. On the other hand, if you bind an endpoint to a specific interface, the OTGetProtAddress function returns the address of that interface, as expected.

OTConnect

The OTConnect function requests a connection to a specified remote endpoint. You can use this function with TCP.

The rcvcall->addr field returns a copy of the TNetbuf structure you specify in the sndcall->addr field. The discon->reason field contains a positive error code that indicates why the connection was rejected.

Because TCP does not allow you to send any application-specific data during the connection establishment phase, you must set the sndcall->udata.len field to 0. TCP ignores the value of the sndcall->udata.buf field.

Note that TCP, not the receiving application, confirms the connection.

As mentioned in the X/Open Transport Interface (XTI) specification, because TCP cannot refuse a connection, t_listen() and t_accept() have a semantic which is slightly different from that for ISO providers."

As a result, an Open Transport TCP server will accept a TCP connection request if the current number of pending connections is less than the queue length (qlen) for the passive endpoint. Basically, what happens is that TCP connects even before you accept a connection.

The client, whether in synchronous or asynchronous mode, will immediately receive notice that the connection has been established. For synchronous endpoints, TCP completes the 3-way connection handshake. For asynchronous endpoints, the OTRcvConnect function must be called to complete the handshake.

This can result in situations like this: You send an OTConnect from a TCP client to a TCP server that passively awaits incoming connections, but even before the server responds with the OTListen and OTAccept calls, the OTConnect call completes with no error. At this point, if you examine the client endpoint's state, you will find that it is in the T_DATAXFER state, which is correct.

OTRcvConnect

The OTRcvConnect function reads the status of a previously issued connection request. You can use this function with TCP.

Because TCP does not allow you to send any application-specific data during the connection establishment phase, you must set the call->udata.maxlen field to 0. TCP ignores the value of the call->udata.buf field.

On return, the call->addr field points to the Internet address of the endpoint that accepted the connection.

OTListen

The OTListen function listens for an incoming connection request. You can use this function with TCP.

When the OTListen function successfully completes execution (that is, when you receive the T_LISTEN event), the call parameter describes a connection that has already been completed at the TCP level. You use the OTAccept function to complete a connection at the application level. If you wish to reject a connection, you must call the OTSndDisconnect function after the OTListen function successfully completes execution.

Because TCP does not allow you to send any application-specific data during the connection establishment phase, you must set the call->udata.maxlen field to 0. TCP ignores the value of the call->udata.buf field.

OTAccept

The OTAccept function accepts an incoming connection request. You can use this function with TCP.

Because TCP does not allow you to send any application-specific data during the connection establishment phase, you must set the call->udata.len field to 0. TCP ignores the value of the call->udata.buf field.

If you wish to send either of the association-related options (IP_OPTIONS or IP_TOS) with the connection confirmation, you must use the OTOptionManagement function to set the values of these options before you receive the T_LISTEN event. TCP has already established a connection when you receive the T_LISTEN event, and it is too late for the OTAccept function to negotiate these options.

OTSndUData

The OTSndUData function sends data through a connectionless transactionless endpoint. You can use this function with UDP.

The current value for the maximum size of a RawIP or UDP datagram is returned in the info->tsdu parameter of the OTOpenEndpoint, OTAsyncOpenEndpoint, and OTGetEndpointInfo functions.

OTSnd

The OTSnd function sends data through a connection-oriented transactionless endpoint. You can use this function with TCP.

Because it does not support TSDU's, TCP ignores the OTSnd function's T_MORE flag.

If you set the T_EXPEDITED flag, you must send at least 1 byte of data. If you call the OTSnd function with more than 1 byte specified and the T_EXPEDITED flag set, the TCP urgent pointer points to the last byte of the buffer.

OTRcv

The OTRcv function receives data through a connection-oriented endpoint. You can use this function with TCP.

Because TCP ignores the T_MORE flag when it is sending data and does not transmit the flag, you should ignore the T_MORE flag when receiving normal data. However, if a byte in the data stream is pointed to by the TCP urgent pointer, TCP receives this byte and as many bytes as possible preceding the marked byte with the T_EXPEDITED flag set. If your buffer is too small to receive all of the expedited data, TCP sets the T_MORE flag as well. Note that this situation might result in the number of bytes received as expedited data not being equal to the number of bytes sent by the originator as expedited data.

OTSndDisconnect

The OTSndDisconnect function initiates an abortive disconnect or rejects a connection request. You can use this function with TCP.

Because TCP does not allow you to send any application-specific data during a disconnect, you must set the call->udata.len field to 0. TCP ignores any data in the call->udata.buf field.

OTRcvDisconnect

The OTRcvDisconnect function returns information about why a connection attempt failed or an established connection was terminated. You can use this function with TCP.

Because TCP does not allow you to send any application-specific data during a disconnection, you must set the discon->udata.len field to 0. TCP ignores the value of the discon->udata.buf field.

This function returns a positive error code. To obtain the negative error code, subtract that positive value from -3199.

Using Mapper Functions With TCP/IP

This section describes protocol-specific information about functions described in the chapter "Mappers Reference" in this book. The functions are listed in the same order that they appear in that chapter.

OTRegisterName

Because the TCP/IP domain name system does not include a method for clients to register their names on the network, the Open Transport domain name resolver (DNR) does not support the OTRegisterName function. If you call this function for a TCP/IP mapper, it will return the kOTNotSupportedErr result code.

OTDeleteName

This function is not supported by the TCP/IP domain name resolver (DNR). If you call this function for a TCP/IP mapper, it will return the kOTNotSupportedErr result code.

OTLookupName

You can use the OTLookupName function to resolve a domain name to an Internet address. Specify the name as a character string pointed to by the request->udata.buf parameter. The name can be just a host name ("otteam"), a partially qualified domain name ("otteam.ssw"), a fully qualified domain name ("otteam.ssw.apple.com."), or an Internet address in dotted-decimal format ("17.202.99.99"), and can optionally include the port number ("otteam.ssw.apple.com:25" or "17.202.99.99:25").

The function returns a pointer to the address in the reply->udata.buf parameter. The address is in the format of an InetAddress structure , which includes the address type, the port number, and the IP address. If you don't specify a port number, the returned InetAddress structure contains a port number of 0. You can use this address directly in all Open Transport functions that require an Internet address, such as OTConnect, OTSndUData, and OTBind.

The OTLookupName function returns only a single address, regardless of how many addresses are known for a single multihomed host. To obtain a list of up to 10 addresses for a multihomed host, use the OTInetStringToAddress function .


Subtopics
Setting Options When Configuring a TCP/IP Provider
Using RawIP
Receiving RawIP Datagrams
Sending RawIP Datagrams
Manually Setting the IP Header
Limitations of the Header-Included Mode
Using IP Multicasting
Querying DNS Servers
Avoiding Delay When Rebinding to TCP Connections
Using General Open Transport Functions With TCP/IP
Obtaining Endpoint Data With TCP/IP
Using Endpoint Functions With TCP/IP
Using Mapper Functions With TCP/IP

Previous Book Contents Book Index Next

© Apple Computer, Inc.
15 JAN 1998