Museum

Home

Lab Overview

Retrotechnology Articles

Online Manuals

⇒ socketx25(7) — HP-UX 9.10

Media Vault

Software Library

Restoration Projects

Artifacts Sought

Related Articles

getsockopt(2)

ioctl(2)

socket(2)

socket(7)

socketx25(7)

Requires Optional LAN/X.25 Software

NAME

socketx25 − Interprocess communications via AF_CCITT sockets

DESCRIPTION

Sockets are communication endpoints that allow processes to communicate either locally or remotely.  They are accessed by means of a set of system calls (see socket(2)). The discussion below describes ioctl(2) calls available when directly accessing the X.25 Packet level (level 3) via sockets. 
 That is, when the sockets are in the address family AF_CCITT.  Refer to the af_ccitt(7F) manual entry for details.

COMMAND FUNCTIONS

The following ioctl(2) requests are defined in <x25/x25ioctls.h>:

X25_RD_HOSTADR Loads the X.121 address of an X.25 interface on the local host into an x25addrstr struct.  Example:

struct x25addrstr bind_addr;
bind_addr.ifname[0] = ’\0’;
error = ioctl(s,X25_RD_HOSTADR,&bind_addr);

X25_WR_MASK_DATA Sets a bit mask to be anded with Protocol-IDs for address-matching during a bind() call. Each byte of the bit mask is set in an entry in the x25_mask[] array in an x25_mask_data data structure.  The length (in bytes) of the bit mask is stored in the x25_masklen field of an x25_mask_data data structure.  x25_mask_data is defined in the x25str.h header file.  For example, to set an address mask accepting Protocol-IDs in the range 0xc033 to 0xcf33:

int s;
struct x25_mask_data in_parms;
in_parms.x25masklen = 2;
in_parms.x25_mask[0] = 0xf0;
in_parms.x25_mask[1] = 0xff;
error = ioctl(s, X25_WR_MASK_DATA, &in_parms);

X25_RESET_VC Resets a Virtual Circuit (VC) associated with a particular socket, which might cause data loss.  Issuing this call sends an out-of-band event (OOB_VC_RESET) to the peer user.  If blocking I/O is being used, ioctl() blocks until an acknowledgement is received from the remote node. Example:

error = ioctl(s, X25_RESET_VC, 0);

X25_SEND_TYPE Sets the D-bit or the Q-bit in a sequence of X.25 packets.  Also controls whether the next send(2) will send a complete or a partial X.25 message via the More-Data-To-Follow (MDTF) bit setting.  On Series 700/800 systems, setting the D-bit lets the sending process indicate that it requires acknowledgement when the end of a message has been received.  If a Series 700/800 system receives a data packet with the D-bit set and the socket has been set to permit D-bit operations, it will ackowledge only when the receiving process has read the data.  If a Series 300/400 system receives a data packet with the D-bit set, it acknowledges automatically, without guaranteeing that the receiving process has read the data.  Any attempt to set the D-bit on a Series 300/400 system is rejected and the error [EINVAL] is returned. 

Setting the Q-bit indicates that the data being sent is significant to a device connected to the remote host.  Set these bits by shifting the appropriate bit mask to the left one bit and ORing the mask with a user-defined send_type variable as in this example:

int s, send_type, error;
send_type |= 1<<X25_Q_BIT; /* set Q*/

or

send_type |= 1<<X25_D_BIT; /* set D*/
error = ioctl(s, X25_SEND_TYPE, &send_type);

To use the D-bit or Q-bit, they must be set before the first send() in a message. Use X25_SEND_TYPE to set the D-bit prior to connect, or X25_SEND_CALL_ACCEPT to set the connection to permit use of D-bit data sends. 

To send a partial X.25 message, MDTF bit should be set to 1.  To send the final segment or to flush out a complete message, the MDTF bit should be reset to 0 before the send(2). Example:

intsocket, send_type, first_msglen,
second_msglen, third_msglen;
u_char*first_msg, second_msg, third_msg;

/* set MDTF indicator to indicate multiple message
* fragments ....
*/
send_type = 1 << X25_MDTF_BIT;
ioctl(socket,X25_SEND_TYPE,&send_type);

/* send the first two message fragments ....
*/
return = send(socket, first_msg, first_msglen, 0);
return = send(socket, second_msg, second_msglen, 0);

/* now clear the MDTF indicator ....
* and send the third (final)  message fragment ...
*/
send_type = 0;
ioctl(socket,X25_SEND_TYPE,&send_type);
return = send(socket, third_msg, third_msglen, 0);

NOTE: The values of the Q bit and the D bit must remain constant throughout a complete X.25 message sequence.  Therefore, the values of the D and Q bits are latched from the first X25_SEND_TYPE ioctl(), and any attempt to change them during transfer is ignored.

X25_NEXT_MSG_STAT Returns status information on the next-available (unread) message, such as its size, whether message is partial or complete, whether the D and Q bits are set, and whether Call-user or clear data is available.  This information is returned in an x25_msg_statstruct.  Example:

/* Globals
*/
int     MDTF;
char    *malloc();
......

intsocket, count;
unsigned intlength;
char*fragment;
struct x25_msg_statnext_msg_stat;

ioctl(socket, X25_NEXT_MSG_STAT, &next_msg_stat);
if (next_msg_stat.x25_msg_flags &
(1 << X25_MDTF_BIT)) {
/* this is a fragment of the complete X.25
message ...
*/
MDTF = TRUE;
printf( More Data To Follow ....0);"
} /* if next_msg_stat .... */
else {
/* MDTF indicator is not set, use the MDTF
* global to determine if this the final
* fragment in a message sequence or a complete
* message sequence ....
*/
if (MDTF == TRUE) {
/* this is the final fragment in the message
* sequence ....
*/
printf( Final fragment of X.25 message
printf( .0);"
} /* if MDTF == TRUE .... */
else {
/* this is a complete X.25 message sequence.
*/
printf( Complete X.25 message received.0);"
} /* else MDTF == FALSE .... */

/* now set MDTF to FALSE ....
*/
MDTF = FALSE;
/* get state of D and Q bits
*/
if (next_msg_stat.x25_msg_flags & (1<<X25_D_BIT)) {
/* D bit for this sequence has been set ....
*/
printf( , D bit set for
}
if (next_msg_stat.x25_msg_flags & (1<<X25_Q_BIT)) {
/* Q bit for this sequence has been set ....
*/
printf( , Q bit set for
}

} /* else next_msg_stat .... */

/* now get enough memory to receive the message ....
*/
length = (unsigned int)next_msg_stat.x25_msg_size;
message = malloc(length);
if (message == NULL) {
/* malloc couldn’t get memory ....
*/
}
count = recv(socket, message, (int) length, 0);

Note that the Q bit value remains the same during subsequent receives of the fragments in a complete X.25 message sequence.  The D bit status for a complete X.25 message sequence are returned before the recv() of the FINAL fragment.  The D bit status is also returned before the recv() of a complete X.25 message sequence. 

X25_WR_CAUSE_DIAG Sets the cause code and diagnostic code for the next user-initiated RESET or CLEAR packet.  Cause and diagnostic codes are set in the x25_cause_diag data structure defined in the x25str.h header file.  The cause code, if non-zero, will have its most significant byte set by the X.25 subsystem as it appears in the X.25 packet.  Set the cause and diagnostic codes as follows:

struct x25_cause_diag diag;
int s; /* socket */

diag.x25_cd_cause = cause_code;
diag.x25_cd_diag =  diagnostic_code;
error = ioctl(s, X25_WR_CAUSE_DIAG, &diag);

X25_CALL_ACPT_APPROVAL Gives user the option to accept incoming calls on a listen () socket.  When the X25_CALL_ACPT_APPROVAL ioctl() call is issued, a new accept() socket is created whenever a valid call comes in, but the call is not accepted at the X.25 level until an X25_SEND_CALL_ACEPT ioctl() call is issued. Until the X25_SEND_CALL_ACEPT ioctl() call is issued, no data can be sent or received on the new socket. If the application does not want to accept the call, the circuit can be cleared with close(). Call acceptance cannot be turned-off for a socket once the X25_CALL_ACPT_APPROVAL ioctl() is issued. The call can be issued as follows:

error = ioctl(s, X25_CALL_ACPT_APPROVAL, 0);

where s is a listen socket; that is, a listen() system call has been issued on it.

X25_SEND_CALL_ACEPT Accepts a call on a new socket returned by accept() when the listen socket has an X25_CALL_ACPT_APPROVAL ioctl() issued against it; otherwise it is illegal. Once X25_SEND_CALL_ACEPT is issued against a new socket, the socket is in a data-transfer state.  Accepting the new socket is described below:

error = ioctl(new_s, X25_SEND_CALL_ACEPT, 0);

where new_s is the new socket created by accept().

X25_SETUP_PVC
Binds a socket to a Permanent Virtual Circuit (PVC).  The user must create a socket, then enter the X.25 interface name and the PVC’s Logical Channel Number (LCI) in the x25_setup_pvc_str data structure defined in the <x25/x25str.h> header file, before issuing the X25_SETUP_PVC ioctl() call. This ioctl() call is the only call required for setting-up a connection on a PVC; listen(), accept(), and connect() are not required for establishing a connection on a PVC.  Once the ioctl() call completes successfully, the socket is in a connected state and data can be transmitted over the connection. For example,

struct x25_setup_pvc_str pvc_str;
int pvc_so;
pvc_so = socket(AF_CCITT, SOCK_STREAM, 0);
/* Set the interface name in pvc_str.ifname.
*  Store the LCI in pvc_str.lci. */
error = ioctl(pvc_so, X25_SETUP_PVC, &pvc_str);

X25_RD_USER_DATA Reads data from the Call-user data field  of a CALL CONNECTED or INCOMING CALL packet, or reads the data field of a CLEAR packet.  Use the X25_NEXT_MSG_STAT ioctl() call to determine that CALL or CLEAR data is available, then issue the X25_RD_USER_DATA ioctl() call. The call transfers user data to the x25_userdata data structure defined in <x25str.h>, up to 126 bytes at a time.  If more than 126 bytes of user data is available, the application should loop, calling X25_NEXT_MSG_STAT and X25_RD_USER_DATA until all the user data is read.  This call is useful for fast-select.  The example below calls X25_NEXT_MSG_STAT to determine whether CALL-user data available, then reads at most 126 bytes of data. 

struct x25_userdata userdata;
struct x25_msg_stat msgstat;
error = ioctl(s, X25_NEXT_MSG_STAT, &msgstat);
if (msgstat.x25_msg_flags & ( 1 << X25_CA_DATA_AVAIL))
error =  ioctl(s, X25_RD_USER_DATA, &userdata);

X25_WR_USER_DATA
Writes to the Call-user data field in the following situations:

• Before issuing a connect() call,

• Before issuing an accept() call when Call-accept approval is in effect,

• Before issuing a close() call (which sends a CLEAR packet over the connection); otherwise, it has no effect. 

If more than 16 bytes of Call and Clear data are written, the fast-select facilities code must be set with the X25_WR_FACILITIES ioctl() call. Only 126 bytes of Call and Clear data can be written per ioctl() call. If the total amount of user data is greater than 126 bytes, X25_WR_USER_DATA returns with an EINVAL error.  This example copies CALL-user data from the array udata to the userdata structure, then writes the CALL-user data field:

struct x25_userdata userdata;
unsigned char udata[128];
int i, j = 0, ndata;
ndata =  num_bytes_CALL_user_data;
while (ndata > 0) {
for (i = 0; ((ndata-- > 0) && ( i <= X25_MAX_CU_DATA)); i++)
userdata.x25_cu_data[i] = udata[j++];
userdata.x25_cud_len = i;
error = ioctl(s, X25_WR_USER_DATA, &userdata);
if (error)
break;
}

X25_RD_FACILITIES Reads any inbound facilities data received with an INCOMING CALL, CALL CONNECTED, CLEAR indication, or CLEAR confirm packet.  Facilities are defined in Section 7 of the CCITT X.25 Recommendations.  Facilities data cannot be reread, but the X25_RD_FACILITIES ioctl () call can be issued again if new facilities data is received.  X25_RD_FACILITIES cannot read outbound facilities data just written with the X25_WR_FACILITIES ioctl () call. 

X25_WR_FACILITIES Sets up the facilities field.  Facilities are defined in Section 7 of the CCITT X.25 Recommendations.  The network provider might impose certain restrictions on facilities.  If no facilities are specified before initiating a call, default flow-control facilities will be used.  The programmer should ensure that the facilities written contain valid values and ranges as defined by the CCITT X.25 Recommendations and allowed by subscription by the network provider.  If a user issues the X25_WR_FACILITIES ioctl() call with a field length of 0, no facilities are used for that connection. This call overwrites any outbound facilities data that hasn’t been sent. Facilities data can also be overwritten by receiving a CALL packet or a CLEAR packet with a facilities field.  The example below copies faciliies data from the udata[] array to the facilities data structure. Facilities data is stored in an x25_facilities data structure defined in <x25/x25str.h>:

struct x25_facilities fac_data;
unsigned char udata[128];
int ndata;
/*  ndata" counts the number"
*of bytes of facilities data.
* udata[] contains the facilities data.
*/
memcpy(fac_data.x25_fac, udata, ndata);
fac_data.x25_fac_len = ndata;
error = ioctl(s, X25_WR_FACILITIES, &fac_data);
}

X25_RD_CTI Returns the Circuit Table Index (CTI) associated with a particular socket.  Knowing the CTI enables the user to read statistics and logging messages for a virtual circuit, as well as examine the contents of a network log file such as those created by netlogstat. The ioctl() call is issued as follows:

int error, s, cti;
error = ioctl (s, X25_RD_CTI, &cti);

X25_RD_CTI returns an error if the

X25_RD_LCI Returns the Logical Channel Index (LCI) associated with a particular virtual circuit.  Knowing the LCI enables the user to relate logging messages to a protocol-analyzer trace.  The ioctl() call is issued as follows:

int error, s, lci; error = ioctl (s, X25_RD_LCI, &lci);

X25_RD_CTI returns an error if the circuit is not fully connected. 

X25_WR_WTHRESHOLD Sets the write threshold for a socket.  This call is used with non-blocking I/O and select() calls. A socket is considered write-selectable if there is enough outbound buffer space for an X.25 message of the size specified with X25_WR_WTHRESHOLD.  Default: 1 byte.  Range: 1 byte to maximum send() message size specified with setsockopt(SO_SNDBUF).  The maximum outbound message size is 4096 bytes by default.  Issue the X25_WR_WTHRESHOLD ioctl () call as follows:

int s, wthresh, error;
wthresh = 1024;
error = ioctl( s, X25_WR_WTHRESHOLD, &wthresh);

X25_WR_CALLING_SUBADDR Specifies a calling subaddress field to be included in the CALL REQUEST packet when initiating a connection request.  This ioctl() is useful for applications where a client process initiates a virtual circuit and sends data to a server process which then shuts down the circuit; the server process manipulates the data, then reestablishes a virtual circuit with the same client (using a subaddress to identify it). The X25_WR_CALLING_SUBADDR ioctl() call enables the client to send addressing information to the server, which the server can retain after a connection is shut down. The addressing information is an X.121 sub-address.  The sequence of system calls for executing the above procedure are summarized below:

1.  The client loads X.121 sub-address information into an x25addrstr struct.  Only the address family, subaddress, and size of subaddress fields in x25addrstr are used:

#define SUBADDR = "12"
struct x25addrstr addrstr;
addrstr.x25_family = AF_CCITT;
addrstr.x25_hostlen = strlen(SUBADDR);
strncpy(addrstr.x25_host, SUBADDR, X25_MAXHOSTADDR);

2.  The client binds to the address that the server will "call back", then listens on that socket:

error = bind(s, &myaddr, sizeof(struct x25addrstr));
error = listen(s, 5);

3.  The client issues the X25_WR_CALLING_SUBADDR ioctl() call, with the subaddress information and all of the other fields of the structure stored in addrstr, then issues the connect() call:

error = ioctl(s, X25_WR_CALLING_SUBADDR, &addrstr);
error = connect(s, &to_addr, sizeof(struct x25addrstr));

4.  The server receives the request, accepts the connection, issues getpeername() on the calling socket to obtain the addressing information, receives the data, then clears the connection.

/* accept the connection request */
new_s = accept(s, &from_addr, &from_addr_len);
/* obtain addressing information for peer socket */
error = getpeername(new_s, &from_addr_info, sizeof(struct x25addrstr));
/* Clear the VC */
error = close(new_s);

For simplicity, no data exchanges between the client and server are shown, but presumably there will be one or more messages transmitted between them. 

5.  The server reestablishes a connection with the client process (which has been listening on the socket s) by creating a call-back socket, then issuing a connect() request using the subaddressing information and other X.25 addressing information from the old socket. 

call_back_s =
socket(AF_CCITT, SOCK_STREAM, X25_PROTO_NUM)
error = connect(call_back_s, &from_addr_info,
sizeof(struct x25addrstr));

X25_SET_FRAGMENT_SIZE Sets fragment size for reception of an X.25 message in fragments.  Normally, X.25/9000 software sends data to the user in complete X.25 message sequences.  If it is desired to receive X.25 normal data in fragments of the complete X.25 sequences, the X25_SET_FRAGMENT_SIZE I/O Control (see ioctl(2)) call must be used. This ioctl() informs the X.25/9000 software that inbound X.25 normal data should be sent to the user when the reassembled data size is equal to or greater than the size specified in the X25_SET_FRAGMENT_SIZE ioctl(). When the end of a complete X.25 message sequence is encountered, the remaining data is sent to the user. 

The X25_SET_FRAGMENT_SIZE ioctl() can be used to set inbound fragment sizes up to 32767 bytes (32K). Values greater than 32K cause the ioctl() to return with an error indicating that the fragment size is too large, and the operation will be ignored. To return to receiving complete X.25 message sequences, set fragment_size to zero (0). 

The semantics of the X25_SET_FRAGMENT_SIZE ioctl() is shown below:

intsocket, fragment_size;
externint errno;

error=ioctl(socket, X25_SET_FRAGMENT_SIZE,
&fragment_size);
if ((error == -1) && (errno == EMSGSIZE)) {
printf( fragment_size too big0);"
}

if ((error == -1) && (errno == ENOTCONN)) {
printf( circuit not connected0);"
}

The received fragment may be slightly larger than the fragment_size specified in the X25_SET_FRAGMENT_SIZE ioctl(). This is due to some inbound data reassembly being performed on the X.25 interface.  It is highly recommended that the X25_NEXT_MSG_STAT ioctl() be used to determine the size of the message before it is received. 

The received fragment may also be smaller than the fragment_size specified in the X25_SET_FRAGMENT_SIZE ioctl(). This is due to the detection of the end of a complete X.25 message sequence. 

The X25_SET_FRAGMENT_SIZE ioctl() can be used only when the circuit is connected. It can be used at any time during the life of the circuit.

Inbound and outbound socket buffers should be set to at least the fragment size plus 4K (4096).  This ensures that the proper amount of memory is reserved for operation of the virtual circuit.  The default socket buffer size is 4K (4096 bytes). 

DEPENDENCIES

Series 300/400 X.25:

Series 300/400 systems do not support setting the D-bit programmatically. 

AUTHOR

socketx25 was developed by HP. 

FILES

<x25/x25ioctls.h>
<x25/x25str.h>
<x25/x25addrstr.h>
<x25/x25com.h>

SEE ALSO

getsockopt(2), ioctl(2), socket(2), socket(7),

CCITT Data Communication Network Interfaces Recommendation X.25 ,

X.25 Programmer’s Manual.

Hewlett-Packard Company  —  HP-UX Release 9.10: April 1995

Typewritten Software • bear@typewritten.org • Edmonds, WA 98026