/* acpconfig.c V2.2 30 June 1987 */ /*************************************************************************/ /* */ /* */ /* ________________________________________________________ */ /* / \ */ /* | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* \________________________________________________________/ */ /* */ /* Copyright (c) 1986 by Advanced Computer Communications */ /* 720 Santa Barbara Street, Santa Barbara, California 93101 */ /* (805) 963-9431 */ /* */ /* */ /* File: acpconfig.c */ /* */ /* Author: Clare E. Russ */ /* */ /* Project: Installation verification program for ACC */ /* ACP 5100/6100 and ACP 5250/6250 network */ /* interface drivers. Acpconfig provides a user */ /* interface which supports the configuration of a */ /* network interface. */ /* */ /* Function: */ /* Based on socket ioctls, provide user interface to Network */ /* Interface Drivers for the following ACC ACP devices: */ /* */ /* ACP 5100/6100 'acp' interface */ /* ACP 5250/6250 'dda' interface */ /* ACP 625 'ddn' interface */ /* */ /* The ioctl indicates what type of message is to be sent to the */ /* front end device (ie, bring up the HDLC line in external */ /* loopback mode). */ /* */ /* Components: acpconfig.c, ioctl.h */ /* */ /* Revision History: */ /* */ /* 16-SEP-1985 Clare Russ: first generated, cut and paste from */ /* ifconfig.c */ /* 20-OCT-1985 Clare Russ: modify for compatibility with TWG */ /* software */ /* 28-APR-1986 Clare Russ: BETA RELEASE V1.2 (version number tracks */ /* ACC Software Library entry) */ /* next version: condense code in main(), */ /* create acpconfig.h for includes and defs */ /* 14-MAY-1986 Clare Russ: BETA RELEASE V1.3 (version number tracks */ /* ACC Software Library entry) */ /* fix error in baud rate logic */ /* 30-MAY-1986 Randy Graves BETA RELEASE V1.4 (version number tracks */ /* ACC Software Library entry) make */ /* changes to support Public Data Networks */ /* 11-JUL-1986 Clare Russ: BETA RELEASE V0.5 (version number tracks */ /* ACC Software Library entry) make */ /* changes to support Public Data Networks */ /* in_addr differences reconciled */ /* 06-AUG-1986 Clare Russ: V2.0 (version number tracks ACC Software */ /* Library entry) modify for compatibility */ /* with ULTRIX-32(m) V1.2 */ /* 10-SEP-1986 Clare Russ: V2.1 (version number tracks ACC Software */ /* Library entry) add ability to reset the */ /* front end. */ /* 18-FEB-1987 Jeff Berkowitz V2.2 Add ability to pass baud rate */ /* divisor to FEP. New ioctl to query */ /* service mode. Add interface to */ /* statistics query message. Add general */ /* configuration interface from user space. */ /* Fix white space syntax on -b to allow */ /* -b 9600. Use acpconfig for debug/log */ /* message control. Add ioctl to configure */ /* the number of LCNs. */ /* */ /* Usage Notes: */ /* */ /* acpconfig interface [address] [-A filename] [-a ipaddr x25addr] */ /* [-b baud] [-d ipaddr] [-m message] */ /* [-n #_lcns] [-q type] [-r count] [-s X.25_service] */ /* [-u mode] [-v variable value] [-z] */ /* */ /* -A filename */ /* Add the contents of the named file to the address */ /* translation table for PDN service */ /* */ /* -a ipaddr x25addr */ /* Add the specified address pair to the address */ /* translation table for PDN service */ /* */ /* -b baud */ /* Note that the baud rate values are different for the */ /* ACP 625. Table 1 contains the baud rates which apply */ /* to the ACP 6100, ACP 5250/6250 products. Table 2 */ /* contains the baud rates which apply to the ACP 625. */ /* */ /* Table 1: Nominal baud rates for ACP 5100/6100, ACP 5250/6250 */ /* */ /* 2.00M 2000K 2.0M 1.33M 1333K 1.3M */ /* 1.00M 1000K 1.0M 500K 250K 100K */ /* 64K 64000 56K 56000 30K 19.2K */ /* 9.6K 9600 4.8K 4800 2.4K 2400 */ /* 1.2K 1200 */ /* */ /* Table 2: Nominal baud rates for ACP 625 */ /* */ /* 316000 153600 115200 76800 76.8K */ /* 57600 57.6K 38400 38.4K 28800 */ /* 28.8K 19200 19.2K 9600 9.6K */ /* 4800 4.8K 2400 2.4K 2150 */ /* 1760 1200 */ /* */ /* The M for megabits/second and K for kilobits/second */ /* are optional. Note that Table 1 allows for the entry */ /* of 9.6 (with the assumed unit of Kilobits/second), but */ /* also allows for the entry of 9600 (bits/second). Thus */ /* there is more than one entry for some of the baud rate */ /* values. */ /* */ /* -d ipaddr */ /* Delete the specified address entry from the address */ /* translation table for PDN service */ /* */ /* -m message */ /* Pass ``message'' to the FEP as a supervisory channel */ /* message. Message is a sequence of numbers separated */ /* by white space. Numbers with leading ``0'' are taken */ /* as octal, other numbers taken as HEX, decimal is not */ /* supported. Hex and octal may be intermixed, as in */ /* ``0140 0 0 2 89 017''. The message is terminated by the */ /* end of the argument list or by an argument beginning */ /* with a dash ``-''. Absolutely no checking is performed; */ /* the bytes are written to the FEP as a supervisor */ /* message. The message is limited to MLEN = 112 bytes. */ /* */ /* -n #_lcns */ /* Set the number of logical channels to ``#_lcns'', which */ /* must be less than some implementation defined maximum. */ /* */ /* -q query-type */ /* Query the board or driver and place results on stdout. */ /* */ /* -q 0 Statistics query to FEP */ /* -q 1 Driver operating mode query */ /* */ /* -r count */ /* Read the specified number of entries from the address */ /* translation table for PDN service */ /* */ /* -r 0 read all entries from the address translation table */ /* */ /* -s X.25 service */ /* Select DDN standard X.25 service or basic X.25 service */ /* or Public Data Network X.25 service */ /* */ /* -s 0 select standard X.25 service */ /* -s 1 select basic X.25 service */ /* -s 2 select X.25 Public Data Network service */ /* */ /* -u mode */ /* Bring the link up or down. */ /* */ /* -u 0 bring down the link */ /* -u 1 bring up link for normal operation, no loopback, DTE */ /* -u 2 bring up link for normal operation, no loopback, DCE */ /* -u 3 bring up the link in external loopback mode */ /* -u 4 bring up the link in internal loopback mode */ /* */ /* -v variable value */ /* Set the value of a driver variable symbolized by */ /* "variable" to "value" decimal. "Variables" understood */ /* are "log", "debug", "window" and "packet" to set the */ /* driver's notion of logging, debug flags, negotiable */ /* window size, and negotiable packet size. */ /* */ /* -z reset the specifed front-end device */ /* */ /* Compile Notes: */ /* */ /* The associated makefile builds the acpconfig executable image */ /* for UNIX 4.2BSD or the specified emulation/simulation such as */ /* The Wollongong Group (TWG) software which runs under VAX/VMS. */ /* The makefile must be invoked with the argument which specifies */ /* the target host operating system, otherwise the makefile */ /* defaults to creating an executable image for UNIX 4.2BSD. */ /* */ /* make (compile for UNIX 4.2BSD) */ /* make sysbsd (compile for UNIX 4.2BSD) */ /* make systwg (compile for TWG under VAX/VMS) */ /* */ /* make clean */ /* make print */ /* */ /* */ /* System Notes: */ /* */ /* Create a socket of the AF_INET address family and of type */ /* datagram, SOCK_DGRAM. Use the socket to send a socket ioctl */ /* to the network driver of the specified interface ('acp0' in */ /* the usage notes). Depending on the type of message specified */ /* in the command line, send the appropriate socket ioctl to */ /* specify what kind of action is to be taken. */ /* */ /* For those drivers which support the Control Interface (CIF) */ /* the message exchange conforms to the CIF which defines paired */ /* command/response Control Interface Messages (CIMs) between the */ /* host and the front end. Otherwise, the exchange of messages */ /* is the pre-CIF protocol (i.e., that used by the ddn interface). */ /* */ /* Assignment of subcodes for the SIOCACPCONFIG ioctl: */ /* */ /* 1..14 -bRATE for RATE != 0 */ /* `0'..`4' -u N (line control) */ /* `a' -A and -a commands (PDN) */ /* `b' -b0 (set external clock) */ /* `d' -d command (PDN) */ /* `m' -m (message command) */ /* `n' -n #_lcns (limit lcns) */ /* `p' -q 0 (statistics query) */ /* `q' -q 1 (driver query) */ /* `r' -r command (PDN) */ /* `s' -s 0 */ /* `t' -s 1 */ /* `u' -s 2 */ /* `v' -v command (set variable) */ /* `z' -z command (reset) */ /* */ /*************************************************************************/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% INCLUDE FILES %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* There are alternate path names for The Wollongong Group (TWG) */ /* IPTCP and Eunice software which runs under VMS */ #include <sys/types.h> #include <sys/socket.h> #ifdef TWG #include </sys/twgtcp/kernel/h/ioctl.h> #include </sys/twgtcp/kernel/netinet/in.h> #include </sys/twgtcp/kernel/net/if.h> #else #include <sys/ioctl.h> #include <netinet/in.h> #include <net/if.h> #endif TWG #include <stdio.h> #include <errno.h> #include <ctype.h> #include <netdb.h> /* 'iface' interface definitions */ #define ACP_INTERFACE 0x01 /* for acp interface */ #define DDN_INTERFACE 0x02 /* for ddn interface */ #define BAUD_VAL 50 /* for baud rate flag, -b */ #define SERVICE_VAL 67 /* for X.25 service flag, -s */ /* Change these for PDN X.25 Addresses of other formats. E.g., for no */ /* restriction change PDNX25AMIN to 1 and PDNBADDNIC to "Z" */ #define PDNX25AMIN 12 /* Minimum X.25 address length */ #define PDNBADDNIC "0000" /* X.25 addr prefix to EXCLUDE */ /* delay macro from /sys/vax/param.h */ #define DELAY(n) { register int N = (n); while (--N > 0); } /* The baud_rate structure maps the user-supplied baud rate into the */ /* parameter used in the socket ioctl to the driver. In some cases, */ /* more than one representation of a value is present to ease the */ /* interface. The manual page for acpconfig recommends that the */ /* user give the baud rate with the assumed unit of Kilobits/second. */ struct baud { char *b_rate; char parameter; } baud_rate[] = { { "2.00", 1 }, /* these are nominal baud rates */ { "2.00M", 1 }, { "2000K", 1 }, { "2000k", 1 }, { "2.0", 1 }, { "2.0M", 1 }, { "1.33", 2 }, { "1.33M", 2 }, { "1333K", 2 }, { "1333k", 2 }, { "1.3", 2 }, { "1.3M", 2 }, { "1.00", 3 }, { "1.00M", 3 }, { "1000K", 3 }, { "1000k", 3 }, { "1.0M", 3 }, { "1.0", 3 }, { "500", 4 }, { "500K", 4 }, { "500k", 4 }, { "250", 5 }, { "250K", 5 }, { "250k", 5 }, { "100", 6 }, { "100K", 6 }, { "100k", 6 }, { "64", 7 }, { "64K", 7 }, { "64k", 7 }, { "64000", 7 }, { "56", 8 }, { "56K", 8 }, { "56k", 8 }, { "56000", 8 }, { "30", 9 }, { "30K", 9 }, { "30k", 9 }, { "19.2", 10 }, { "19.2K", 10 }, { "19.2k", 10 }, { "9.6", 11 }, { "9.6K", 11 }, { "9.6k", 11 }, { "9600", 11 }, { "4.8", 12 }, { "4.8K", 12 }, { "4.8k", 12 }, { "4800", 12 }, { "2.4", 13 }, { "2.4K", 13 }, { "2.4k", 13 }, { "2400", 13 }, { "1.2", 14 }, { "1.2K", 14 }, { "1.2k", 14 }, { "1200", 14 }, { 0, 0 }, }; /* Table of baud rate values and the associated parameter for the Set */ /* System Parameters message, ddninit_msg. The 'parameter1' is nonzero */ /* for valid baud rate divisors. The user's manual gives both the */ /* actual and nominal baud rates, either one is accepted from the user, */ /* but the nominal baud rate is the figure which is closest to the rate */ /* set on the front end. */ struct baud ddnbaud_rate[] = { { "333333", 1 }, /* actual baud rate */ { "316000", 1 }, /* nominal baud rate */ { "153846", 2 }, /* actual baud rate */ { "153600", 2 }, /* nominal baud rate */ { "114285", 3 }, /* actual baud rate */ { "115200", 3 }, /* nominal baud rate */ { "76923", 4 }, /* actual baud rate */ { "76800", 4 }, /* nominal baud rate */ { "76.8K", 4 }, /* nominal baud rate */ { "76.8k", 4 }, /* nominal baud rate */ { "57971", 5 }, /* actual baud rate */ { "57600", 5 }, /* nominal baud rate */ { "57.6K", 5 }, /* nominal baud rate */ { "57.6k", 5 }, /* nominal baud rate */ { "38461", 6 }, /* actual baud rate */ { "38400", 6 }, /* nominal baud rate */ { "38.4K", 6 }, /* nominal baud rate */ { "38.4k", 6 }, /* nominal baud rate */ { "28776", 7 }, /* actual baud rate */ { "28800", 7 }, /* nominal baud rate */ { "28.8K", 7 }, /* nominal baud rate */ { "28.8k", 7 }, /* nominal baud rate */ { "19230", 8 }, /* actual baud rate */ { "19200", 8 }, /* nominal baud rate */ { "19.2K", 8 }, /* nominal baud rate */ { "19.2k", 8 }, /* nominal baud rate */ { "9592", 9 }, /* actual baud rate */ { "9600", 9 }, /* nominal baud rate */ { "9.6K", 9 }, /* nominal baud rate */ { "9.6k", 9 }, /* nominal baud rate */ { "4801", 10 }, /* actual baud rate */ { "4800", 10 }, /* nominal baud rate */ { "4.8K", 10 }, /* nominal baud rate */ { "4.8k", 10 }, /* nominal baud rate */ { "2400", 11 }, /* actual baud rate */ { "2.4K", 11 }, /* actual baud rate */ { "2.4k", 11 }, /* actual baud rate */ { "2150", 12 }, /* nominal baud rate */ { "1760", 13 }, /* actual baud rate */ { "1760", 13 }, /* actual baud rate */ { "1200", 14 }, /* nominal baud rate */ { "1.2K", 14 }, /* nominal baud rate */ { "1.2k", 14 }, /* nominal baud rate */ { 0, 0 }, }; /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% MAIN() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* For the specified interface, create a socket for a socket */ /* ioctl to set the Internet address and configuration. The */ /* ioctl kicks the appropriate driver, the value in ifr_data */ /* indicates the type of action to be taken. ('b' indicates */ /* external clock, 1-14 indicates baud rate from the table of */ /* possible values, '0' - '4' indicates -u options, and 's','t', */ /* 'u' indicate -s options.) */ /* Enhancement: 'a' indicates addition of an address table */ /* entry, 'd' indicates deletion of an address table entry, */ /* and 'r' is a request to read address table entries. */ /* Etc, etc (see comment at top for complete list) */ /* */ /* Call: main( argc, argv) */ /* Arguments: argc: argument count */ /* argv: argument value */ /* Returns: nothing */ /* Called by: invoked by privileged user */ /* Calls to: socket() */ /* perror() */ /* strcpy() */ /* strncpy() */ /* sizeof() */ /* usage() */ /* ioctl() */ /* exit() */ /* Perror() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ struct ifreq ifr; struct sockaddr_in sin = { AF_INET }; char name[30]; int s; /* socket descriptor */ struct ddactl { /* used by various ioctls */ u_char func; /* must be first */ u_char nothing[3]; /* alignment */ int drval; /* used by -v, etc */ char msg[112]; /* used by -m, -q: XXX 112 should be "MLEN" */ } ddactl; struct trtab { /* Address Translation Table Entry */ u_char func; u_char x25addr[15]; u_long ipaddr; } trtab; struct lg2tab { /* For use with packet sizes, so just the */ int base2log; /* legal packet sizes for X.25 are here */ int binval; } lg2tab[] = { 4, 16, 5, 32, 6, 64, 7, 128, 8, 256, 9, 512, 10, 1024, 11, 2048, 12, 4096, 0, 0, }; extern int errno; main(argc, argv) int argc; char *argv[]; { register struct baud *p; /* baud rates */ int iface = 0; /* indicate interface, i.e. acp */ int tmp, top; FILE *fp; char line[80], arg1[40], arg2[40]; if (argc < 3) { fprintf(stderr,"\n acpconfig: invalid number of arguments\n"); usage(); /* display proper syntax for user */ } s = socket(AF_INET, SOCK_DGRAM, 0); /* try create socket */ if (s < 0) { perror("acpconfig: socket"); exit(1); } argc--, argv++; /* interface name */ strcpy(name, *argv); strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if ((*argv)[2] == 'n') /* check for 'ddn' interface */ iface |= DDN_INTERFACE; if ((*argv)[0] == 'a') /* check for 'acp' interface */ iface |= ACP_INTERFACE; if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCGIFFLAGS)"); exit(1); } while (--argc > 0) { argv++; if(**argv != '-') setifaddr(*argv, 0); /* set interface address */ else { ifr.ifr_data = 0; switch((*argv)[1]) { /* process flag(s) */ case 'b': /* set baud rate or ext clocking */ { /* accept -bN or -b N for any N */ char *ap = *argv; if (ap[2] == '\0') { /* -b N case */ argc--, argv++, ap = *argv; if (ap == 0) ap = "???"; } else { ap += 2; /* -bN case, skip over the "-b" */ } /* ap points to purported number, regardless -bN or -b N */ if (*ap == '0') { /* set external clocking */ if (iface&DDN_INTERFACE) { fprintf (stderr, "\nacpconfig %s: -b 0 invalid\n", name); break; } *ap = 'b'; /* cookie for driver ioctl routine */ ifr.ifr_data = ap; } else { /* set baud rate, if N is valid */ if (iface&DDN_INTERFACE) p = ddnbaud_rate; else p = baud_rate; while (p->b_rate) { if (strcmp (ap, p->b_rate) == 0) break; p++; } if (p->parameter) ifr.ifr_data = &(p->parameter); } if (setconfig(ifr.ifr_data, s)) { fprintf(stderr,"\nacpconfig: -b %s invalid\n", ap); usage(); } break; } case 's': /* select DDN standard X.25 service */ /* or basic X.25 service, accept -sn */ /* or -s n */ if (iface & ACP_INTERFACE) { /* error if 'acp' device */ fprintf(stderr,"\n acpconfig: '-s' flag invalid for"); fprintf(stderr," specified interface\n"); break; } ifr.ifr_data = 0; /* clear value */ if (((*argv)[2] >='0' && (*argv)[2]<= '2') && (*argv)[3] == 0) { (*argv)[2] += SERVICE_VAL; /* match 's','t','u' */ ifr.ifr_data = &((*argv)[2]); } else { argc--, argv++; if(((*argv)[0]>= '0'&&(*argv)[0]<='2')&&(*argv)[1]==0) { (*argv)[0] += SERVICE_VAL; ifr.ifr_data = &((*argv)[0]); } } if ((ifr.ifr_data == 0) || setconfig(ifr.ifr_data, s)) { if (errno == EALREADY) { fprintf(stderr,"acpconfig: must shut down interface to\ change modes between DDN and PDN\n"); exit(1); } else { fprintf(stderr,"\n acpconfig: invalid X.25 service\n"); usage(); /* display proper syntax for user */ } } break; case 'u': /* bring up the line, specify loopback */ /* or DTE/DCE, accept -un or -u n */ if(((*argv)[2] >= '0' && (*argv)[2] <= '4') && (*argv)[3] == 0) ifr.ifr_data = &((*argv)[2]); else { argc--, argv++; if(((*argv)[0] >= '0'&& (*argv)[0]<='4')&&(*argv)[1]==0) ifr.ifr_data = &((*argv)[0]); } if (argc > 1) { /* any more parameters? */ fprintf(stderr, " acpconfig: -u flag must be last\n"); exit(1); } if (ifr.ifr_data == 0) { fprintf(stderr,"\n acpconfig: invalid mode"); fprintf(stderr," '%s'\n", *argv); usage(); /* display proper syntax for user */ } else { if ( setconfig(ifr.ifr_data, s) ) { if (errno == EADDRNOTAVAIL) { fprintf(stderr, "acpconfig: no local X.25 address \ translation in table; cannot start up PDN mode\n"); exit(1); } else { usage(); /* display proper syntax for user */ } } } break; case 'm': /* Send supervisory message to FEP */ { char *mp = ddactl.msg; int i; static char *xfmt = "%x"; static char *ofmt = "%o"; bzero (ddactl.msg, sizeof (ddactl.msg)); while (--argc > 0) { argv++; if (*argv[0] == '-') { /* hit a non-number */ argc++; /* fix these guys for */ argv--; /* the main loop */ break; } if (mp > &ddactl.msg[sizeof (ddactl.msg)]) { fprintf ("\nacpconfig: -m message too long\n"); exit(1); } #ifdef notdef printf ("argument %s argc %d\n", *argv, argc); #endif sscanf (*argv, (**argv == '0') ? ofmt : xfmt, &i); *mp++ = i; } /* while */ ddactl.func = 'm'; strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_data = (caddr_t) &ddactl; #ifdef notdef printf ("before ioctl msg %x %x %x %x %x %x %x %x\n", ddactl.msg[0], ddactl.msg[1], ddactl.msg[2], ddactl.msg[3], ddactl.msg[4], ddactl.msg[5], ddactl.msg[6], ddactl.msg[7] ); sleep(1); #endif if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } } break; case 'n': /* set SVC limit */ { char *ap = *argv; if (ap[2] == '\0') { argc--; /* value is next arg; fix pointers */ argv++; ap = *argv; } else ap += 2; /* value at 3rd char of this arg */ ddactl.func = 'n'; ddactl.drval = atoi(ap); ifr.ifr_data = (caddr_t) &ddactl; if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } } break; case 'q': /* query board or driver for status */ { char *cp = *argv; if (strlen (cp) > 2) cp += 2; else { argc--; argv++; cp = *argv; } doquery (atoi (cp)); } break; case 'v': /* set variable */ { char *cp; /* keyword determining what to set */ char *vp; /* value to set it to. */ int i, val; /* * The ORDERING of this table must match the * cases in the processing for this ioctl * inside the driver (kludgy interface!) */ static char *nametab[] = { "log", "debug", "packet", "window", 0 }; cp = *argv; if (strlen (cp) > 2) /* handle -vNAME VAL and -v NAME VAL */ cp += 2; else { argc--; argv++; cp = *argv; if (cp == 0) { fprintf (stderr, "acpconfig: -v what?\n"); exit(1); } } argc--; argv++; vp = *argv; if (vp == 0) { fprintf (stderr, "acpconfig: -v %s what?\n", cp); exit(1); } for (i = 0; nametab[i] != 0; i++) if (0 == strcmp (nametab[i], cp)) goto strmatch; fprintf (stderr, "acpconfig: -v: \"%s\" unknown\n", cp); exit(1); strmatch: /* hit the keyword, and "i" is its index */ if (i < 2) /* log, debug: masks: read in hexadecimal */ sscanf (vp, "%x", &val); else /* window, packet: counts: read in decimal */ sscanf (vp, "%d", &val); if (i == 2) { /* packet size: take base 2 log */ struct lg2tab *lp; /* * The sad thing is that after putting in all this * code to take the base 2 log, I realized that the * FEP takes this argument in BYTES and does the * log internally. Left the code in, though, since * it's the easiest way to check the argument. * (The driver converts to bytes with a shift). */ for (lp = lg2tab; lp->base2log; ++lp) if (lp->binval == val) { val = lp->base2log; goto goodsize; } fprintf (stderr, "acpconfig: -v: bad packet size\n"); exit(1); } goodsize: ddactl.drval = val; ddactl.msg[0] = i; ddactl.func = 'v'; strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_data = (caddr_t) &ddactl; if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } } break; case 'z': /* reset the specified front-end device*/ ifr.ifr_data = &((*argv)[1]); setconfig(ifr.ifr_data, s); fprintf(stderr,"\nacpconfig: reset in progress, wait 1 minute\n"); break; case 'A': /* add a file of address translation table entries */ /* accept "-A filename OR "-Afilename" */ if (iface & ACP_INTERFACE) { /* error if 'acp' device */ fprintf(stderr,"acpconfig: '-A' flag invalid for specified \ interface\n"); exit(1); } ifr.ifr_data = 0; /* clear value */ if (strlen(*argv) > 2) fp = fopen(&((*argv)[2]),"r"); else { argc--, argv++; fp = fopen(*argv,"r"); } if (fp==NULL) { fprintf(stderr,"acpconfig: cannot open file '%s'\n", ((*argv)[0] == '-') ? &((*argv)[2]) : *argv); exit(1); } fprintf(stdout, "acpconfig: processing file '%s'; please wait...\n", ((*argv)[0] == '-') ? &((*argv)[2]) : *argv); while (fgets(line, 124, fp) != NULL) { if (line[0] == '#' || line[0] == '\n') continue; sscanf (line, "%s %s", arg1, arg2); /* fprintf(stdout, "acpconfig: processing translation %s ==> %s\n", arg1, arg2); */ if ((top=strlen(arg2)) > 14 || top < PDNX25AMIN) { fprintf(stderr, "acpconfig: bad X.25 address length: '%s'\n", arg2); exit(1); } for (tmp=0; tmp<top; tmp++) if (arg2[tmp] > '9' || arg2[tmp] < '0') { fprintf(stderr, "acpconfig: invalid X.25 address '%s'\n", arg2); exit(1); } if (strncmp(arg2, PDNBADDNIC, 4) == 0) { fprintf(stderr, "acpconfig: incomplete X.25 address '%s'\n", arg2); exit(1); } getaddr(arg1,&sin); trtab.func = 'a'; trtab.ipaddr = sin.sin_addr.s_addr; trtab.x25addr[0]=strlen(arg2); strncpy(&trtab.x25addr[1], arg2, 14); ifr.ifr_data = (caddr_t)&trtab; strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } } break; case 'a': /* add an address translation table entry */ /* accept "-a inet_addr x25_addr" */ /* OR "-ainet_addr x25_addr" */ if (iface & ACP_INTERFACE) { /* error if 'acp' device */ fprintf(stderr,"acpconfig: '-a' flag invalid for specified \ interface\n"); exit(1); } ifr.ifr_data = 0; /* clear value */ if (strlen(*argv) > 2) getaddr(&((*argv)[2]),&sin); else { argc--, argv++; getaddr(*argv,&sin); } trtab.func = 'a'; trtab.ipaddr = sin.sin_addr.s_addr; argc--, argv++; if ((top=strlen(*argv)) > 14 || top < PDNX25AMIN) { fprintf(stderr, "acpconfig: bad X.25 address length: '%s'\n", *argv); exit(1); } for (tmp=0; tmp<top; tmp++) if ((*argv)[tmp] > '9' || (*argv)[tmp] < '0') { fprintf(stderr, "acpconfig: invalid X.25 address '%s'\n", *argv); exit(1); } if (strncmp(*argv, PDNBADDNIC, 4) == 0) { fprintf(stderr, "acpconfig: incomplete X.25 address '%s'\n", *argv); exit(1); } trtab.x25addr[0]=strlen(*argv); strncpy(&trtab.x25addr[1], *argv, 14); ifr.ifr_data = (caddr_t)&trtab; strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } break; case 'd': /* delete an address translation table entry */ /* accept "-d inet_addr" OR "-dinet_addr" */ if (iface & ACP_INTERFACE) { /* error if 'acp' device */ fprintf(stderr,"acpconfig: '-d' flag invalid for specified \ interface\n"); exit(1); } ifr.ifr_data = 0; /* clear value */ if (strlen(*argv) > 2) getaddr(&((*argv)[2]),&sin); else { argc--, argv++; getaddr(*argv,&sin); } trtab.func = 'd'; trtab.ipaddr = sin.sin_addr.s_addr; ifr.ifr_data = (caddr_t)&trtab; strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } break; case 'r': /* read an address translation table entry */ /* accept "-r length" OR "-rlength" */ if (iface & ACP_INTERFACE) { /* error if 'acp' device */ fprintf(stderr,"acpconfig: '-r' flag invalid for specified \ interface\n"); exit(1); } ifr.ifr_data = 0; /* clear value */ if (strlen(*argv) > 2) top = atoi(&((*argv)[2])); else { argc--, argv++; top = atoi(*argv); } if (top==0) top = 30000; /* (a big number) */ for (tmp=0; tmp<top; tmp++) { trtab.ipaddr = (u_long) tmp; trtab.func = 'r'; ifr.ifr_data = (caddr_t)&trtab; strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { if (errno == EFAULT) { fprintf(stdout,"acpconfig: end of address table \ (%d entries max)\n", tmp); break; } else { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } } /* * The byte ordering "works" on the inet_ntoa() call. * Probably only on VAXen, though (fine for us). */ if (trtab.x25addr[0]) { printf("acpconfig: address table entry %d: ", tmp); printf ("%s ==> %01.14s\n", inet_ntoa (trtab.ipaddr), &trtab.x25addr[1]); } } break; default: usage(); /* display proper syntax for user */ } } } exit(0); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% SETIFADDR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Set the interface address. */ /* */ /* Call: setifaddr(addr, param) */ /* Arguments: addr: internet address */ /* param: argument value */ /* Returns: nothing */ /* Called by: main() */ /* Calls to: getaddr() */ /* strncpy() */ /* ioctl() */ /* Perror() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ setifaddr(addr, param) char *addr; int param; { getaddr(addr, (struct sockaddr_in *)&ifr.ifr_addr); strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCSIFADDR, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCSIFADDR)"); exit(1); } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% GETADDR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* THIS ROUTINE IS SLOW AS MOLASSES - - FIX IT SOONER OR LATER !!! */ /* Purpose: */ /* */ /* Get the interface address and stick it in the appropriate */ /* data structure. This routine has four different methods for */ /* deriving the address. If successful return nothing, else */ /* print error message and exit. */ /* */ /* */ /* Call: getaddr(s, sin) */ /* Arguments: s: string for address */ /* sin: socket address struct */ /* Returns: nothing for success, else error message exit */ /* Called by: setifaddr() */ /* Calls to: gethostbyname() */ /* bcopy() */ /* getnetbyname() */ /* inet_makeaddr() */ /* inet_addr() */ /* inet_network() */ /* fprintf() */ /* exit() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ struct in_addr inet_makeaddr(); getaddr(s, sin) char *s; struct sockaddr_in *sin; { struct hostent *hp; struct netent *np; int val; hp = gethostbyname(s); if (hp) { sin->sin_family = hp->h_addrtype; bcopy(hp->h_addr, (char *)&sin->sin_addr, hp->h_length); return; } np = getnetbyname(s); if (np) { sin->sin_family = np->n_addrtype; sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); return; } sin->sin_family = AF_INET; val = inet_addr(s); if (val != -1) { sin->sin_addr.s_addr = val; return; } val = inet_network(s); if (val != -1) { sin->sin_addr = inet_makeaddr(val, INADDR_ANY); return; } fprintf(stderr, "acpconfig: invalid internet address '%s'\n", s); exit(1); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% SETCONFIG() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Set the configuration parameters by sending socket ioctl */ /* to the specified interface driver. */ /* */ /* */ /* Call: setconfig(action, s) */ /* Arguments: action indicates action to be taken */ /* s socket descriptor */ /* Returns: 0 for success, nonzero for failure */ /* NOTE: if no internet address, doesn't return, */ /* program exits */ /* Called by: main() */ /* Calls to: fprintf() */ /* strncpy() */ /* sizeof() */ /* ioctl() */ /* Perror() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ setconfig(action, s) char *action; int s; { extern int errno; int saves; if (action == 0) return(1); saves = s; strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { if (errno == EINTR) { fprintf(stderr,"\nacpconfig: command in progress\n"); DELAY(3000000); /* wait for device pwrup diags */ if (ioctl(saves, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); return(1); } else return(0); } else { Perror("ioctl (SIOCACPCONFIG) returns"); return(1); } } return(0); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% USAGE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Display the correct syntax for the acpconfig command line. */ /* */ /* Call: usage() */ /* Returns: nothing */ /* Called by: main() */ /* Calls to: fprintf() */ /* exit() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ static char *usemsg = "Usage: acpconfig interface [address] [-A filename] [-a ipaddr x25addr]\n\ \t[-b baud] [-d ipaddr] [-m message]\n\ \t[-n #_lcns] [-q type] [-r count] [-s X.25_service]\n\ \t[-u mode] [-v variable value] [-z]\n\ \n\ This version supports firmware revisions 1.0 and 2.0\n"; usage() { fprintf(stderr, usemsg); exit(1); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PERROR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* print error message, based on errno */ /* */ /* Call: Perror(cmd) */ /* Arguments: cmd: error message */ /* Returns: nothing, exits if no internet address is set */ /* Called by: main() */ /* Calls to: fprintf() */ /* perror() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ Perror(cmd) char *cmd; { extern int errno; fprintf(stderr, "acpconfig: "); switch (errno) { case ENXIO: fprintf(stderr, "%s: ", cmd); fprintf(stderr, "no such interface\n"); break; case EPERM: fprintf(stderr, "permission denied\n"); break; case EDESTADDRREQ: fprintf(stderr, "%s: ", cmd); fprintf(stderr, "no internet address assigned to interface\n"); usage(); /* display proper syntax for user */ exit(1); break; case EINVAL: fprintf(stderr, "%s: ", cmd); fprintf(stderr, "invalid command\n"); break; case EINTR: fprintf(stderr, "%s: ", cmd); fprintf(stderr, "device not operational\n"); break; case EINPROGRESS: fprintf(stderr, "%s: ", cmd); fprintf (stderr, "Can't change parameters with link up\n"); fprintf (stderr, "Bring the link down and try again\n"); break; case ENOPROTOOPT: fprintf (stderr, "%s: ", cmd); fprintf (stderr, "Changing packet size, window size, or\n"); fprintf (stderr, "SVC limit requires firmware\n"); fprintf (stderr, "revision 2.0 or greater\n"); break; default: perror(cmd); } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DOQUERY() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Query either FEP or driver for status, depending on argument */ /* */ /* Call: doquery(n) */ /* Arguments: n: 0 for statistics query, 1 for driver mode */ /* Returns: nothing, exits if error return from ioctl call */ /* Prints status display on stdout */ /* Called by: main() */ /* Calls to: fprintf() */ /* perror() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ doquery (mode) int mode; { ddactl.func = (mode == 0) ? 'p' : 'q'; ifr.ifr_data = (caddr_t) &ddactl; strncpy (ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } if (mode == 0) fmtstats(ddactl.msg); else fmtmode(ddactl.msg); } /* * from PROJECT:[X25.ACP5250W.HCOMMON]NCP.H on * VAX/Very Multiloquious System, node SAGE. */ struct stat_rsp { u_char cmnd, path, var, count; /* message header */ u_long uptime; /* seconds since startup */ u_long elapsed; /* tenths since last statistics response */ u_long frames_sent; /* frames and... */ u_long bytes_sent; /* bytes transmitted */ u_long frames_rcvd; /* frames and... */ u_long bytes_rcvd; /* bytes received */ u_short SABMs_sent; /* similarly for SABMs, */ u_short SABMs_rcvd; u_short DISCs_sent; /* DISCs */ u_short DISCs_rcvd; u_short FRMRs_sent; /* FRMRs */ u_short FRMRs_rcvd; u_short RRs_sent; /* RRs */ u_short RRs_rcvd; u_short REJs_sent; /* REJs */ u_short REJs_rcvd; u_short RNRs_sent; /* RNRs */ u_short RNRs_rcvd; u_short crc_errors; /* CRC errors received */ u_short bad_frames; /* invalid frames received */ u_short rexmitted; /* I-frames RE-transmitted */ u_short UAs_sent; /* UAs transmitted, received */ u_short UAs_rcvd; u_short DMs_sent; /* DMs transmitted, received */ u_short DMs_rcvd; u_short I_frames_sent; /* I frames transmitted, received */ u_short I_frames_rcvd; }; /* * A long time ago in a UNIBUS far, far away, there was a Z80 based * front end processor happily talking to its PDP-11 host. They * exchanged many cheerful messages, some of which were statistics * queries. The statistics query was defined in the only logical * format, the format shared by both the Z80 and its PDP-11 host. * Well, thousands of computer generations passed, and the little * Z80 grew up into a big, strong 68000 based front end. His * friendly little PDP-11 host grew up too, into a big honkin' * VAX. They still liked to exchange cheerful messages, though, * and some of those were still the same well-worn statistics * queries, which are still in the only "logical" data format. * So don't ask again why a VAX and a 68000 exchange data in Z80 * byte ordering! */ #include <sys/time.h> #define z80toVAX(l) (((l) << 16) | (((l) >> 16) & 0xFFFF)) #define pr(A) printf("%-10s%10d%10d\n", "A", sp->A/**/_sent, sp->A/**/_rcvd) /* * internal routines for use by doquery() */ fmtstats (buf) char *buf; { register struct stat_rsp *sp = (struct stat_rsp *) buf; struct timeval t; char *cp; sp->elapsed = z80toVAX (sp->elapsed); sp->uptime = z80toVAX (sp->uptime); sp->frames_sent = z80toVAX (sp->frames_sent); sp->frames_rcvd = z80toVAX (sp->frames_rcvd); sp->bytes_sent = z80toVAX (sp->bytes_sent); sp->bytes_rcvd = z80toVAX (sp->bytes_rcvd); gettimeofday (&t, 0); cp = ctime (&t); cp[24] = '\0'; /* get rid of the embedded newline */ printf ("\n----------------------------------------------\n"); printf ("Front End Processor statistics (host time: %s)\n", cp); t.tv_sec -= sp->uptime; cp = ctime (&t); cp[24] = '\0'; printf ("FEP up %d seconds (since %s)\n", sp->uptime, cp); t.tv_sec += sp->uptime; t.tv_sec -= sp->elapsed; cp = ctime (&t); cp[24] = '\0'; printf ("%d seconds since last statistics (at %s)\n", sp->elapsed, cp); /* ---- debugging stuff printf ("cmnd = %x path = %x var = %x count = %x\n", sp->cmnd, sp->path, sp->var, sp->count); ---- */ printf ("\n%20s%10s\n", "SENT", "RCVD"); pr (frames); pr (bytes); pr (I_frames); pr (SABMs); pr (DISCs); pr (FRMRs); pr (RRs); pr (REJs); pr (RNRs); pr (UAs); pr (DMs); printf ("\n%d CRC errors; %d bad frames; %d frames retransmitted\n", sp->crc_errors, sp->bad_frames, sp->rexmitted); printf ("--------------- END OF STATISTICS -----------------\n\n"); } /* * This is disgustingly ugly. The whole interface between acpconfig * and the driver has gotten completely out of hand. Has to do with * the fact that we want this program to be compatible with both * ULTRIX and BSD and they have different include file structures AND * historically many of the structures weren't even IN include files! * * buf: * dda_state, dda_init, dda_flags, dda_firmrev */ fmtmode (buf) char *buf; { static char *brd_modes[] = { "DISABLED", "COMING UP", "UP", "GOING DOWN" }; printf ("\n>>> Driver status for %s (Interface is %s) <<<\n", name, brd_modes[*buf & 3]); buf++; printf ("\tMode 0x%x ", *buf); if (*buf & 0x1) printf ("<DDN_STANDARD>"); if (*buf & 0x2) printf ("<DDN_BASIC>"); if (*buf & 0x4) printf ("<PDN>"); printf ("\n"); buf++; if (*buf == 0) printf ("\tWarning, DDAF_OK is OFF <--------------\n"); else printf ("\tInterface on-line (DDAF_OK is ON)\n"); buf++; printf ("\tFirmware revision %d.%d installed\n", (*buf >> 4) & 0xF, *buf & 0xF); buf++; printf ("*** End of driver status information ***\n\n"); }