Net2/usr/src/contrib/isode/compat/x25addr.c

Compare this file to the similar file:
Show the results in this format:

/* x25addr.c - X.25 level generic <-> interface address munging */

#ifndef lint
static char *rcsid = "$Header: /f/osi/compat/RCS/x25addr.c,v 7.6 91/02/22 09:16:14 mrose Interim $";
#endif

/*
 * $Header: /f/osi/compat/RCS/x25addr.c,v 7.6 91/02/22 09:16:14 mrose Interim $
 *
 * Contributed by George Michaelson, Julian Onions, and John Pavel
 *
 *
 * $Log:	x25addr.c,v $
 * Revision 7.6  91/02/22  09:16:14  mrose
 * Interim 6.8
 * 
 * Revision 7.5  91/01/14  13:33:56  mrose
 * loader
 * 
 * Revision 7.4  91/01/07  12:40:04  mrose
 * update
 * 
 * Revision 7.3  90/07/09  14:32:30  mrose
 * sync
 * 
 * Revision 7.2  89/12/11  16:21:42  mrose
 * comments
 * 
 * Revision 7.1  89/12/11  01:36:14  mrose
 * SUN_X25_HACK
 * 
 * Revision 7.0  89/11/23  21:23:49  mrose
 * Release 6.0
 * 
 */

/*
 *                                NOTICE
 *
 *    Acquisition, use, and distribution of this module and related
 *    materials are subject to the restrictions of a license agreement.
 *    Consult the Preface in the User's Manual for the full terms of
 *    this agreement.
 *
 */


/* LINTLIBRARY */

/*
 * for *really* generic address translation
 */

#include <errno.h>
#include <stdio.h>
#include "general.h"
#include "manifest.h"

#ifdef X25
#include "tailor.h"
#include "tpkt.h"
#include <sys/file.h>
#include "x25.h"

#ifndef	DEBUG
#define	DEBUG
#endif

/*  */

/*
 * convert from the generic X25 structure to interface specific
 */
/* ARGSUSED */
CONN_DB *gen2if (generic, specific, context)
struct NSAPaddr *generic;
CONN_DB         *specific;
int             context;
{
    int     dtelen;
    char    dte[NSAP_DTELEN + 1];
#ifdef CAMTEC_CCL
    struct iovec *iov;
#endif

    if (generic == NULLNA
	    || specific == (CONN_DB *) 0
	    || generic -> na_stack != NA_X25)
	return (CONN_DB *)0;

    if (x25_dnic_prefix && *x25_dnic_prefix) {
		/* need DNIC on local calls? */
	register int    i;

	if ( strncmp(generic -> na_dte, x25_dnic_prefix,
	    i = strlen(x25_dnic_prefix)) == 0 )
	    {
	    if (x25_strip_dnic) bcopy(generic -> na_dte + i, dte, dtelen =
		generic -> na_dtelen - i);
	    else bcopy (generic -> na_dte, dte, dtelen = generic -> na_dtelen);
	    }
	else
	    if (x25_intl_zero)
		{
		bcopy(generic -> na_dte, dte + 1, dtelen = generic-> na_dtelen);
		*dte = '0', dtelen++;
		}
	    else bcopy(generic -> na_dte, dte, dtelen = generic -> na_dtelen);

    }
    else bcopy (generic -> na_dte, dte, dtelen = generic -> na_dtelen);
    dte[dtelen] = NULL;

#ifdef	SUN_X25_HACK
	/*	
	 * If your X.25 provider expects to receive the subaddress alone
	 * on listen requests, and you are using SunLink X.25, you may need
	 * to enable SUN_X25_HACK in your config file. This will allow you
	 * to use x25_local_dte in isotailor to specify a dte mask to be
	 * stripped when listening, and thus use full DTE strings in
	 * isoentities and QUIPU EDB files. You will also have to use the 
	 * tsapd -a <dte> option to specify the listen address in
	 * /etc/rc.local and other tsapd startups since by default this equals
	 * x25_local_dte and thus will be masked to <null> unless overridden
	 * with full DTE + subaddress. 
	 */

	/* 
	 * in ADDR_LISTEN context, it may be neccessary to only listen
	 * on the sub-address, because certain PTT-provided networks
	 * remove the local DTE from incoming CR packets. 
	 *
	 * SunLink X.25 listen asserts whatever DTE it is given as a simple
	 * string-compare, and will never receive inbound calls that bear
	 * only the sub-address if you assert the full DTE.
	 *
	 * this behaviour is orthogonal to any requirements to remove DNIC
	 * or add a leading 0 on outbound calls, and so needs a separate
	 * test. It uses tailor variable x25_local_dte to assert the local 
	 * DTE *without* subaddress which should be tested for and stripped 
	 * when detected. 
	 */

    if ((context == ADDR_LISTEN) && x25_local_dte && *x25_local_dte) 
	{
	register int    i;

	if ( strncmp(generic -> na_dte, x25_local_dte,
	    i = strlen(x25_local_dte)) == 0 ) 
	    {
	    bcopy(generic -> na_dte + i, dte, dtelen =
		generic -> na_dtelen - i);
    	    dte[dtelen] = NULL;
	    }
	}
#endif

    DLOG (x25_log, LLOG_DEBUG,
	   ("gen2if %s -> %s, %d octets; PID %s",
	    generic -> na_dte, dte, dtelen,
	    sel2str (generic -> na_pid, (int) generic -> na_pidlen,1)));


#ifndef CAMTEC_CCL
    bzero ((char *)specific, sizeof *specific);
#endif

#ifdef UBC_X25
    if ((specific -> xaddr_len = dtelen) != 0)  {
	bcopy (dte, specific -> xaddr_addr,
	       dtelen);
	specific -> xaddr_len = dtelen;
	specific -> xaddr_facilities = 0;
	bcopy (generic -> na_pid, specific -> xaddr_proto,
	       generic -> na_pidlen);
	bcopy (generic -> na_cudf, specific -> xaddr_userdata,
	       generic -> na_cudflen);
    }
#endif

#ifdef SUN_X25
    specific -> hostlen = char2bcd (dte, specific -> host);

    /* Zero PID */
    if (generic -> na_pidlen) { /* non-null PID */
	if (generic -> na_pidlen > NPSIZE) {
	    SLOG (compat_log, LLOG_EXCEPTIONS, NULLCP,
		  ("PID too long (%d > %d)", generic -> na_pidlen, NPSIZE));
	    return (CONN_DB *)0;
	} else {
	    bzero((char *)specific -> data, NPSIZE);
	    bcopy (generic -> na_pid, (char *)specific -> data,
		   generic -> na_pidlen);
	    bcopy (generic -> na_cudf, (char *) specific -> data + NPSIZE,
		   generic -> na_cudflen);
	    specific -> datalen = generic -> na_pidlen + generic -> na_cudflen;
	}
    } else { /* Null PID (just copy in CUDF, the first four octets of which
		will be the PID in any case) */
	 bcopy (generic -> na_cudf, (char *)specific -> data,
		generic -> na_cudflen);
	 specific -> datalen = generic -> na_cudflen;
    }
#endif

#ifdef CAMTEC_CCL
    switch (context) {
	case ADDR_REMOTE:
	    iov = &(specific -> ccl_iovec[0]);
	    if (x25_outgoing_port == '#') {
		char *a, *b;
		int i;

		iov -> iov_len = dtelen + 4;
		bzero(iov -> iov_base, iov -> iov_len + 1);
		a = iov -> iov_base;
		b = dte;
		*a++ = '#';
		*a++ = '[';
		for (i = 0; i < dtelen; i++) {
			if (i == 2) *a++ = ':';
			else if (i == 14) *a++ = ']';
			*a++ = *b++;
		}
	    }
	    else {
		iov -> iov_len = dtelen+1;
		bcopy(dte, (iov -> iov_base)+1, dtelen);
		*(iov -> iov_base) = x25_outgoing_port;
	    }
	    break;

	case ADDR_LOCAL:
	    iov = &(specific -> ccl_iovec[0]);
	    strncpy(iov -> iov_base, generic -> na_dte, generic -> na_dtelen);
	    iov -> iov_base[generic -> na_dtelen] = '\0';
	    return (specific);

	case ADDR_LISTEN:
	    iov = &(specific -> ccl_iovec[0]);
	    if (generic -> na_pidlen)
		{                       /* listen on a PID */
		register int i;
		iov -> iov_base[0] = 'C';
		bcopy(generic -> na_pid, iov -> iov_base + 1,
		    i = generic -> na_pidlen);
		iov -> iov_len = i + 1;
		}
	    else
	    if (generic -> na_dtelen < 6)
		{           /* listen on a subaddress */
		register int i;
		iov -> iov_base[0] = 'S';
		bcopy(generic -> na_dte, iov -> iov_base + 1,
		    i = generic -> na_dtelen);
		iov -> iov_len = i + 1;
		}
	    else    /* full DTE */
		bcopy(dte, iov -> iov_base,
		    iov -> iov_len = dtelen);
	    return (specific);
    }
    /*
     * CUDF & PID must be merged. malloc initailly PIDsize space
     * and bzero it. this may be UK net specific action which
     * ensures we do NOT fall foul of listeners which use pid
     * to match as well as "true" cudf & DTE.
     */

    (iov = &(specific -> ccl_iovec[2])) -> iov_len = 0;
    if (generic -> na_faclen != 0)
	bcopy (generic -> na_fac, iov -> iov_base,
	    iov -> iov_len = min( generic -> na_faclen, FACSIZE) );
    iov++;
    if ( (iov -> iov_len = generic -> na_pidlen) != 0)
	bcopy (generic -> na_pid, iov -> iov_base, generic -> na_pidlen);

    /*
     * if there is any other user data add that in now...
     * actually cudf is a variable length field so this is
     * all very suspect.
     */

    if (generic -> na_cudflen != 0)
	bcopy(generic -> na_cudf, iov -> iov_base + iov -> iov_len,
	    generic -> na_cudflen), iov -> iov_len += generic -> na_cudflen;
#endif

    return(specific);
}

/*  */

/*
 * convert from interface specific format to generic X.25 structure
 */
/* ARGSUSED */
struct NSAPaddr *if2gen (generic, specific, context)
struct NSAPaddr *generic;
CONN_DB         *specific;
int             context;
{
    int     dtelen;
    char    dte[NSAP_DTELEN + 1];
#ifdef CAMTEC_CCL
    struct iovec *iov;
#endif

    if (generic == NULLNA || specific == (CONN_DB *) 0)
	return NULLNA;
    bzero ((char *)generic, sizeof *generic);
    bzero (dte, sizeof dte);
    dtelen = 0;

    generic -> na_stack = NA_X25;
    generic -> na_community = ts_comm_x25_default;

#ifdef UBC_X25
    if (specific -> xaddr_len  != 0) {
	bcopy (specific -> xaddr_addr, dte, specific -> xaddr_len);
	dtelen = specific -> xaddr_len;
	bcopy (specific -> xaddr_proto, generic -> na_pid,
				sizeof(specific -> xaddr_proto));
	generic -> na_pidlen = sizeof specific -> xaddr_proto;
	bcopy (specific -> xaddr_userdata, generic -> na_cudf,
				sizeof(specific -> xaddr_userdata));
	generic -> na_cudflen = sizeof specific -> xaddr_userdata;
    }
#endif

#ifdef SUN_X25
    dtelen = bcd2char (specific -> host, dte, (int) specific -> hostlen);

    if (specific -> datalen > NPSIZE) { /* have some real user data after the PID */
	bcopy((char *)specific -> data, generic -> na_pid,
	      generic -> na_pidlen = NPSIZE);
	bcopy((char *) specific -> data + generic -> na_pidlen,
	      generic -> na_cudf,
	      generic -> na_cudflen = specific -> datalen - generic -> na_pidlen);
    }
    else { /* PID only */
	bcopy((char *)specific -> data, generic -> na_pid,
	      generic -> na_pidlen = specific -> datalen);
	generic -> na_cudflen = 0;
    }

#endif

#ifdef CAMTEC_CCL
    switch (context) {
    case ADDR_REMOTE:

	iov = &(specific -> ccl_iovec[1]);
	if (iov -> iov_len) {
		if (*(iov->iov_base) == '#') {
			char *a;

			a = iov -> iov_base;
			while (*a && iov -> iov_len) {
				if (*a == ']') {
					iov -> iov_len--;
					a++;
					break;
				}
				iov -> iov_len--;
				a++;
			}
			if (*a == 0 || iov -> iov_len == 0)
				dtelen = 0;
			else {
				dtelen = iov -> iov_len;
				bcopy (a, dte, dtelen);
			}
		}
		else {
			dtelen = iov -> iov_len - 1;
			bcopy ((iov -> iov_base)+1, dte,
				dtelen);
		}
	}
	else dtelen = 0;
	break;

    case ADDR_LOCAL:
	iov = &(specific -> ccl_iovec[0]);
	if (iov -> iov_len) {
		dtelen = iov -> iov_len -1;
		bcopy ((iov -> iov_base)+1, dte,
			dtelen);
	}
	else dtelen = 0;
	break;

    case ADDR_LISTEN:
	return NULLNA;
    }

    if ( (iov = &(specific -> ccl_iovec[2])) -> iov_len )
	bcopy( iov -> iov_base, generic -> na_fac,
	    generic -> na_faclen = min( iov -> iov_len, FACSIZE));

    if ( ++iov -> iov_len)
	{
	bcopy( iov -> iov_base, generic -> na_pid,
	    generic -> na_pidlen = min( iov -> iov_len, NPSIZE));
	if ( iov -> iov_len > NPSIZE)
	    bcopy( iov -> iov_base + NPSIZE, generic -> na_cudf,
		generic -> na_cudflen = min(iov -> iov_len - NPSIZE, CUDFSIZE));
	}
#endif

    if (x25_dnic_prefix && *x25_dnic_prefix) {
	register int    i;

	i = 0;
	if (x25_intl_zero && dte[0] == '0' && dte[1] != '0')
	    i = 1;
	else
	    if (x25_dnic_prefix
		    && *x25_dnic_prefix
		    && x25_strip_dnic
		    && dtelen < 12)   /* local call... */
		bcopy (x25_dnic_prefix, generic -> na_dte,
		       generic -> na_dtelen = strlen (x25_dnic_prefix));

	bcopy (dte + i, generic -> na_dte + generic -> na_dtelen, dtelen - i);
	generic -> na_dtelen += dtelen - i;
    }
    else
	bcopy (dte, generic -> na_dte, generic -> na_dtelen = dtelen);

    DLOG (x25_log, LLOG_DEBUG,
	   ("if2gen %s -> %s, %d octets; PID %s",
	    dte, generic -> na_dte, generic -> na_dtelen,
	    sel2str (generic -> na_pid, (int) generic -> na_pidlen,1)));

    return(generic);
}

/*  */

elucidate_x25_err (flags, pkt)
int flags;
char * pkt;
{
    char * cp;

    if (flags & (1 << RECV_DIAG)) {
	SLOG (compat_log, LLOG_EXCEPTIONS, NULLCP,
	      (( flags & (1 << DIAG_TYPE) ) ? "cleared 0x%02x" : "reset 0x%02x",
	       pkt[0] ));

	if ((flags) & (1 << DIAG_TYPE)) /* cleared */
	    switch(pkt[0]) {
		case 0x00:
		    cp = "DTE Clearing";
		    break;

		case 0x01:
		    cp = "Number Busy";
		    break;

		case 0x09:
		    cp = "Out of Order";
		    break;

		case 0x11:
		    cp = "Remote Procedure Error";
		    break;

		case 0x19:
		    cp = "Reverse Charging not subscribed";
		    break;

		case 0x03:
		    cp = "Invalid Facility Request";
		    break;

		case 0x0B:
		    cp = "Access Barred";
		    break;

		case 0x13:
		    cp = "Local Procedure Error";
		    break;

		case 0x05:
		    cp = "Network Congestion";
		    break;

		case 0x0D:
		    cp = "Not Obtainable";
		    break;

		case 0x21:
		    cp = "DTE Incompatible Call";
		    break;

		case 0x29:
		    cp = "Fast Select Acceptance not Subscribed";
		    break;

		default:
		    SLOG (compat_log, LLOG_EXCEPTIONS, NULLCP,
			  ("clearing cause 0x2%x", pkt[0]));
		    goto next;
	    }
	else /* reset */
	    switch(pkt[0]) {
		case 0x00:
		    cp = "DTE Reset";
		    break;

		case 0x01:
		    cp = "Out of Order (PVC Only)";
		    break;

		case 0x03:
		    cp = "Remote Procedure Error";
		    break;

		case 0x05:
		    cp = "Local Procedure Error";
		    break;

		case 0x07:
		    cp = "Network Congestion";
		    break;

		case 0x09:
		    cp = "Remote DTE Operational (PVC Only)";
		    break;

		case 0x0F:
		    cp = "Network Operational (PVC Only";
		    break;

		default:
		    SLOG (compat_log, LLOG_EXCEPTIONS, NULLCP,
			  ("resetting cause 0x%2x", pkt[0]));
		    goto next;

	    }
	SLOG (compat_log, LLOG_EXCEPTIONS, NULLCP, ("%s%s",
	    ( flags & (1 << DIAG_TYPE) ) ? "clearing cause " :
	    "resetting cause ", cp));

next: ;
	/* The following may only be applicable to PSS in the UK */
	/* In any case, if someone is keen, they can stuff it all
	   into a text file and read it out */

	switch (pkt[1]) {
	    case 0x00:
		cp = "NO ADDITIONAL INFORMATION";
		break;

	    case 0x01:
		cp = "INVALID P(S)\tRESET";
		break;

	    case 0x02:
		cp = "INVALID P(R)\tRESET";
		break;

	    case 0x11:
		cp = "PACKET TYPE INVALID FOR STATE r1\tRESTART";
		break;

	    case 0x12:
		cp = "PACKET TYPE INVALID FOR STATE r2\tRESTART";
		break;

	    case 0x13:
		cp = "PACKET TYPE INVALID FOR STATE r3\tRESTART";
		break;

	    case 0x14:
		cp = "PACKET TYPE INVALID FOR STATE p1\tCLEAR";
		break;

	    case 0x15:
		cp = "PACKET TYPE INVALID FOR STATE p2\tCLEAR";
		break;

	    case 0x16:
		cp = "PACKET TYPE INVALID FOR STATE p3\tCLEAR";
		break;

	    case 0x17:
		cp = "PACKET TYPE INVALID FOR STATE p4\tCLEAR";
		break;

	    case 0x18:
		cp = "PACKET TYPE INVALID FOR STATE p5\tRESET";
		break;

	    case 0x19:
		cp = "PACKET TYPE INVALID FOR STATE p6\tCLEAR";
		break;

	    case 0x1A:
		cp = "PACKET TYPE INVALID FOR STATE p7\tCLEAR";
		break;

	    case 0x1B:
		cp = "PACKET TYPE INVALID FOR STATE d1\tRESET";
		break;

	    case 0x1C:
		cp = "PACKET TYPE INVALID FOR STATE d2\tRESET";
		break;

	    case 0x1D:
		cp = "PACKET TYPE INVALID FOR STATE d3\tRESET";
		break;

	    case 0x20:
		cp = "PACKET NOT ALLOWED";
		break;

	    case 0x21:
		cp = "UNIDENTIFIABLE PACKET";
		break;

	    case 0x22:
		cp = "CALL ON ONE-WAY LOGICAL CHANNEL\tCLEAR";
		break;

	    case 0x23:
		cp = "INVALID PACKET TYPE ON PVC\tRESET";
		break;

	    case 0x24:
		cp = "PACKET ON UNASSIGNED LCN\tCLEAR";
		break;

	    case 0x25:
		cp = "REJECT NOT SUBSCRIBED TO\tRESET";
		break;

	    case 0x26:
		cp = "PACKET TOO SHORT\tRESET";
		break;

	    case 0x27:
		cp = "PACKET TOO LONG\tRESET";
		break;

	    case 0x28:
		cp = "INVALID GFI\tCLEAR";
		break;

	    case 0x29:
		cp = "RESTART WITH NON-ZERO BITS 5-16";
		break;

	    case 0x2A:
		cp = "PACKET TYPE NOT COMPATIBLE WITH FACILITY\tCLEAR";
		break;

	    case 0x2B:
		cp = "UNAUTHORISED INTERRUPT CONF\tRESET";
		break;

	    case 0x2C:
		cp = "UNAUTHORISED INTERRUPT\tRESET";
		break;

	    case 0x31:
		cp = "TIMER EXPIRED;  INCOMING CALL";
		break;

	    case 0x32:
		cp = "TIMER EXPIRED;\tCLEAR INDICATION";
		break;

	    case 0x33:
		cp = "TIMER EXPIRED;\tRESET INDICATION";
		break;

	    case 0x34:
		cp = "TIMER EXPIRED;\tRESTART IND";
		break;

	    case 0x40:
		cp = "UNSPECIFIED CALL SET-UP PROBLEM CLEAR";
		break;

	    case 0x41:
		cp = "FACILITY CODE NOT ALLOWED\tCLEAR";
		break;

	    case 0x42:
		cp = "FACILITY PARAMETER NOT ALLOWED\tCLEAR";
		break;

	    case 0x43:
		cp = "INVALID CALLED ADDRESS\tCLEAR";
		break;

	    case 0x44:
		cp = "INVALID CALLING ADDRESS\tCLEAR";
		break;

	    case 0x90:
		cp = "DTE/DCE CONGESTION\tRESET";
		break;

	    case 0x91:
		cp = "RECEIVED FAST SELECT CLEAR REQUEST";
		break;

	    case 0x92:
		cp = "LINE RESTARTING BY INMC COMMAND\tRESTART";
		break;

	    case 0xA0:
		cp = "NON-ZERO RESET CAUSE FROM DTE\tRESET";
		break;

	    case 0xA1:
		cp = "DATA PACKET TOO LONG\tRESET";
		break;

	    case 0xA2:
		cp = "INTERRUPT PACKET TOO LONG\tRESET";
		break;

	    case 0xA3:
		cp = "INT PACKET TOO SHORT; NO USER DATA\tRESET";
		break;

	    case 0xA4:
		cp = "INT CONFIRMATION PACKET TOO LONG\tRESET";
		break;

	    case 0xA5:
		cp = "RR PACKET TOO LONG\tRESET";
		break;

	    case 0xA6:
		cp = "RNR PACKET TOO LONG\tRESET";
		break;

	    case 0xA7:
		cp = "RESET PACKET TOO LONG\tRESET";
		break;

	    case 0xA8:
		cp = "RESET CONF PACKET TOO LONG\tRESET";
		break;

	    case 0xA9:
		cp = "INVALID `Q' BIT IN DATA PACKET\tRESET";
		break;

	    case 0xAA:
		cp = "PACKET WINDOW RANGE EXCEEDED\tRESET";
		break;

	    case 0xAB:
		cp = "UNABLE TO TRANSMIT PACKET\tRESET";
		break;

	    case 0xAC:
		cp = "diagnostic `Q' BIT SET IN NON-DATA PACKET\tRESET";
		break;

	    case 0xAD:
		cp = "OUTSTANDING PACKET COUNT LESS THAN ZERO\tRESET";
		break;

	    case 0xAE:
		cp = "RETRANSMISSION ERROR\tRESET";
		break;

	    case 0xAF:
		cp = "RESET PACKET TOO SHORT (NO CAUSE)\tRESET";
		break;

	    case 0xB0:
		cp = "REJECT PACKET TOO LONG\tRESET";
		break;

	    case 0xB1:
		cp = "INVALID 1D PACKET\tRESET";
		break;

	    case 0xB2:
		cp = "UNSUCCESSFUL RECONNECTION RESNC\tCLEAR";
		break;

	    case 0xB3:
		cp = "NON-RECONNECT CALL IN STATE C1\tCLEAR";
		break;

	    case 0xB4:
		cp = "SECOND 1D PACKET FROM DTE\tCLEAR";
		break;

	    case 0xB5:
		cp = "BAD DATA TRANSFER STATE IN RECONNECT\tCLEAR";
		break;

	    case 0xB6:
		cp = "PACKET FORMAT INVALID\tCLEAR";
		break;

	    case 0xB7:
		cp = "FACILITY BYTE COUNT TOO LARGE\tCLEAR";
		break;

	    case 0xB8:
		cp = "INVALID PACKET DETECTED\tCLEAR";
		break;

	    case 0xB9:
		cp = "FACILITY/UTILITY FIELD BYTE COUNT > 63\tCLEAR";
		break;

	    case 0xBA:
		cp = "OUTGOING CALLS BARRED\tCLEAR";
		break;

	    case 0xBB:
		cp = "INCOMING CALLS BARRED\tCLEAR";
		break;

	    case 0xBC:
		cp = "CLEARING OF PVC\tCLEAR";
		break;

	    case 0xBD:
		cp = "CALLED ADDRESS TOO LONG\tCLEAR";
		break;

	    case 0xBE:
		cp = "CALLED ADDRESS TOO SHORT\tCLEAR";
		break;

	    case 0xBF:
		cp = "CALLING ADDRESS TOO LONG\tCLEAR";
		break;

	    case 0xC0:
		cp = "CALLING ADDRESS TOO SHORT\tCLEAR";
		break;

	    case 0xC1:
		cp = "BCD ERROR IN CALL ADDRESS\tCLEAR";
		break;

	    case 0xC2:
		cp = "BCD ERROR IN CALLING ADDRESS\tCLEAR";
		break;

	    case 0xC3:
		cp = "USER DATA FIELD TOO LONG\tCLEAR";
		break;

	    case 0xC4:
		cp = "NO BUFFER AVAILABLE\tCLEAR";
		break;

	    case 0xC5:
		cp = "LOCAL DTE IS NOT ENHANCED\tCLEAR";
		break;

	    case 0xC6:
		cp = "FACILITY NEGOTIATION INVALID\tCLEAR";
		break;

	    case 0xC7:
		cp = "MANDATORY UTILITY NOT INPUT\tCLEAR";
		break;

	    case 0xC8:
		cp = "BUFFER NO AVAILABLE FOR TNIC\tCLEAR";
		break;

	    case 0xC9:
		cp = "OVERFLOW OF TNIC IN BUFFER\tCLEAR";
		break;

	    case 0xCA:
		cp = "DTE LINE CONGESTED\tCLEAR";
		break;

	    case 0xCB:
		cp = "TABLE ERROR IN PACKET PROCEDURES";
		break;

	    case 0xCC:
		cp = "INSERT TABLE OVERFLOW";
		break;

	    case 0xCD:
		cp = "DELETE TABLE OVERFLOW";
		break;

	    case 0xD0:
		cp = "TRUNK LINE RESTART\tRESTART";
		break;

	    case 0xD1:
		cp = "INVALID EVENT IN STATE p2";
		break;

	    case 0xD2:
		cp = "INVALID EVENT IN STATE p3";
		break;

	    case 0xD3:
		cp = "INVALID 1D EVENT IN STATE d1";
		break;

	    case 0xD4:
		cp = "CALL COLLISION ON TRUNK LINE";
		break;

	    case 0xD5:
		cp = "NO BUFFER AVAILABLE";
		break;

	    case 0xD6:

		cp = "CALL COLLISION ON DTE LINE";
		break;

	    case 0xD7:
		cp = "DTE RESTART";
		break;

	    case 0xD8:
		cp = "CALL REQUEST TO TRUNK LINE TIMEOUT";
		break;

	    case 0xD9:
		cp = "RECONNECT SET-UP TIMED OUT";
		break;

	    case 0xDA:
		cp = "INVALID OUTPUT SIDE STATE";
		break;

	    case 0xDB:
		cp = "ERROR DETECTED IN BLINK PACKET QUEUE PROCEDURE";
		break;

	    case 0xDC:
		cp = "RESET INDICATION RETRANSMISSION COUNT EXPIRED";
		break;

	    case 0xDD:
		cp = "INVALID OUTPUT SIDE STATE";
		break;

	    case 0xDE:
		cp = "BLIND BUFFER QUEUE OVERFLOW IN STATE d4";
		break;

	    case 0xDF:
		cp = "BLIND BUFFER QUEUE OVERFLOW IN STATE c1";
		break;

	    case 0xE0:
		cp = "BLIND BUFFER QUEUE OVERFLOW IN STATE c2";
		break;

	    case 0xE1:
		cp = "CLEAR PACKET BYTE COUNT TOO LARGE OR TOO SMALL";
		break;

	    case 0xE2:
		cp = "NON-ZERO\tCLEAR CAUSE";
		break;

	    case 0xE3:
		cp = "CLEAR CONF PACKET BYTE COUNT TOO SMALL OR TOO LARGE";
		break;

	    case 0xE4:
		cp = "CALL COLLISION";
		break;

	    case 0xE5:
		cp = "INVALID TP LOAD REQUEST CALL PKT";
		break;

	    case 0xE6:
		cp = "MAXIMUM HOPCOUNT EXCEEDED";
		break;

	    case 0xE7:
		cp = "ROUTING LOOP DETECTED";
		break;

	    case 0xE8:
		cp = "PVC CALL REQUEST FAILURE";
		break;

	    case 0xE9:
		cp = "RECONNECT CALL REQUEST FAILED";
		break;

	    case 0xEA:
		cp = "NO LC AVAILABLE ON OUTPUT SIDE";
		break;

	    case 0xEB:
		cp = "NO BUFFER AVAILABLE";
		break;

	    case 0xEC:
		cp = "CALL REDIRECTION CLEAR";
		break;

	    case 0xED:
		cp = "NO PATH ROUTE CALL";
		break;

	    case 0xEE:
		cp = "CALL ROUTED TO DTE LINE";
		break;

	    case 0xEF:
		cp = "CALL CANNOT BE REROUTED";
		break;

	    case 0xF0:
		cp = "ADDRESS NOT IN ROUTING TABLES";
		break;

	    case 0xF1:
		cp = "ROUTING TABLE CHANGE DURING CALL ROUTING";
		break;

	    case 0xF2:
		cp = "NO LC AVAILABLE ON FAKE TRUNK";
		break;

	    case 0xF3:
		cp = "REMOTE DTE DOWN ON A PVC";
		break;

	    case 0xF4:
		cp = "INVALID EVENT DETECTED";
		break;

	    case 0xF5:
		cp = "INVALID PACKET RECEIVED; STATE d4";
		break;

	    case 0xF6:
		cp = "INVALID PACKET RECEIVED; STATE d5";
		break;

	    case 0xF7:
		cp = "INVALID PACKET RECEIVED; STATE p8";
		break;

	    case 0xF8:
		cp = "INTERNAL PROCESSING FAILURE";
		break;

	    case 0xF9:
		cp = "INVALID RESTART INDICATION";
		break;

	    case 0xFA:
		cp = "LINE STATUS CHANGE IN STATE r4";
		break;

	    case 0xFB:
		cp = "INVALID PACKET RECEIVED; STATE r4";
		break;

	    case 0xFC:
		cp = "INVALID PACKET RECEIVED; STATE r3";
		break;

	    case 0xFD:
		cp = "LINE STATUS CHANGE IN STATE r2";
		break;

	    case 0xFE:
		cp = "LINE STATUS CHANGE IN STATE r1";
		break;

	    case 0xFF:
		cp = "LINE STATUS CHANGE IN STATE r0";
		break;

	    default:
		SLOG (compat_log, LLOG_EXCEPTIONS, NULLCP,
		      ("diagnostic: 0x%2x", pkt[1]));
		goto done;
	}
	SLOG (compat_log, LLOG_EXCEPTIONS, NULLCP, ("diagnostic %s", cp));
    }
    else        /* Not RECV_DIAG */
	if (flags)
	    SLOG (compat_log, LLOG_EXCEPTIONS, NULLCP,
		  ("diag flags: 0x%02x", flags));

done: ;
    return OK;
}

/*  */

#ifdef  SUN_X25
static int  char2bcd (s, d)
register char   *s;
register u_char *d;
{
    register int    c,
		    i;

    for (i = 0; *s; i++) {
	if ((c = *s++) >= 'a' && c <= 'f')
	    c -= 'a' + 0x0a;
	else
	    if (c >= 'A' && c <= 'F')
		c -= 'A' + 0x0a;
	    else
		if (c >= '0' && c <= '9')
		    c -= '0';
		else
		    c = 0;

	if (i & 1)
	    *d++ |= c & 0xf;
	else
	    *d = (c & 0xf) << 4;
    }

    return i;
}

/*  */

static int     bcd2char (s, d, len)
register    u_char *s;
register char  *d;
int     len;
{
    register int    i,
		    g;

    for (i = 0; i < len; i++) {
	g = s[i >> 1];
	if ((i & 1) == 0)
	    g >>= 4;
	g &= 0xf;

	if (g < 0x0a)
	    *d++ = g + '0';
	else
	    *d++ = g + 'a' - 0x0a;
    }

    *d = NULL;

    return len;
}
#endif
#else
int	_x25addr_stub () {};
#endif