This chapter contains examples of how to make and receive calls. All of the examples involve the application opening a stream to the X.25 PLP Driver. Once the stream has been opened, it can be used for initiating, listening for, or accepting a connection. There is a one-to-one mapping between X.25 virtual circuits and PLP driver streams. Once a connection has been established on a stream, the stream cannot be used other than for passing data and protocol messages for that connection.
Sample code for making OSI CONS calls, dealing with Expedited Data and Resets and receiving a remote disconnect is given at the end of this section.
There are copies of the code samples referred to in this chapter in the /opt/SUNWconn/x25/samples.nli directory.
This section shows the process for making a single, straightforward call. The call being made is a standard X.25 call. It does not have to deal with Expedited Data or Resets. The disconnect is initiated locally. The steps for making a standard X.25 call are:
Open a stream on the /dev/x25 device:
if ((x25_fd = open("/dev/x25", O_RDWR)) < 0) { perror("Opening Stream"); exit(1); }
Open a connection to the open stream.
Allocate a Connect Request structure.
Supply any quality of service and facilities parameters that are required.
Set the called (and optionally calling) addresses.
Pass the Connect Request down to the X.25 Driver.
Wait for the connect confirmation or rejection
#define FALSE 0 #define TRUE 1 #define CUDFLEN 4 #define DBUFSIZ 128 #include <memory.h> #include <netx25/x25_proto.h> struct xaddrf called = { 0, 0, { 14, { 0x23, 0x42, 0x31, 0x56, 0x56, 0x56, 0x56 }}, 0 }; /* no flags * DTE = "23423156565656", null NSAP */ struct xcallf conreq; struct strbuf ctlblk, datblk; struct xdataf data; main () { . /* Convert link to internal format */ called.link_id = 0; conreq.xl_type = XL_CTL; conreq.xl_command = N_CI; conreq.CONS_call = FALSE; /* This is not a CONS call */ conreq.negotiate_qos = FALSE; /* Just use default */ memset(&conreq.qos, 0, sizeof(struct qosformat)); memcpy(&conreq.calledaddr, &called, sizeof(struct xaddrf)); memset(&conreq.callingaddr, 0, sizeof(struct xaddrf)); }
In the example, the entire QOS field is zeroed, allowing for future additions to the structure. Setting the calling address to null, as shown, leaves the network to fill in this value. For more information on QOS and Facilities, see "2.7 Facilities and QOS Parameters".
Send the message on the stream using the putmsg system call, passing any call user data in the data part of the message:
char cudf[CUDFLEN] = { 1, 0, 0, 0 }; ctlblk.len = sizeof(struct xcallf); ctlblk.buf = (char *) &conreq; datblk.len = CUDFLEN; datblk.buf = cudf; if (putmsg(x25_fd, &ctlblk, &datblk, 0) < 0 ) { perror("Call putmsg"); exit(1); }
Transfer the data.
In the data transfer phase, access is given to:
Expedited data, to support X.29 and OSI CONS.
Normal and Q-bit data is sent and received using the N_Data message and may be acknowledged using the N_DAck message. Expedited data uses the N_EData message, and is acknowledged using an N_EAck message.
Once a connection has been successfully opened on a stream, sending a data packet is straightforward:
char datbuf[DBUFSIZ]; /* Copy data into datbuf[] here*/ data.xl_type = XL_DAT; data.xl_command = N_Data; data.More = data.setQbit = data.setDbit = FALSE; ctlblk.len = sizeof(struct xdataf); ctlblk.buf = (char *) &data; datblk.len = DBUFSIZ; datblk.buf = datbuf; retval = putmsg(x25_fd, &ctlblk, &datblk, 0);
Normally, the call to putmsg blocks if there are flow control conditions in the connection which lead to either a full queue at the stream head, or a lack of streams resources. To avoid blocking due to a full queue, open the stream with the option O_NDELAY flagged. In this case, putmsg returns immediately, and the failure is signalled by a return value (retval) of EAGAIN.
This procedure allows the application to carry out other processing (for example, receiving data) before trying again. The best method to use depends on the nature of the application.
Close the connection.
In this example, closure is initiated locally. The application sends a Disconnect Request (N_DI) message on the stream. Unless this is being used to reject an incoming call the X.25 driver signals that it has observed the message. It does this by sending a Disconnect Confirm upstream when it receives the Clear Confirm. In this way, the upper components can be certain that no messages will follow the Disconnect.
In the case of rejection, the connection identifier supplied on the Connect Indication must be returned in the disconnect message. The disconnect (reject) is not acknowledged in this case.
As in the case of a remote disconnection, once the response has been received the stream becomes idle, and remains in this state until the application sends out another control message. This may be to close the stream, or to initiate a new Listen or Connect request on it. The application should, however, not send any of these messages until it receives the Disconnect Response.
As described in "6.4.7 N_DI--Clear Request/Indication ", a disconnect collision may occur. If this happens, no Clear Confirm is sent.
/* Coded and sent disconnect request, process response */ struct xdiscf *dis_ind; struct xdcnff *dis_cnf; struct extraformat *xqos = (struct extraformat *)0; if ( hdrptr->xl_type == XL_CTL ) { switch( hdrptr->xl_command ) { /* Disconnect Collision */ case N_DI: dis_ind = (struct xdiscf*)hdrptr; xqos = &dis_ind->indicatedqos.xtras; break; /* Disconnect Confirmation */ case N_DC: dis_cnf = (struct xdcnff*)hdrptr; xqos = &dis_cnf->indicatedqos.xtras; break; default: return; } if ( xqos ) { /* * Print any charging information returned */ if ( xqos->chg_cd_len ) { /* Print out Call Duration from chg_cd_field */ } if ( xqos->chg_mu_len ) { /* Print out Monetary Unit from chg_mu_field */ } if ( xqos->chg_sc_len ) { /* Print out Segment Count from chg_sc_field */ } } /* end if (xqos) */ } /* end if (hdrptr->xl_type==XL_CTL) */
In the same way as sending data, data reception is straightforward. When data is received with the D-bit set, action may be required by the application. When the initial Call Request is sent, it may request that data confirmation be at the application-to-application level. If application-to-application confirmation is agreed upon, then on receiving a packet with the D-bit set, an application must send a Data Acknowledgment (N_DAck) message.
This example prints out incoming data as a string, if the Q-bit is not set:
S_X25_HDR *hdrptr; struct xdataf *dat_msg; struct xdatacf *dack; for(;;) { if (getmsg(x25_fd, &ctlblk, &datblk, &getflags) < 0) { perror("Getmsg fail"); exit(1); } hdrptr = (S_X25_HDR *) ctlbuf; if (hdrptr->xl_type == XL_CTL) { /* Deal with protocol message as required - * see below */ } if (hdrptr->xl_type == XL_DAT) { dat_msg = (struct xdataf *) ctlbuf; switch (dat_msg->xl_command) { case N_Data: if (dat_msg->More) printf("M-bit set \n"); if (dat_msg->setQbit) printf("Q-bit set \n"); else { if (dat_msg->setDbit) printf("D-bit set \n"); for (i = 1;i<datblk.len; i++) printf("%c", datbuf[i]); /* * If application to application * Dbit confirmation was negotiated * at call setup time, * send an N_DAck */ if (app_to_app && dat_msg->setDbit) { dack = (struct xdatacf *)malloc(sizeof(struct xdatacf)); memset((char *)dack 0, sizeof(struct xdatacf)); dack- >xl_command = N_DAck; dack->xl_type = XL_DAT; ctlblk->len = sizeof(struct xdatacf); ctlblk->buf = (char *)dack; datblk->len = 0; datblk->buf = (char *)0; putmsg(x25_fd, &ctlblk, &datblk, &getflags); } } /* end else */ break; case N_EData: printf("***Expedited data received \n"); /* Must deal with it */ break; case N_DAck: printf("***Data Acknowledgment received \n"); break; default: break; } /* end switch */ } /* end if */ } /* end for */
The example in "3.1 Making a Single Call", is of a relatively straightforward call. Procedures for making a call using OSI CONS, for receiving expedited data, for dealing with resets and for receiving remotely initiated disconnects are given in the following sections. These can be integrated into the example above, as required.
The following example opens a connection for an OSI CONS call:
#define FALSE 0 #define TRUE 1 #define CUDFLEN 4 #define EXPLEN 4 #include <memory.h> #include <netx25/x25_proto.h> struct xaddrf called = { 0, 0, {14, { 0x23, 0x42, 0x31, 0x56, 0x56, 0x56, 0x56 }}, 0}; /* Subnetwork "A" (filled in later), no flags, * DTE = "23423156565656", null NSAP */ struct xcallf conreq; struct strbuf, ctlblk, datblk; struct xedataf exp; main () { . . . called.link_id = 0; /* * snidtox25 only fails if a * NULL string is passed to it */ conreq.xl_type = XL_CTL; conreq.xl_command = N_CI; conreq.CONS_call = TRUE; /* This is a CONS call */ conreq.negotiate_qos = TRUE; /* Negotiate requested */ memset(&conreq.qos, 0, sizeof (struct qosformat)); conreq.qos.reqexpedited = TRUE; /* Expedited requested */ conreq.qos.xtras.locpacket = 8; /* 256 bytes */ conreq.qos.xtras.rempacket = 8; /* 256 bytes */ memcpy(&conreq.calledaddr, &called, sizeof(struct xaddrf)); memset(&conreq.callingaddr, 0, sizeof(struct xaddrf)); . . . }
When negotiate_qos is true (non-zero), setting the QOS fields to zero means that the connection uses defaults for QOS and Facilities. If required, these can be set to different values but it is recommended that the entire QOS structure be zeroed first as shown. This is preferable to setting each field individually, as it allows for any future additions to this structure. Setting the calling address to null leaves the network to fill this value in.
The message is sent on the stream using the putmsg system call, with any call user data being passed in the data part of the message:
char cudf[CUDFLEN] = { 1, 0, 0, 0 }; ctlblk.len = sizeof(struct xcallf); ctlblk.buf = (char *) &conreq; datblk.len = CUDFLEN; datblk.buf = cudf; if (putmsg(x25_fd, &ctlblk, &datblk, 0) < 0 ) { perror("Call putmsg"); exit(1); }
At this stage, the application should wait for a response to the Call Request. The response may be either a Connect Confirmation or a Disconnect (rejection) message.
The preceding example allows for the possibility of receiving expedited data messages, which are carried in X.25 interrupt packets. These must be dealt with appropriately. Since only one expedited data packet can be outstanding in the connection at any time, its sender is prevented from sending any further such messages until the receiver has acknowledged it. The receiver does this by sending an Expedited Acknowledgment (EAck) message. The EAck is sent in the same way as an ordinary data packet, but with no data part.
If an application does not need to use the expedited data capability, then it responds to receiving an EData by resetting or closing the connection.
When sending expedited data, the application must wait for an acknowledgment before requesting further expedited transmissions.
char expdata[]= {1, 2, 3, 4}; exp.xl_type= XL_CTL; exp.xl_command= N_Edata; ctlblk.len= sizeof (struct xedataf); ctlblk.buf= (char *) &exp; datblk.len= EXPLEN; datblk.buf= expdata; if (putmsg(x25_fd, &ctlblk, &datblk, 0) < 0) { error("Exp putmsg"); exit(1); } for (;;) { if (getmsg(x25_fd, &ctlblk, &datblk, &getflags) < 0) { perror("Getmsg fail"); exit(1); } hdrptr = (S_X25_HDR *) ctlbuf; if (hdrptr->xl_type == XL_CTL) { /* Deal with protocol message as required */ } if (hdrptr->xl_type == XL_DAT) { dat_msg = (struct xdataf *) ctlbuf; switch (dat_msg->xl_command) { case N_Data: /* process more data */ break; case N_EData: printf("***Expedited data received \n"); /* Must deal with */ .... send N_EAck .... break; case N_EAck: /* Expedited data received */ /* Further N_Edata can now be sent */ break; default: break; } }
Resets and Interrupts are dealt with in a similar way, except that there is no data passed with a Reset Request. When a Reset Request or Interrupt is issued, the application must wait for the acknowledgment, as for an expedited request. However, until this is received, the only action that can be taken is to issue a Disconnect Request.
The diagnostic field in a Reset Request or Interrupt contains the reason for issuing the reset. Standard values for this are defined in the include file <netx25/x25_proto.h>, although the application can set any value. See Chapter 9, Error Codes for more details.
When a Reset Indication is received, there are only two valid actions that may be taken:
In either case, pending data is flushed from the queue.
Reset Indications can be dealt with as part of the general processing of incoming messages, as shown in the following disconnect handling example.
#include<netx25/x25_proto.h> struct xrstf rst; S_X25_HDR *hdrptr; rst.xl_type= XL_CTL; rst.xl_command= N_RI; rst.cause= 0; rst.diag= NU_RESYNC; ctlblk.len= sizeof (struct rstf); ctlblk.buf= (char *) &rst; if (putmsg(x25_fd, &ctlblk, 0, 0) < 0) { perror(" putnmsg"); exit(1); } for (;;) { if (getmsg(x25_fd, &ctlblk, &datblk, &getflags) < 0) { perror("Getmsg fail"); exit(1); } hdrptr = (S_X25_HDR *) ctlbuf; if (hdrptr->xl_type == XL_CTL) { continue; } switch (hdrptr->xl_command) { case N_RC: /* Reset complete */ /* Enter data transfer */ break; default: break; } /* end switch */ } /* end for */
Control messages, like resets and interrupts, take higher priority than normal data messages, both internally in the PLP driver, and across the network.
If the remote end initiates a Disconnect, then a Disconnect Indication (N_DI) message (or possibly an N_Abort message, see "6.4.1 N_Abort--Abort Indication") is received at the NLI. The application need not acknowledge this message since, after sending a Disconnect, the X.25 driver silently discards all messages received except for connect and accept messages. These are the only meaningful X.25 messages on the stream after disconnection.
The receiver of a Disconnect Indication should ensure that enough room is available in the getmsg call to receive all parameters and, when present, up to 128 bytes of Clear User Data. Handling such a Disconnect event would normally be part of the general processing of incoming messages.
The following example could be combined with the code from the data transfer example in the previous section.
struct xdiscf *dis_msg; if (hdrptr->xl_type == XL_CTL) { switch (hdrptr->xl_command) { /* Other events/indications dealt with * here - e.g. Reset Indication (N_RI) */ case N_DI: dis_msg = (struct xdiscf *) hdrptr; printf("Remote disconnect, cause = %x, diagnostic = %x \n", dis_msg->cause, dis_msg->diag); /* Any other processing needed here - * e.g. change connection state */ return; case N_Abort: printf("***Connection Aborted \n"); /* etc. */ return; default: break; } }
It is guaranteed that no X.25 interface messages are sent to the application once a disconnect message has been passed up to it, wherever the message came from.
Although at this stage the stream is idle, it is in an open state and remains so until some user action. This could be to close the stream, or to initiate a new Listen or Connect request on it.