From 78fa98ac15b35361b259752c624c74949adbd222 Mon Sep 17 00:00:00 2001 From: Lars Brinkhoff Date: Thu, 2 Feb 2017 20:39:08 +0100 Subject: [PATCH] Add ch11 files and apply patch from http://victor.se/bjorn/its/ch11.tar.gz --- src/Makefile.mk | 13 +- src/dpchudp.c | 910 ++++++++++++++++++++++++++++++++ src/dpchudp.h | 108 ++++ src/dvch11.c | 1328 ++++++++++++++++++++++++++++++++++++++++++++++- src/dvch11.h | 5 + src/dvch11.txt | 111 ++++ src/kn10dev.c | 4 +- 7 files changed, 2463 insertions(+), 16 deletions(-) create mode 100644 src/dpchudp.c create mode 100644 src/dpchudp.h create mode 100644 src/dvch11.txt diff --git a/src/Makefile.mk b/src/Makefile.mk index a64443e..c566364 100644 --- a/src/Makefile.mk +++ b/src/Makefile.mk @@ -199,7 +199,7 @@ OFILES_KS = klh10.o prmstr.o fecmd.o feload.o wfio.o osdsup.o \ DPROCS_KL = dprpxx dptm03 dpni20 DPROCS_KS = dprpxx dptm03 -DPROCS_KSITS = dprpxx dptm03 dpimp +DPROCS_KSITS = dprpxx dptm03 dpimp dpchudp # Base utility programs, independent of KN10 @@ -290,6 +290,8 @@ install: mv ${KLH10_HOME}/dpni20 ${KLH10_HOME}/flushed; fi @if [ -x ${KLH10_HOME}/dpimp ]; then \ mv ${KLH10_HOME}/dpimp ${KLH10_HOME}/flushed; fi + @if [ -x ${KLH10_HOME}/dpchudp ]; then \ + mv ${KLH10_HOME}/dpchudp ${KLH10_HOME}/flushed; fi @if [ -x kn10-ks ]; then cp -p kn10-ks ${KLH10_HOME}/; fi @if [ -x kn10-ks-its ]; then cp -p kn10-ks-its ${KLH10_HOME}/; fi @if [ -x kn10-kl ]; then cp -p kn10-kl ${KLH10_HOME}/; fi @@ -297,6 +299,7 @@ install: @if [ -x dptm03 ]; then cp -p dptm03 ${KLH10_HOME}/; fi @if [ -x dpni20 ]; then cp -p dpni20 ${KLH10_HOME}/; fi @if [ -x dpimp ]; then cp -p dpimp ${KLH10_HOME}/; fi + @if [ -x dpchudp ]; then cp -p dpchudp ${KLH10_HOME}/; fi @if [ -x enaddr ]; then cp -p enaddr ${KLH10_HOME}/; fi @if [ -x tapedd ]; then cp -p tapedd ${KLH10_HOME}/; fi @if [ -x udlconv ]; then cp -p udlconv ${KLH10_HOME}/; fi @@ -546,6 +549,14 @@ kl0i-rtmopt: $(CONFFLAGS_AUX) \ $(CONFFLAGS_USR) " +# --------- CHUDP subprocess (ITS KS only; counterpart for dvch11) +# +dpchudp.o: $(SRC)/dpchudp.c $(SRC)/dpchudp.h $(SRC)/dpsup.h + $(BUILDMOD) $(SRC)/dpchudp.c + +dpchudp: dpchudp.o dpsup.o + $(LINKER) $(LDFLAGS) $(LDOUTF) dpchudp dpchudp.o dpsup.o $(LIBS) + #################################################################### ## Device Process (DP) programs diff --git a/src/dpchudp.c b/src/dpchudp.c new file mode 100644 index 0000000..fd9fbd4 --- /dev/null +++ b/src/dpchudp.c @@ -0,0 +1,910 @@ +/* DPCHUDP.C - Chaos over UDP process +*/ +/* Copyright © 2005 Björn Victor and Kenneth L. Harrenstien +** All Rights Reserved +** +** This file may become part of the KLH10 Distribution. Use, modification, and +** re-distribution is permitted subject to the terms in the file +** named "LICENSE", which contains the full text of the legal notices +** and should always accompany this Distribution. +** +** This software is provided "AS IS" with NO WARRANTY OF ANY KIND. +** +** This notice (including the copyright and warranty disclaimer) +** must be included in all copies or derivations of this software. +*/ +/* This is based on DPIMP.C, to some extent. + Some things are irrelevant inheritage from dpimp, and could be cleaned up... */ +/* + This is a program intended to be run as a child of the KLH10 +PDP-10 emulator, in order to provide a Chaos-over-UDP tunnel. +Given a Chaos packet and a mapping between Chaosnet and IP addresses, +it simply sends the packet encapsulated in a UDP datagram. + +The protocol is very similar to the original Chaosnet link protocol, +with the addition of a four-byte protocol header: + + | V | F | A1 | A2 | +where + V is the CHUDP protocol version (currently 1) + F is the function code (initially only CHUDP_PKT) + A1, A2 are arguments for the function + +For F = CHUDP_PKT, A1 and A2 are zero (ignored), followed by the Chaos packet, +and a trailer + | D1 | D2 | S1 | S2 | + | C1 | C2 | +where D1,D2 are the destination Chaos address (subnet, address) + S1,S2 source + C1,C2 are the two bytes of Chaosnet checksum + (for the Chaos packet + trailer) + *note* that this is (currently) the standard Internet + checksum, not the original Chaosnet checksum + + "Messages" to and from the IMP are sent over the standard DP +shared memory mechanism; these are composed of 8-bit bytes in exactly +the same order as if a CH11 was sending or receiving them. Packets to +and from the NET are CHUDP packets. + + There are actually two processes active, one which pumps data +from the NET to the CH11-output buffer, and another that pumps in the +opposite direction from an input buffer to the NET. Generally they +are completely independent. +*/ + +#include +#include +#include +#include +#include + +#include "klh10.h" /* Get config params */ + +/* This must precede any other OSD includes to ensure that DECOSF gets + the right flavor sockaddr (sigh) +*/ +#define OSN_USE_IPONLY 1 /* Only need IP stuff */ +#include "osdnet.h" /* OSD net defs, shared with DPNI20 and DPIMP */ + +#include /* For setpriority() */ +#include /* For mlockall() */ + +#include "dpchudp.h" /* DPCHUDP specific defs, grabs DPSUP if needed */ + + +/* Globals */ + +struct dp_s dp; /* Device-Process struct for DP ops */ + +int cpupid; /* PID of superior CPU process */ +int chpid; /* PID of child (R proc) */ +int swstatus = TRUE; +int sock; /* UDP socket */ + + +int myaddr; /* My chaos address */ +struct in_addr ihost_ip; /* My host IP addr, net order */ + +/* FROM HERE: replace + "dpchudp" for "dpimp", + "chudp" for "imp", +*/ + +/* Debug flag reference. Use DBGFLG within functions that have "dpchudp"; + * all others must use DP_DBGFLG. Both refer to the same location. + * Caching a local copy of the debug flag is a no-no because it lives + * in a shared memory location that may change at any time. + */ +#define DBGFLG (dpchudp->dpchudp_dpc.dpc_debug) +#define DP_DBGFLG (((struct dpchudp_s *)dp.dp_adr)->dpchudp_dpc.dpc_debug) + +/* Local predeclarations */ + +void chudptohost(struct dpchudp_s *); +void hosttochudp(struct dpchudp_s *); + +void net_init(struct dpchudp_s *); +void dumppkt(unsigned char *, int); +void ihl_hhsend(register struct dpchudp_s *dpchudp, int cnt, register unsigned char *pp); +void ip_write(struct in_addr *ipa, in_port_t ipport, unsigned char *buf, int len, struct dpchudp_s *dpchudp); + + +/* Error and diagnostic output */ + +static const char progname_i[] = "dpchudp"; +static const char progname_r[] = "dpchudp-R"; +static const char progname_w[] = "dpchudp-W"; +static const char *progname = progname_i; + +static void efatal(int num, char *fmt, ...) +{ + fprintf(stderr, "\r\n[%s: Fatal error: ", progname); + { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } + fputs("]\r\n", stderr); + + /* DP automatically kills any child as well. */ + dp_exit(&dp, num); +} + +static void esfatal(int num, char *fmt, ...) +{ + fprintf(stderr, "\r\n[%s: ", progname); + { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } + fprintf(stderr, " - %s]\r\n", dp_strerror(errno)); + + /* DP automatically kills any child as well. */ + dp_exit(&dp, num); +} + +static void dbprint(char *fmt, ...) +{ + fprintf(stderr, "[%s: ", progname); + { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } + fputs("]", stderr); +} + +static void dbprintln(char *fmt, ...) +{ + fprintf(stderr, "[%s: ", progname); + { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } + fputs("]\r\n", stderr); +} + +static void error(char *fmt, ...) +{ + fprintf(stderr, "\r\n[%s: ", progname); + { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } + fputs("]\r\n", stderr); +} + +static void syserr(int num, char *fmt, ...) +{ + fprintf(stderr, "\r\n[%s: ", progname); + { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } + fprintf(stderr, " - %s]\r\n", dp_strerror(num)); +} + +int initdebug = 0; + +int +main(int argc, char **argv) +{ + register struct dpchudp_s *dpchudp; /* Ptr to shared memory area */ + + /* Search for a "-debug" command-line argument so that we can start + debug output ASAP if necessary. + */ + if (argc > 1) { + int i; + for (i = 1; i < argc; ++i) { + if (strcmp(argv[i], "-debug") == 0) { + initdebug = TRUE; + break; + } + } + } + if (initdebug) + dbprint("Starting"); + + /* Right off the bat attempt to get the highest scheduling priority + ** we can, since a slow response will cause the 10 monitor to declare + ** the interface dead. + */ +#if CENV_SYS_SOLARIS || CENV_SYS_DECOSF || CENV_SYS_XBSD || CENV_SYS_LINUX + if (setpriority(PRIO_PROCESS, 0, -20) < 0) + syserr(errno, "Warning - cannot set high priority"); +#elif CENV_SYS_UNIX /* Try old generic Unix call */ + if (nice(-20) == -1) + syserr(errno, "Warning - cannot set high priority"); +#else + error("Warning - cannot set high priority"); +#endif + + /* Next priority is to quickly close the vulnerability window; + disable TTY cruft to ensure that any TTY hacking done by superior + process doesn't inadvertently kill us off. + */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + + if (initdebug) + dbprint("Started"); + + /* General initialization */ + if (!dp_main(&dp, argc, argv)) { + efatal(1, "DP init failed!"); + } + dpchudp = (struct dpchudp_s *)dp.dp_adr; /* Make for easier refs */ + + /* Verify that the structure version is compatible */ + if (dpchudp->dpchudp_ver != DPCHUDP_VERSION) { + efatal(1, "Wrong version of DPCHUDP: ch11=%0lx dpchudp=%0lx", + (long)dpchudp->dpchudp_ver, (long)DPCHUDP_VERSION); + } + + /* Now can access DP args! + From here on we can use DBGFLG, which is actually a shared + memory reference that dpchudp points to. Check here to accomodate the + case where it's not already set but "-debug" was given as a command + arg; leave it alone if already set since the exact bits have + significance. + */ + if (initdebug && !DBGFLG) + DBGFLG = 1; + if (DBGFLG) + dbprint("DP inited"); + + /* Always attempt to lock memory since the DP processes are fairly + ** small, must respond quickly, and SU mode is more or less guaranteed. + ** Skip it only if dp_main() already did it for us. + */ +#if CENV_SYS_DECOSF || CENV_SYS_SOLARIS || CENV_SYS_LINUX + if (!(dpchudp->dpchudp_dpc.dpc_flags & DPCF_MEMLOCK)) { + if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0) { + dbprintln("Warning - cannot lock memory"); + } + } +#endif + + /* Now set up legacy variables based on parameters passed through + shared DP area. + */ + if ((myaddr = dpchudp->dpchudp_myaddr) == 0) /* My chaos address */ + efatal(1, "no CHAOS address specified"); + + /* Initialize various network info */ + net_init(dpchudp); + + /* Make this a status (rather than debug) printout? */ + if (swstatus) { + int i; + char ipbuf[OSN_IPSTRSIZ]; + + dbprintln("ifc \"%s\" => chaos %lo", + dpchudp->dpchudp_ifnam, (long)myaddr); + for (i = 0; i < dpchudp->dpchudp_chip_tlen; i++) + dbprintln(" chaos %6o => ip %s:%d", + dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_chaddr, + ip_adrsprint(ipbuf, + (unsigned char *)&dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_ipaddr), + dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_ipport); + } + + /* Now start up a child process to handle input */ + if (DBGFLG) + dbprint("Forking R process"); + if ((chpid = fork()) < 0) + esfatal(1, "fork failed"); + if (chpid == 0) { + /* Child process. + ** Child inherits signal handlers, which is what we want here. + */ + /* Fix up xfer mechanism so ACK of DP input goes to correct proc */ + dp.dp_adr->dpc_frdp.dpx_donpid = getpid(); + + /* And ensure its memory is locked too, since the lockage isn't + ** inherited over a fork(). Don't bother warning if it fails. + */ +#if CENV_SYS_DECOSF || CENV_SYS_SOLARIS || CENV_SYS_LINUX + (void) mlockall(MCL_CURRENT|MCL_FUTURE); +#endif + progname = progname_r; /* Reset progname to indicate identity */ + chudptohost(dpchudp); /* Child process handles input */ + } + progname = progname_w; /* Reset progname to indicate identity */ + + hosttochudp(dpchudp); /* Parent process handles output */ + + return 1; /* Never returns, but placate compiler */ +} + +/* NET_INIT - Initialize net-related variables, +** given network interface we'll use. +*/ +void +net_init(register struct dpchudp_s *dpchudp) +{ + struct ifreq ifr; + + /* Ensure network device name, if specified, isn't too long */ + if (dpchudp->dpchudp_ifnam[0] && (strlen(dpchudp->dpchudp_ifnam) + >= sizeof(ifr.ifr_name))) { + esfatal(0, "interface name \"%s\" too long - max %d", + dpchudp->dpchudp_ifnam, (int)sizeof(ifr.ifr_name)); + } + +#if 0 + /* Don't determine network device to use, if none was specified + ** (since we simply use all interfaces in that case). + */ + if (dpchudp->dpchudp_ifnam[0]) { + /* Find host's IP address for this interface */ + if (!osn_ifipget(-1, dpchudp->dpchudp_ifnam, (unsigned char *)&ihost_ip)) { + efatal(1,"osn_ifipget failed for \"%s\"", dpchudp->dpchudp_ifnam); + } +#endif + /* Set up appropriate net fd. + */ + { + struct sockaddr_in mysin; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + efatal(1,"socket failed: %s", dp_strerror(errno)); + + mysin.sin_family = AF_INET; + mysin.sin_port = htons(dpchudp->dpchudp_port); + mysin.sin_addr.s_addr = INADDR_ANY; +#if 0 + if (dpchudp->dpchudp_ifnam[0] && ihost_ip.s_addr != 0) + mysin.sin_addr.s_addr = ihost_ip.s_addr; +#endif + + if (bind(sock, (struct sockaddr *)&mysin, sizeof(mysin)) < 0) + efatal(1,"bind failed: %s", dp_strerror(errno)); + } +} + +/* CHUDPTOHOST - Child-process main loop for pumping packets from CHUDP to HOST. +** Reads packets from net and feeds CHUDP packets to DP superior process. +*/ + +#define NINBUFSIZ (DPCHUDP_DATAOFFSET+MAXETHERLEN) + +/* Duplicated from dvch11.c - should go in dpsup.c */ +static unsigned int +ch_checksum(const unsigned char *addr, int count) +{ + /* RFC1071 */ + /* Compute Internet Checksum for "count" bytes + * beginning at location "addr". + */ + register long sum = 0; + + while( count > 1 ) { + /* This is the inner loop */ + sum += *(addr)<<8 | *(addr+1); + addr += 2; + count -= 2; + } + + /* Add left-over byte, if any */ + if( count > 0 ) + sum += * (unsigned char *) addr; + + /* Fold 32-bit sum to 16 bits */ + while (sum>>16) + sum = (sum & 0xffff) + (sum >> 16); + + return (~sum) & 0xffff; +} + +void +chudptohost(register struct dpchudp_s *dpchudp) +{ + register struct dpx_s *dpx = dp_dpxfr(&dp); + register int cnt; + unsigned char *inibuf; + unsigned char *buffp; + size_t max; + int stoploop = 50; + struct sockaddr_in ip_sender; + socklen_t iplen; + + inibuf = dp_xsbuff(dpx, &max); /* Get initial buffer ptr */ + + /* Tell KLH10 we're initialized and ready by sending initial packet */ + dp_xswait(dpx); /* Wait until buff free, in case */ + dp_xsend(dpx, DPCHUDP_INIT, 0); /* Send INIT */ + + if (DBGFLG) + fprintf(stderr, "[dpchudp-R: sent INIT]\r\n"); + + for (;;) { + /* Make sure that buffer is free before clobbering it */ + dp_xswait(dpx); /* Wait until buff free */ + + if (DBGFLG) + fprintf(stderr, "[dpchudp-R: InWait]\r\n"); + + /* Set up buffer and initialize offsets */ + buffp = inibuf; + + /* OK, now do a blocking read on UDP socket! */ + errno = 0; /* Clear to make sure it's the actual error */ + memset(&ip_sender, 0, sizeof(ip_sender)); + memset(&iplen, 0, sizeof(iplen)); + cnt = recvfrom(sock, buffp, DPCHUDP_MAXLEN, 0, (struct sockaddr *)&ip_sender, &iplen); + if (cnt <= DPCHUDP_DATAOFFSET) { + if (DBGFLG) + fprintf(stderr, "[dpchudp-R: ERead=%d, Err=%d]\r\n", + cnt, errno); + + if (cnt < 0 && (errno == EINTR)) /* Ignore spurious signals */ + continue; + + /* Error of some kind */ + fprintf(stderr, "[dpchudp-R: Eread = %d, ", cnt); + if (cnt < 0) { + if (--stoploop <= 0) + efatal(1, "Too many retries, aborting]"); + fprintf(stderr, "errno %d = %s]\r\n", + errno, dp_strerror(errno)); + } else if (cnt > 0) + fprintf(stderr, "no chudp data]\r\n"); + else fprintf(stderr, "no packet]\r\n"); + + continue; /* For now... */ + } + if (DBGFLG) { + if (DBGFLG & 0x4) { + fprintf(stderr, "\r\n[dpchudp-R: Read=%d\r\n", cnt); + dumppkt(buffp, cnt); + fprintf(stderr, "]"); + } + else + fprintf(stderr, "[dpchudp-R: Read=%d]", cnt); + } + + /* Have packet, now dispatch it to host */ + + if (((struct chudp_header *)buffp)->chudp_version != CHUDP_VERSION) { + if (DBGFLG) + error("wrong protocol version %d", + ((struct chudp_header *)buffp)->chudp_version); + continue; + } + switch (((struct chudp_header *)buffp)->chudp_function) { + case CHUDP_PKT: + break; /* deliver it */ + default: + error("Unknown CHUDP function: %0X", + ((struct chudp_header *)buffp)->chudp_function); + continue; + } + /* Check who it's from, update Chaos/IP mapping */ + { + /* check that the packet is complete: + 4 bytes CHUDP header, Chaos header, add Chaos pkt length, plus 6 bytes trailer */ + int chalen = ((buffp[DPCHUDP_DATAOFFSET+2] & 0xf)<<4) | buffp[DPCHUDP_DATAOFFSET+3]; + int datalen = (DPCHUDP_DATAOFFSET + CHAOS_HEADERSIZE + chalen); + int chafrom = (buffp[cnt-4]<<8) | buffp[cnt-3]; + unsigned char *ip = (unsigned char *)&ip_sender.sin_addr.s_addr; + in_port_t port = ntohs(ip_sender.sin_port); + time_t now = time(NULL); + int i, cks; + if ((cks = ch_checksum(buffp+DPCHUDP_DATAOFFSET,cnt-DPCHUDP_DATAOFFSET)) != 0) { + if (1 || DBGFLG) + dbprintln("Bad checksum 0x%x",cks); + /* #### must free buffer first(?) */ +/* return; */ + } + if (cnt < datalen + CHAOS_HW_TRAILERSIZE) { /* may have a byte of padding */ + if (1 || DBGFLG) { + dbprintln("Rcvd bad length: %d. < %d. (expected), chaos data %d bytes, errno %d", + cnt, datalen + CHAOS_HW_TRAILERSIZE, chalen, errno); + dumppkt(buffp, cnt); + } + /* #### must free buffer first(?) */ +/* return; */ + } +#if 0 + if (DBGFLG) { + dbprintln("Rcv from chaos %o = ip %d.%d.%d.%d port %d., %d. bytes (datalen %d)", + chafrom, + ip[0],ip[1],ip[2],ip[3], port, + cnt, datalen); + dumppkt(buffp,cnt); + } +#endif + /* #### remove cks when buffer freed instead */ + if ((cks == 0) && (chafrom != myaddr) && (dpchudp->dpchudp_chip_tlen < DPCHUDP_CHIP_MAX)) { + /* Space available, see if we need to add this */ + /* Look for old entries: if one is found which is either + static (from config file) or sufficiently fresh, + keep it and update the freshness (if not static) */ + for (i = 0; i < dpchudp->dpchudp_chip_tlen; i++) + if (chafrom == dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_chaddr) { + if (dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_lastrcvd != 0) { + if (now - dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_lastrcvd > + DPCHUDP_CHIP_DYNAMIC_AGE_LIMIT) { + /* Old, update it (in case he moved) */ + if (1 || DBGFLG) + dbprintln("Updating CHIP entry %d for %o/%d.%d.%d.%d:%d", + i, chafrom, ip[0],ip[1],ip[2],ip[3], port); + dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_ipport = port; + memcpy(&dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_ipaddr, ip, IP_ADRSIZ); + } + /* update timestamp */ + dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_lastrcvd = now; + } + chafrom = -1; /* Not it shouldn't be added */ + break; + } + if (chafrom > 0) { + /* It's OK to write here, the other fork will see it when tlen is updated */ + i = dpchudp->dpchudp_chip_tlen; + if (1 || DBGFLG) + dbprintln("Adding CHIP entry %d for %o/%d.%d.%d.%d:%d", + i, chafrom, ip[0],ip[1],ip[2],ip[3], port); + dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_chaddr = chafrom; + dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_ipport = port; + dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_lastrcvd = now; + memcpy(&dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_ipaddr, ip, IP_ADRSIZ); + dpchudp->dpchudp_chip_tlen++; + } +#if 0 + else if (0 && DBGFLG) { + dbprintln("Already know chaos %o",chafrom); + } +#endif + } + else if ((dpchudp->dpchudp_chip_tlen >= DPCHUDP_CHIP_MAX) && DBGFLG) { + dbprintln("CHIP table full - cannot add %o", chafrom); + } + } + + ihl_hhsend(dpchudp, cnt, buffp); + } +} + + +/* Send regular message from CHUDP to HOST. +*/ + +void +ihl_hhsend(register struct dpchudp_s *dpchudp, + int cnt, + register unsigned char *pp) + /* "pp" is packet data ptr, has room for header preceding */ +{ + + /* Send up to host! Assume we're already in shared buffer. */ + + register struct dpx_s *dpx = dp_dpxfr(&dp); + size_t off = DPCHUDP_DATAOFFSET; + + dpchudp->dpchudp_inoff = off; /* Tell host what offset is */ + dp_xsend(dpx, DPCHUDP_RPKT, cnt-off); + + if (DBGFLG) + fprintf(stderr, "[dpchudp-R: sent RPKT %d+%d]", (int)off, cnt-off); + +} + +/* HOSTTOCHUDP - Parent main loop for pumping packets from HOST to CHUDP. +** Reads CHUDP message from DP superior +** and interprets it. If a regular message, bundles it up and +** outputs to NET. +*/ +void +hosttochudp(register struct dpchudp_s *dpchudp) +{ + register struct dpx_s *dpx = dp_dpxto(&dp); /* Get ptr to "To-DP" dpx */ + register unsigned char *buff; + size_t max; + register int rcnt; + unsigned char *inibuf; + struct in_addr ipdest; + in_port_t ipport; + struct chudp_header chuhdr = { CHUDP_VERSION, CHUDP_PKT, 0, 0 }; + + inibuf = dp_xrbuff(dpx, &max); /* Get initial buffer ptr */ + + if (DBGFLG) + fprintf(stderr, "[dpchudp-W: Starting loop]\r\n"); + + for (;;) { + if (DBGFLG) + fprintf(stderr, "[dpchudp-W: CmdWait]\r\n"); + + /* Wait until 10 has a command for us */ + dp_xrwait(dpx); /* Wait until something there */ + + /* Process command from 10! */ + switch (dp_xrcmd(dpx)) { + + default: + fprintf(stderr, "[dpchudp: Unknown cmd %d]\r\n", dp_xrcmd(dpx)); + dp_xrdone(dpx); + continue; + + case DPCHUDP_SPKT: /* Send regular packet */ + rcnt = dp_xrcnt(dpx); + /* does rcnt include chudp header size? yes. */ + buff = inibuf; + memcpy(buff, (void *)&chuhdr, sizeof(chuhdr)); /* put in CHUDP hdr */ + if (DBGFLG) { + if (DBGFLG & 0x2) { + fprintf(stderr, "\r\n[dpchudp-W: Sending %d\r\n", rcnt); + dumppkt(buff, rcnt); + fprintf(stderr, "]"); + } + else + fprintf(stderr, "[dpchudp-W: SPKT %d]", rcnt); + } + break; + } + + /* Come here to handle output packet */ +#if 1 + { + int chlen = ((buff[DPCHUDP_DATAOFFSET+2] & 0xf) << 4) | buff[DPCHUDP_DATAOFFSET+3]; + if ((chlen + CHAOS_HEADERSIZE + DPCHUDP_DATAOFFSET + CHAOS_HW_TRAILERSIZE) > rcnt) { + if (1 || DBGFLG) + dbprintln("NOT sending less than packet: pkt len %d, expected %d (Chaos data len %d)", + rcnt, (chlen + CHAOS_HEADERSIZE + DPCHUDP_DATAOFFSET + CHAOS_HW_TRAILERSIZE), + chlen); + dp_xrdone(dpx); /* ack it to dvch11, but */ + return; /* #### don't send it! */ + } + } +#endif + + /* find IP destination given chaos packet */ + if (hi_iproute(&ipdest, &ipport, &buff[DPCHUDP_DATAOFFSET], rcnt - DPCHUDP_DATAOFFSET,dpchudp)) { + ip_write(&ipdest, ipport, buff, rcnt, dpchudp); + } + + /* Command done, tell 10 we're done with it */ + if (DBGFLG) + fprintf(stderr, "[dpchudp-W: CmdDone]"); + + dp_xrdone(dpx); + } +} + +int +hi_lookup(struct dpchudp_s *dpchudp, + struct in_addr *ipa, /* Dest IP addr to be put here */ + in_port_t *ipport, /* dest IP port to be put here */ + unsigned int chdest, /* look up this chaos destination */ + int sbnmatch) /* just match subnet part */ +{ + int i; + for (i = 0; i < dpchudp->dpchudp_chip_tlen; i++) + if ( +#if DPCHUDP_DO_ROUTING + ((!sbnmatch && (chdest == dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_chaddr)) + || + /* handle subnet matching */ + (sbnmatch && ((chdest >> 8) == (dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_chaddr >> 8)))) +#else + (chdest == dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_chaddr) +#endif + && dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_ipaddr.s_addr != 0) { + memcpy(ipport, &dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_ipport, sizeof(in_port_t)); + memcpy((void *)ipa, &dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_ipaddr, + sizeof(struct in_addr)); + if (DBGFLG) { + char ipbuf[OSN_IPSTRSIZ]; + dbprintln("found Chaos address %o at %s:%d", + chdest, ip_adrsprint(ipbuf, (unsigned char *)ipa), *ipport); + } + return TRUE; + } + if (DBGFLG) + dbprintln("failed looking up Chaos address %o",chdest); + + return FALSE; +} + +/* HI_IPROUTE - Determine where to actually send Chaos packet. +** Fill in ipa with IP address, based on Chaos header in lp, +** or if DPCHUDP_DO_ROUTING, based on Chaos trailer, +** and also do some basic routing if destination is DEFAULT_CHAOS_ROUTER (3040) +** or just some other subnet we know about. +*/ + +int +hi_iproute(struct in_addr *ipa, /* Dest IP addr to be put here */ + in_port_t *ipport, /* dest IP port to be put here */ + unsigned char *lp, /* Ptr to start of Chaos packet */ + int cnt, /* Cnt of data including header */ + struct dpchudp_s *dpchudp) +{ + unsigned int chdest; +#if DPCHUDP_DO_ROUTING + unsigned int hwdest; +#endif + + if (cnt < CHAOS_HEADERSIZE) { + error("Chaos packet too short: %d", cnt); + return FALSE; + } + +#if !DPCHUDP_DO_ROUTING + /* Derive destination IP address from Chaos header */ + chdest = lp[DPCHUDP_CH_DESTOFF]<<8 | lp[DPCHUDP_CH_DESTOFF+1]; +#else + /* Use hw trailer instead */ + hwdest = lp[cnt-6] << 8 | lp[cnt-5]; + if ((hwdest == DEFAULT_CHAOS_ROUTER) || ((hwdest >> 8) != (myaddr >> 8))) { + /* Look up header dest, + if successful, change trailer dest, increase FW count, recompute checksum */ + if ((lp[DPCHUDP_CH_FC] >> 4) == 0xf) /* FC about to wrap */ + return FALSE; /* drop it */ + /* look in header */ + chdest = lp[DPCHUDP_CH_DESTOFF]<<8 | lp[DPCHUDP_CH_DESTOFF+1]; + } else + /* my subnet */ + chdest = hwdest; + if (DBGFLG) + dbprintln("looking for route to chaos %o (hw %o) (FC %d.)", chdest, hwdest, + lp[DPCHUDP_CH_FC] >> 4); +#endif + if (hi_lookup(dpchudp, ipa, ipport, chdest, 0) +#if DPCHUDP_DO_ROUTING + /* try subnet match */ + || ((chdest != hwdest) && hi_lookup(dpchudp, ipa, ipport, chdest, 1)) +#endif + ) { +#if DPCHUDP_DO_ROUTING + if (chdest != hwdest) { + int cks; + if (DBGFLG) + dbprintln("found route"); + /* change hw dest */ + lp[cnt-6] = chdest >> 8; + lp[cnt-5] = chdest & 0xff; + /* make sure it's got the right hw source addr */ + lp[cnt-4] = myaddr >> 8; + lp[cnt-3] = myaddr & 0xff; + /* increase FW count */ + lp[DPCHUDP_CH_FC] = (lp[DPCHUDP_CH_FC] & 0xf) | ((lp[DPCHUDP_CH_FC]>>4)+1)<<4; + /* recompute checksum */ + cks = ch_checksum(lp,cnt-2); /* w/o orig checksum */ + lp[cnt-2] = cks >> 8; + lp[cnt-1] = cks & 0xff; + } +#endif + return TRUE; + } + return FALSE; +} + + +/* IP_WRITE - Send IP packet out over UDP +*/ + +void +ip_write(struct in_addr *ipa, in_port_t ipport, unsigned char *buf, int len, struct dpchudp_s *dpchudp) +{ + struct sockaddr_in sin; + + if (DBGFLG) { + dbprintln("sending to port %d",ipport); + } + sin.sin_family = AF_INET; + sin.sin_port = htons(ipport); + memcpy((void *)&sin.sin_addr, ipa, sizeof(struct in_addr)); + + if (sendto(sock, buf, len, 0, (struct sockaddr *) &sin, (socklen_t) (sizeof(sin))) != len) { + error("sendto failed - %s", dp_strerror(errno)); + } +} + + +void +dumppkt_raw(unsigned char *ucp, int cnt) +{ + int i; + + while (cnt > 0) { + for (i = 8; --i >= 0 && cnt > 0;) { + if (--cnt >= 0) + fprintf(stderr, " %02x", *ucp++); + if (--cnt >= 0) + fprintf(stderr, "%02x", *ucp++); + } + fprintf(stderr, "\r\n"); + } +} + +static char + *ch_opc[] = { "NIL", + "RFC", "OPN", "CLS", "FWD", "ANS", "SNS", "STS", "RUT", + "LOS", "LSN", "MNT", "EOF", "UNC", "BRD" }; +static char * +ch_opcode(int op) + { + char buf[7]; + if (op < 017 && op > 0) + return ch_opc[op]; + else if (op == 0200) + return "DAT"; + else if (op == 0300) + return "DWD"; + else + return "bogus"; + } + +void +dumppkt(unsigned char *ucp, int cnt) +{ + int i, row; + + fprintf(stderr,"CHUDP version %d, function %d\r\n", ucp[0], ucp[1]); + fprintf(stderr,"Opcode: %o (%s), unused: %o\r\nFC: %o, Nbytes %o\r\n", + ucp[0+DPCHUDP_DATAOFFSET], ch_opcode(ucp[0+DPCHUDP_DATAOFFSET]), + ucp[1+DPCHUDP_DATAOFFSET], ucp[2+DPCHUDP_DATAOFFSET]>>4, ((ucp[2+DPCHUDP_DATAOFFSET]&0xf)<<4) | ucp[3+DPCHUDP_DATAOFFSET]); + fprintf(stderr,"Dest host: %o, index %o\r\nSource host: %o, index %o\r\n", + (ucp[4+DPCHUDP_DATAOFFSET]<<8)|ucp[5+DPCHUDP_DATAOFFSET], (ucp[6+DPCHUDP_DATAOFFSET]<<8)|ucp[7+DPCHUDP_DATAOFFSET], + (ucp[8+DPCHUDP_DATAOFFSET]<<8)|ucp[9+DPCHUDP_DATAOFFSET], (ucp[10+DPCHUDP_DATAOFFSET]<<8)|ucp[11+DPCHUDP_DATAOFFSET]); + fprintf(stderr,"Packet #%o\r\nAck #%o\r\n", + (ucp[12+DPCHUDP_DATAOFFSET]<<8)|ucp[13+DPCHUDP_DATAOFFSET], (ucp[14+DPCHUDP_DATAOFFSET]<<8)|ucp[15+DPCHUDP_DATAOFFSET]); + fprintf(stderr,"Data:\r\n"); + + /* Skip headers */ + ucp += DPCHUDP_DATAOFFSET+CHAOS_HEADERSIZE; + /* Show only data portion */ + cnt -= DPCHUDP_DATAOFFSET+CHAOS_HEADERSIZE+CHAOS_HW_TRAILERSIZE; + + for (row = 0; row*8 < cnt; row++) { + for (i = 0; (i < 8) && (i+row*8 < cnt); i++) { + fprintf(stderr, " %02x", ucp[i+row*8]); + fprintf(stderr, "%02x", ucp[(++i)+row*8]); + } + fprintf(stderr, " (hex)\r\n"); +#if 0 + for (i = 0; (i < 8) && (i+row*8 < cnt); i++) { + fprintf(stderr, " %2c", ucp[i+row*8]); + fprintf(stderr, "%2c", ucp[(++i)+row*8]); + } + fprintf(stderr, " (chars)\r\n"); + for (i = 0; (i < 8) && (i+row*8 < cnt); i++) { + fprintf(stderr, " %2c", ucp[i+1+row*8]); + fprintf(stderr, "%2c", ucp[(i++)+row*8]); + } + fprintf(stderr, " (11-chars)\r\n"); +#endif + } + /* Now show trailer */ + fprintf(stderr,"HW trailer:\r\n Dest: %o\r\n Source: %o\r\n Checksum: 0x%x\r\n", + (ucp[cnt]<<8)|ucp[cnt+1],(ucp[cnt+2]<<8)|ucp[cnt+3],(ucp[cnt+4]<<8)|ucp[cnt+5]); +} + + +/* Add OSDNET shared code here */ + +/* OSDNET is overkill and assumes a lot of packet filter defs, + which we're not at all interested in providing */ +#if 0 +# include "osdnet.c" +#else +char * +ip_adrsprint(char *cp, unsigned char *ia) +{ + sprintf(cp, "%d.%d.%d.%d", ia[0], ia[1], ia[2], ia[3]); + return cp; +} +#endif diff --git a/src/dpchudp.h b/src/dpchudp.h new file mode 100644 index 0000000..d39646a --- /dev/null +++ b/src/dpchudp.h @@ -0,0 +1,108 @@ +/* DPCHUDP.H - Definitions for CHUDP process +*/ +/* Copyright © 2005 Björn Victor and Kenneth L. Harrenstien +** All Rights Reserved +** +** This file may become part of the KLH10 Distribution. Use, modification, and +** re-distribution is permitted subject to the terms in the file +** named "LICENSE", which contains the full text of the legal notices +** and should always accompany this Distribution. +** +** This software is provided "AS IS" with NO WARRANTY OF ANY KIND. +** +** This notice (including the copyright and warranty disclaimer) +** must be included in all copies or derivations of this software. +*/ + +#ifndef DPCHUDP_INCLUDED +#define DPCHUDP_INCLUDED 1 + +#ifndef DPSUP_INCLUDED +# include "dpsup.h" +#endif +#ifndef OSDNET_INCLUDED +# include "osdnet.h" +#endif + +/* Version of DPCHUDP-specific shared memory structure */ + +#define DPCHUDP_VERSION ((1<<10) | (0<<5) | (0)) /* 1.0.0 */ + +#ifndef DPCHUDP_CHIP_MAX +# define DPCHUDP_CHIP_MAX 10 +#endif + +/* If a dynamically added CHIP entry is older than this (seconds), it can get updated */ +#ifndef DPCHUDP_CHIP_DYNAMIC_AGE_LIMIT +# define DPCHUDP_CHIP_DYNAMIC_AGE_LIMIT (60*5) +#endif + +/* Chaos/IP mapping entry - see ch_chip */ +struct dpchudp_chip { + unsigned int dpchudp_chip_chaddr; /* Chaos address */ + struct in_addr dpchudp_chip_ipaddr; /* IP address */ + in_port_t dpchudp_chip_ipport; /* IP port */ + time_t dpchudp_chip_lastrcvd; /* When last received, if dynamically added */ +}; + +/* DPCHUDP-specific stuff */ + /* C = controlling parent sets, D = Device proc sets */ + /* If both, 1st letter indicates inital setter */ +struct dpchudp_s { + struct dpc_s dpchudp_dpc; /* CD Standard DPC portion */ + int dpchudp_ver; /* C Version of shared struct */ + int dpchudp_attrs; /* C Attribute flags */ + char dpchudp_ifnam[16]; /* CD Interface name if any */ + unsigned int dpchudp_myaddr; + /* probably not used */ + int dpchudp_inoff; /* C Offset in buffer of input (I->H) data */ + int dpchudp_outoff; /* D Offset in buffer of output (H->I) data */ + int dpchudp_backlog; /* C Max sys backlog of rcvd packets */ + int dpchudp_dedic; /* C TRUE if dedicated ifc, else shared */ + in_port_t dpchudp_port; /* C port for CHUDP protocol */ + /* Chaos/IP mapping */ + int dpchudp_chip_tlen; /* C table length */ + struct dpchudp_chip dpchudp_chip_tbl[DPCHUDP_CHIP_MAX]; +}; + + +/* Buffer offset: + CHUDP protocol header is 4 bytes; read/write data after those +*/ +struct chudp_header { + char chudp_version; + char chudp_function; + char chudp_arg1; + char chudp_arg2; +}; + +/* CHUDP protocol port - should perhaps be registered? */ +#define CHUDP_PORT 42042 +/* Protocol version */ +#define CHUDP_VERSION 1 +/* Protocol function codes */ +#define CHUDP_PKT 1 /* Chaosnet packet */ + + +#include "dvch11.h" +#define DPCHUDP_DATAOFFSET (sizeof(struct chudp_header)) +#define DPCHUDP_MAXLEN (CHAOS_MAXDATA+DPCHUDP_DATAOFFSET+42) /* some slack */ + +#ifndef DPCHUDP_DO_ROUTING +# define DPCHUDP_DO_ROUTING 1 +#endif +#define DEFAULT_CHAOS_ROUTER 03040 /* default router (MX-11.LCS.MIT.EDU) */ +#define DPCHUDP_CH_DESTOFF 4 /* offset to dest addr in chaos pkt */ +#define DPCHUDP_CH_FC 2 /* offset to forwarding count */ + +/* Commands to and from DP and KLH10 CH11 driver */ + + /* From 10 to DP */ +#define DPCHUDP_RESET 0 /* Reset DP */ +#define DPCHUDP_SPKT 1 /* Send data packet to ethernet */ + + /* From DP to 10 */ +#define DPCHUDP_INIT 1 /* DP->10 Finished init */ +#define DPCHUDP_RPKT 2 /* DP->10 Received data packet from net */ + +#endif /* ifndef DPCHUDP_INCLUDED */ diff --git a/src/dvch11.c b/src/dvch11.c index a4ac9cb..ccea801 100644 --- a/src/dvch11.c +++ b/src/dvch11.c @@ -2,10 +2,10 @@ */ /* $Id: dvch11.c,v 2.3 2001/11/10 21:28:59 klh Exp $ */ -/* Copyright © 1992, 1993, 2001 Kenneth L. Harrenstien +/* Copyright © 2005 Björn Victor and Kenneth L. Harrenstien ** All Rights Reserved ** -** This file is part of the KLH10 Distribution. Use, modification, and +** This file may become part of the KLH10 Distribution. Use, modification, and ** re-distribution is permitted subject to the terms in the file ** named "LICENSE", which contains the full text of the legal notices ** and should always accompany this Distribution. @@ -22,7 +22,17 @@ * */ -/* Just a dummy for now; writes do nothing and reads return 0. +/* NOT just a dummy! +** +** See MIT AI Memo 628, "Chaosnet", by David A. Moon (esp. chapter 7) for documentation. +** Note: chapter 7 ("Hardware Programming Documentation") is not completely correct, +** and should be cross-referenced with the ITS source (SYSTEM;CHAOS). +** E.g. the "timer interrupt enable" bit is "transmit busy", and the +** r/w-only definitions of CSR bits is sometimes wrong (e.g. "transmit +** done"). +** +** Based on dvlhdh.c, with dpchudp.c based on dpimp.c. +** (Some things may still be irrelevant inheritage from dvlhdh, and could be cleaned up...) */ #include "klh10.h" @@ -36,29 +46,1321 @@ static int decosfcclossage; #if KLH10_DEV_CH11 /* Moby conditional for entire file */ +#include +#include + #include "kn10def.h" #include "kn10dev.h" +#include "prmstr.h" /* For parameter parsing */ #include "dvuba.h" #include "dvch11.h" -#ifdef RCSID - RCSID(dvch11_c,"$Id: dvch11.c,v 2.3 2001/11/10 21:28:59 klh Exp $") +#include "dpchudp.h" + +#ifndef KLH10_CH11_USE_GETHOSTBYNAME +# define KLH10_CH11_USE_GETHOSTBYNAME 1 #endif -struct device dvch11; /* Device structure for one CH11 */ +#if KLH10_CH11_USE_GETHOSTBYNAME +# include +#endif + +#define CHUDPBUFSIZ (DPCHUDP_MAXLEN+500) /* Plenty of slop */ + + +#ifndef CH11_NSUP +# define CH11_NSUP 1 /* Only one device supported */ +#endif + +#ifndef CH11_CHIP_MAX +# define CH11_CHIP_MAX 10 /* max Chaos/IP mappings - see DPCHUDP_CHIP_MAX */ +#endif + +#if CH11_CHIP_MAX > DPCHUDP_CHIP_MAX +# error "DPCHUDP_CHIP_MAX must be at least CH11_CHIP_MAX" +#endif + +#define REG(u) ((u)->ch_reg) + +/* Chaos/IP mapping entry - see dpchudp_chip */ +struct ch_chip { + unsigned int ch_chip_chaddr; /* Chaos address */ + struct in_addr ch_chip_ipaddr; /* IP address */ + in_port_t ch_chip_ipport; /* IP port */ +}; + +struct ch11 { + struct device ch_dv; /* Generic 10 device structure */ + + /* CH11-specific vars */ + + /* CH11 internal register (only one) */ + dvureg_t ch_reg; /* Storage for register (16 bits) */ + unsigned short ch_lost; /* # msgs rcved with rcv bfr full */ + + /* I/O signalling flags */ + int ch_ipireq; /* Input side doing PI request */ + int ch_opireq; /* Output side doing PI request */ + int ch_inactf; /* TRUE if input can be done */ + int ch_outactf; /* TRUE if output can be done */ + unsigned char *ch_iptr; /* Pointer to input data */ + unsigned char *ch_optr; /* Pointer to output data */ + + /* Misc config info not set elsewhere */ + char *ch_ifnam; /* Native platform's interface name */ + int ch_dedic; /* TRUE if interface dedicated (else shared) */ + int ch_backlog; /* Max # input msgs to queue up in kernel */ + unsigned int ch_myaddr; /* My Chaos address */ + in_port_t ch_chudp_port; /* CHUDP port to use */ + + /* DP stuff */ + char *ch_dpname; /* Pointer to dev process pathname */ + int ch_dpidly; /* # secs to sleep when starting DP */ + int ch_dpdbg; /* Initial DP debug flag */ + + int ch_dpstate; /* TRUE if dev process has finished its init */ + struct dp_s ch_dp; /* Handle on dev process */ + unsigned char *ch_sbuf; /* Pointers to shared memory buffers */ + unsigned char *ch_rbuf; + int ch_rcnt; /* # chars in received packet input buffer */ + + /* Chaos/IP mapping */ + int ch_chip_tlen; /* table length */ + struct ch_chip ch_chip_tbl[CH11_CHIP_MAX]; +}; + +static int nch11s = 0; +struct ch11 dvch11[CH11_NSUP]; /* Can be static, but left external for debugging */ +/* Function predecls */ -struct device *dvch11_init(FILE *f, char *s) +static int ch11_conf(FILE *f, char *s, struct ch11 *ch); +static int ch11_init(struct device *d, FILE *of); +static int ch11_cmd(register struct device *d, FILE *of, char *cmd); +static dvureg_t ch11_pivec(struct device *d); +static dvureg_t ch11_read(struct device *d, uint18 addr); +static void ch11_write(struct device *d, uint18 addr, dvureg_t val); +static void ch11_clear(struct device *d); +static void ch11_powoff(struct device *d); + +static void ch_clear(struct ch11 *ch); +static void ch_oint(struct ch11 *ch); +static void ch_iint(struct ch11 *ch); +static void ch_igo(struct ch11 *ch); +static void ch_ogo(struct ch11 *ch); +static void ch_idone(struct ch11 *ch); +static void ch_odone(struct ch11 *ch); +static void showpkt(FILE *f, char *id, unsigned char *buf, int cnt); + + /* Virtual CHUDP low-level stuff */ +/* static */ +int chudp_init(struct ch11 *ch, FILE *of); +static int chudp_start(struct ch11 *ch); +static void chudp_stop(struct ch11 *ch); +static void chudp_kill(struct ch11 *ch); +static int chudp_incheck(struct ch11 *ch); +static void chudp_inxfer(struct ch11 *ch); +static int chudp_outxfer(struct ch11 *ch); + +/* Configuration Parameters */ + +#define DVCH11_PARAMS \ + prmdef(CH11P_DBG, "debug"), /* Initial debug value */\ + prmdef(CH11P_BR, "br"), /* BR priority */\ + prmdef(CH11P_VEC, "vec"), /* Interrupt vector */\ + prmdef(CH11P_ADDR,"addr"), /* Unibus address */\ +\ + prmdef(CH11P_IFC,"ifc"), /* Ethernet interface name */\ + prmdef(CH11P_BKL,"backlog"), /* Max bklog for rcvd pkts (else sys deflt) */\ + prmdef(CH11P_DED,"dedic"), /* TRUE= Ifc dedicated (else shared) */\ + prmdef(CH11P_DPDLY,"dpdelay"),/* # secs to sleep when starting DP */\ + prmdef(CH11P_DPDBG,"dpdebug"), /* Initial DP debug value */\ + prmdef(CH11P_DP, "dppath"), /* Device subproc pathname */ \ +\ + prmdef(CH11P_MYADDR, "myaddr"),/* My Chaosnet address */\ + prmdef(CH11P_CHUPORT, "chudpport"), /* CHUDP port to use */\ + prmdef(CH11P_CHIP, "chip") /* Chaos/IP mapping */ + + +enum { +# define prmdef(i,s) i + DVCH11_PARAMS +# undef prmdef +}; + +static char *ch11prmtab[] = { +# define prmdef(i,s) s + DVCH11_PARAMS +# undef prmdef + , NULL +}; + +static int parip(char *cp, unsigned char *adr); + +/* CH11_CONF - Parse configuration string and set defaults. +** At this point, device has just been created, but not yet bound +** or initialized. +** NOTE that some strings are dynamically allocated! Someday may want +** to clean them up nicely if config fails or device is uncreated. +*/ + +static int +ch11_conf(FILE *f, char *s, struct ch11 *ch) { - iodv_setnull(&dvch11); /* Set up as null device */ + int i, ret = TRUE; + struct prmstate_s prm; + char buff[200]; + long lval; - dvch11.dv_addr = UB_CH11; /* 1st valid Unibus address */ - dvch11.dv_aend = UB_CH11END; /* 1st invalid Unibus address */ - dvch11.dv_brlev = UB_CH11_BR; - dvch11.dv_brvec = UB_CH11_VEC; + /* First set defaults for all configurable parameters + Unfortunately there's currently no way to access the UBA # that + we're gonna be bound to, otherwise could set up defaults. Later + fix this by giving ch11_create() a ptr to an arg structure, etc. + */ + DVDEBUG(ch) = FALSE; + ch->ch_ifnam = NULL; + ch->ch_backlog = 0; + ch->ch_dedic = FALSE; + ch->ch_dpidly = 0; + ch->ch_dpdbg = FALSE; + ch->ch_dpname = "dpchudp"; /* Pathname of device subproc */ + ch->ch_chip_tlen = 0; + ch->ch_chudp_port = CHUDP_PORT; - return &dvch11; + prm_init(&prm, buff, sizeof(buff), + s, strlen(s), + ch11prmtab, sizeof(ch11prmtab[0])); + while ((i = prm_next(&prm)) != PRMK_DONE) { + switch (i) { + case PRMK_NONE: + fprintf(f, "Unknown CH11 parameter \"%s\"\n", prm.prm_name); + ret = FALSE; + continue; + case PRMK_AMBI: + fprintf(f, "Ambiguous CH11 parameter \"%s\"\n", prm.prm_name); + ret = FALSE; + continue; + default: /* Handle matches not supported */ + fprintf(f, "Unsupported CH11 parameter \"%s\"\n", prm.prm_name); + ret = FALSE; + continue; + + case CH11P_DBG: /* Parse as true/false boolean or number */ + if (!prm.prm_val) /* No arg => default to 1 */ + DVDEBUG(ch) = 1; + else if (!s_tobool(prm.prm_val, &DVDEBUG(ch))) + break; + continue; + + case CH11P_BR: /* Parse as octal number */ + if (!prm.prm_val || !s_tonum(prm.prm_val, &lval)) + break; + if (lval < 4 || lval > 7) { + fprintf(f, "CH11 BR must be one of 4,5,6,7\n"); + ret = FALSE; + } else + ch->ch_dv.dv_brlev = lval; + continue; + + case CH11P_VEC: /* Parse as octal number */ + if (!prm.prm_val || !s_tonum(prm.prm_val, &lval)) + break; + if (lval < 4 || lval > 0400 || (lval&03)) { + fprintf(f, "CH11 VEC must be valid multiple of 4\n"); + ret = FALSE; + } else + ch->ch_dv.dv_brvec = lval; + continue; + + case CH11P_ADDR: /* Parse as octal number */ + if (!prm.prm_val || !s_tonum(prm.prm_val, &lval)) + break; + if (lval < UB_CH11 || (lval&037)) { + fprintf(f, "CH11 ADDR must be valid Unibus address\n"); + ret = FALSE; + } else + ch->ch_dv.dv_addr = lval; + continue; + + case CH11P_MYADDR: /* Parse as octal number */ + if (!prm.prm_val || !s_tonum(prm.prm_val, &lval)) + break; + if ((lval < 1) || (lval >= 0xffff)) { + fprintf(f, "CH11 MYADDR must be a valid Chaosnet address\n"); + ret = FALSE; + } else + ch->ch_myaddr = lval; + continue; + + case CH11P_CHUPORT: + if (!prm.prm_val || !s_todnum(prm.prm_val, &lval)) + break; + if ((lval < 1) || (lval >= 0xffff)) { + fprintf(f, "CH11 CHUDPPORT must be a valid UDP port\n"); + break; + } else + ch->ch_chudp_port = lval; + continue; + + case CH11P_CHIP: + if (ch->ch_chip_tlen >= CH11_CHIP_MAX) { + fprintf(f,"CH11 Chaos/IP table full\n"); + break; + } + if (!prm.prm_val) + break; + { + char *c, *s = index(prm.prm_val, '/'); + if (s == NULL) + break; + else { + unsigned long cha; + unsigned char ipa[IP_ADRSIZ]; + struct hostent *he; + in_port_t ipp = CHUDP_PORT; + struct ch_chip *chip; + int idx; + *(s++) = '\0'; /* separate chaos from ip */ + if (!s_tonum(prm.prm_val, (long*)&cha)) { + *(--s) = '/'; /* failed to parse chaos, put back slash */ + break; /* and complain */ + } + if ((c = index(s,':')) != NULL) { + long x; + if (s_todnum(c+1,&x) && (x > 0) && (x < 0xffff)) { + ipp = x; + } else { + fprintf(f,"CH11 Chaos/IP mapping port number invalid"); + *(s-1) = '/'; + break; + } + *c = '\0'; /* zap for IP parsing */ + } +#if KLH10_CH11_USE_GETHOSTBYNAME + if ((he = gethostbyname(s)) == NULL) +#else + if (!parip(s, &ipa[0])) +#endif + { + *(--s) = '/'; /* put back slash */ + if (c) + *c = ':'; /* and colon */ + break; /* and complain */ + } +#if KLH10_CH11_USE_GETHOSTBYNAME + if ((he->h_addrtype != AF_INET) || (he->h_length != IP_ADRSIZ)) { + fprintf(stderr,"CH11 CHIP spec found non-IPv4 address"); + break; + } +#endif + idx = (ch->ch_chip_tlen); /* new index */ + ch->ch_chip_tbl[idx].ch_chip_chaddr = cha; + ch->ch_chip_tbl[idx].ch_chip_ipport = ipp; +#if KLH10_CH11_USE_GETHOSTBYNAME + memcpy(&ch->ch_chip_tbl[idx].ch_chip_ipaddr, he->h_addr, IP_ADRSIZ); +#else + memcpy(&ch->ch_chip_tbl[idx].ch_chip_ipaddr, &ipa[0], IP_ADRSIZ); +#endif + ch->ch_chip_tlen = idx+1; /* update length */ + } + continue; + } + case CH11P_IFC: /* Parse as simple string */ + if (!prm.prm_val) + break; + ch->ch_ifnam = s_dup(prm.prm_val); + continue; + + case CH11P_BKL: /* Parse as decimal number */ + if (!prm.prm_val || !s_todnum(prm.prm_val, &lval)) + break; + ch->ch_backlog = lval; + continue; + + case CH11P_DED: /* Parse as true/false boolean */ + if (!prm.prm_val) + break; + if (!s_tobool(prm.prm_val, &ch->ch_dedic)) + break; + continue; + + case CH11P_DPDLY: /* Parse as decimal number */ + if (!prm.prm_val || !s_todnum(prm.prm_val, &lval)) + break; + ch->ch_dpidly = lval; + continue; + + case CH11P_DPDBG: /* Parse as true/false boolean or number */ + if (!prm.prm_val) /* No arg => default to 1 */ + ch->ch_dpdbg = 1; + else if (!s_tobool(prm.prm_val, &(ch->ch_dpdbg))) + break; + continue; + + case CH11P_DP: /* Parse as simple string */ + if (!prm.prm_val) + break; + ch->ch_dpname = s_dup(prm.prm_val); + continue; + + } + ret = FALSE; + fprintf(f, "CH11 param \"%s\": ", prm.prm_name); + if (prm.prm_val) + fprintf(f, "bad value syntax: \"%s\"\n", prm.prm_val); + else + fprintf(f, "missing value\n"); + } + + /* Param string all done, do followup checks or cleanup */ + if (!ch->ch_dv.dv_brlev || !ch->ch_dv.dv_brvec || !ch->ch_dv.dv_addr) { + fprintf(f, "CH11 missing one of BR, VEC, ADDR params\n"); + ret = FALSE; + } + if (ch->ch_dedic || ch->ch_backlog || ch->ch_dpidly || ch->ch_ifnam) + fprintf(f, "CH11 params \"dedic\", \"backlog\", \"dpdelay\", \"ifc\" not supported, ignoring"); + /* Set 1st invalid addr */ + ch->ch_dv.dv_aend = ch->ch_dv.dv_addr + (UB_CH11END-UB_CH11); + + /* MYADDR must always be set! */ + if (ch->ch_myaddr == 0) { + fprintf(f, + "CH11 param \"myaddr\" must be set\n"); + return FALSE; + } + + return ret; +} + +static int +parip(char *cp, unsigned char *adr) +{ + unsigned int b1, b2, b3, b4; + + if (4 != sscanf(cp, "%u.%u.%u.%u", &b1, &b2, &b3, &b4)) + return FALSE; + if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255) + return FALSE; + *adr++ = b1; + *adr++ = b2; + *adr++ = b3; + *adr = b4; + return TRUE; +} + + +/* CH11 interface routines to KLH10 */ + +struct device * +dvch11_create(FILE *f, char *s) +{ + register struct ch11 *ch; + + /* Allocate a CH11 device structure */ + if (nch11s >= CH11_NSUP) { + fprintf(f, "Too many CH11s, max: %d\n", CH11_NSUP); + return NULL; + } + ch = &dvch11[nch11s++]; /* Pick unused CH11 */ + + /* Various initialization stuff */ + memset((char *)ch, 0, sizeof(*ch)); + + iodv_setnull(&ch->ch_dv); /* Init as null device */ + + ch->ch_dv.dv_init = ch11_init; /* Set up own post-bind init */ + ch->ch_dv.dv_reset = ch11_clear; /* System reset (clear stuff) */ + ch->ch_dv.dv_powoff = ch11_powoff; /* Power-off cleanup */ + ch->ch_dv.dv_cmd = ch11_cmd; /* General user command to device */ + + /* Unibus stuff */ + ch->ch_dv.dv_pivec = ch11_pivec; /* Return PI vector */ + ch->ch_dv.dv_read = ch11_read; /* Read unibus register */ + ch->ch_dv.dv_write = ch11_write; /* Write unibus register */ + + /* Configure from parsed string and remember for init + */ + if (!ch11_conf(f, s, ch)) + return NULL; + + return &ch->ch_dv; +} + + +static void +ch11_cmd_dpdebug(struct ch11 *ch, FILE *of, int val) +{ + struct dpchudp_s *dpc = (struct dpchudp_s *)ch->ch_dp.dp_adr; + fprintf(of,"Old value: %d. New value: %d.\n", dpc->dpchudp_dpc.dpc_debug, val); + dpc->dpchudp_dpc.dpc_debug = val; +} + +static void +ch11_cmd_chiptable(struct ch11 *ch, FILE *of) +{ + int n, i; + unsigned char *ip; + char ipa[4*4]; + struct tm *ltime; + char last[128]; + struct dpchudp_chip *chip; + struct dpchudp_s *dpc = (struct dpchudp_s *)ch->ch_dp.dp_adr; + if (!dpc) { + fprintf(of,"Can't find DP!\n"); + return; + } + n = dpc->dpchudp_chip_tlen; + fprintf(of,"Currently %d entries in Chaos/IP table\n",n); + if (n > 0) { + fprintf(of,"Chaos IP Port Last received\n"); + for (i = 0; i < n; i++) { + chip = &dpc->dpchudp_chip_tbl[i]; + ip = (unsigned char *)&chip->dpchudp_chip_ipaddr.s_addr; + sprintf(ipa,"%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + if (chip->dpchudp_chip_lastrcvd != 0) { + ltime = localtime(&chip->dpchudp_chip_lastrcvd); + strftime(last, sizeof(last), "%Y-%m-%d %T", ltime); + } else + strcpy(last,"[static]"); + fprintf(of,"%6o %-15s %d. %s\n", + chip->dpchudp_chip_chaddr, + ipa, + chip->dpchudp_chip_ipport, + last); + } + } +} + +static void +ch11_cmd_status(struct ch11 *ch, FILE *of) +{ + fprintf(of,"My CHAOS address: 0%o, CHUDP port: %d.\n", ch->ch_myaddr, ch->ch_chudp_port); + fprintf(of,"Status register: 0%o\n", REG(ch)); + if (REG(ch) & CH_BSY) + fprintf(of, " Transmit busy\n"); + if (REG(ch) & CH_LUP) + fprintf(of, " Loopback\n"); + if (REG(ch) & CH_SPY) + fprintf(of, " Spy (promiscuous)\n"); + if (REG(ch) & CH_REN) + fprintf(of, " Receive enabled\n"); + if (REG(ch) & CH_TEN) + fprintf(of, " Transmit enabled\n"); + if (REG(ch) & CH_TAB) + fprintf(of, " Transmit aborted\n"); + if (REG(ch) & CH_TDN) + fprintf(of, " Transmit done\n"); + if (REG(ch) & CH_RDN) + fprintf(of, " Receive done\n"); + if (REG(ch) & CH_ERR) + fprintf(of, " CRC error\n"); + fprintf(of, " Lost count: %d\n", ch->ch_lost); + fprintf(of,"PI request: input %d, output: %d\n", ch->ch_ipireq, ch->ch_opireq); + fprintf(of,"Input possible: %d, Output possible: %d\n", ch->ch_inactf, ch->ch_outactf); + fprintf(of, "DP status: %d\n", ch->ch_dpstate); + fprintf(of, "DP Rtest: %d, Stest: %d\n", + (int)dp_xrtest(dp_dpxfr(&ch->ch_dp)), (int)dp_xstest(dp_dpxfr(&ch->ch_dp))); + fprintf(of, "DP rcmd: %d, rcnt: %d\n", + (int)dp_xrcmd(dp_dpxfr(&ch->ch_dp)), (int)dp_xrcnt(dp_dpxfr(&ch->ch_dp))); + fprintf(of,"Input buffer: "); + if (ch->ch_iptr) + fprintf(of,"%d chars\n",ch->ch_iptr - ch->ch_rbuf); + else + fprintf(of,"none\n"); + fprintf(of,"Output buffer: "); + if (ch->ch_optr) + fprintf(of,"%d chars\n",ch->ch_optr - ch->ch_sbuf); + else + fprintf(of,"none\n"); + fprintf(of,"Receive count: %d\n", ch->ch_rcnt); +} + +static int +ch11_cmd(register struct device *d, FILE *of, char *cmd) +{ + register struct ch11 *ch = (struct ch11 *)d; + + if (*cmd) + while (*cmd == ' ') + cmd++; + if (*cmd && (strcmp(cmd,"chiptable") == 0)) + ch11_cmd_chiptable(ch, of); + else if (*cmd && (strcmp(cmd,"status") == 0)) + ch11_cmd_status(ch, of); + else if (*cmd && strncmp(cmd,"dpdebug",strlen("dpdebug")) == 0) { + int val = 0; + cmd += strlen("dpdebug"); + while (*cmd == ' ') + cmd++; + if (s_tobool(cmd, &val)) + ch11_cmd_dpdebug(ch, of, val); + else + fprintf(of,"Couldn't grok argument: \"%s\"\n", cmd); + } + else if (*cmd) { + fprintf(of,"Unknown command \"%s\"\n", cmd); + fprintf(of,"Commands:\n \"chiptable\" to show the Chaos/IP table\n \"status\" to show device status\n \"dpdebug x\" to set dpchudp debug level to x\n"); + } else + /* No command, do both */ + ch11_cmd_status(ch, of); + ch11_cmd_chiptable(ch, of); + return TRUE; +} + +static dvureg_t +ch11_pivec(register struct device *d) +{ + (*d->dv_pifun)(d, 0); /* Turn off interrupt request */ + return d->dv_brvec; /* Return vector to use */ +} + +static int +ch11_init(struct device *d, FILE *of) +{ + register struct ch11 *ch = (struct ch11 *)d; + + if (!chudp_init(ch, of)) + return FALSE; + ch_clear(ch); + return TRUE; +} + +/* CH11_POWOFF - Handle "power-off" which usually means the KLH10 is +** being shut down. This is important if using a dev subproc! +*/ +static void +ch11_powoff(struct device *d) +{ + chudp_kill((struct ch11 *)d); +} + + +static void +ch11_clear(register struct device *d) +{ + ch_clear((struct ch11 *)d); +} + +/* CH_CLEAR - clear device */ +static void +ch_iint_off(register struct ch11 *ch) +{ + if (ch->ch_ipireq) { + ch->ch_ipireq = 0; /* Clear any interrupt request */ + if (ch->ch_opireq == 0) /* if no output interrupt requested */ + (*ch->ch_dv.dv_pifun)(&ch->ch_dv, 0); + } +} +static void +ch_oint_off(register struct ch11 *ch) +{ + if (ch->ch_opireq) { + ch->ch_opireq = 0; /* Clear any interrupt request */ + if (ch->ch_ipireq == 0) /* if no input interrupt requested */ + (*ch->ch_dv.dv_pifun)(&ch->ch_dv, 0); + } +} + +static void +ch_iclear(register struct ch11 *ch) +{ + ch_iint_off(ch); + if (ch->ch_rbuf) + ch->ch_iptr = ch->ch_rbuf + DPCHUDP_DATAOFFSET; + else + ch->ch_iptr = NULL; + ch->ch_rcnt = -1; + ch->ch_lost = 0; /* When receiver is re-enabled (write 1 into %CARDN) + the count is then cleared. */ + ch->ch_inactf = TRUE; /* OK to receive */ + REG(ch) &= ~(CH_ERR|CH_RDN); /* No checksum error, no pkt here */ +} + +static void +ch_oclear(register struct ch11 *ch) +{ + ch_oint_off(ch); + if (ch->ch_sbuf) + ch->ch_optr = ch->ch_sbuf + DPCHUDP_DATAOFFSET; + else + ch->ch_optr = NULL; + REG(ch) &= ~CH_TAB; /* Not aborted */ + if (ch->ch_outactf) /* OK to transmit? */ + REG(ch) |= CH_TDN; /* Ready to send (again) */ +} + +static void +ch_clear(register struct ch11 *ch) +{ + chudp_stop(ch); /* Kill CHUDP process, ready line going down */ + + ch->ch_outactf = TRUE; /* Initialise to true */ + + ch_iclear(ch); + ch_oclear(ch); +} + +static unsigned int +ch_checksum(const unsigned char *addr, int count) +{ + /* RFC1071 */ + /* Compute Internet Checksum for "count" bytes + * beginning at location "addr". + */ + register long sum = 0; + + while( count > 1 ) { + /* This is the inner loop */ + sum += *(addr)<<8 | *(addr+1); + addr += 2; + count -= 2; + } + + /* Add left-over byte, if any */ + if( count > 0 ) + sum += * (unsigned char *) addr; + + /* Fold 32-bit sum to 16 bits */ + while (sum>>16) + sum = (sum & 0xffff) + (sum >> 16); + + return (~sum) & 0xffff; +} + +static dvureg_t +ch11_read(struct device *d, register uint18 addr) +{ + register struct ch11 *ch = (struct ch11 *)d; + dvureg_t val; + + if (DVDEBUG(ch) > 4) + fprintf(DVDBF(ch), "[CH11 Read %#o]\r\n", addr); + + if (ch->ch_dp.dp_chpid == 0) /* if DP process is not running, start it */ + chudp_start(ch); + + switch (addr) { +/* case UB_CHWBF: /* Write buffer (write only) = CHMYN */ +/* break; */ + case UB_CHMYN: /* My chaos address (read only) */ + val = ch->ch_myaddr; + break; + case UB_CHCSR: /* Command status reg */ + val = ch->ch_reg; + val &= ~(CH_LOS); /* clear lost field */ + val |= (ch->ch_lost & 017) << 9; /* insert current counter */ + break; + case UB_CHRBF: /* Read buffer (read only) */ + /* The first word read will be filled to the left + to make the message recieved a multiple of 16 bits. + #### (Assume this is managed by ITS, until found wrong.) + */ + if (ch->ch_rcnt > 0) { + val = *(ch->ch_iptr++) << 8; + val |= *(ch->ch_iptr++); + if ((ch->ch_iptr - ch->ch_rbuf) >= (ch->ch_rcnt + DPCHUDP_DATAOFFSET)) { + if (DVDEBUG(ch) > 4) + fprintf(DVDBF(ch), "[CH11 reading last word, clearing RDN]\r\n"); + ch->ch_rcnt = -1; /* read last word */ + REG(ch) &= ~CH_RDN; /* Done receiving? */ + } + break; + } + val = 0; + break; + case UB_CHRBC: /* Receive bit counter (read only) */ + if (ch->ch_rcnt > 0) + val = (dvureg_t)ch->ch_rcnt*8-1; /* byte count */ + else + val = (dvureg_t)07777; + break; + case UB_CHXMT: /* Initiate transmit (read only) */ + /* last 16b in buffer is dest addr; + add source addr and checksum, and send + */ + if (ch->ch_optr) { /* #### range check too */ + int cks, len; + /* Dest addr already in data, checksum at end */ + len = (ch->ch_optr - ch->ch_sbuf)-DPCHUDP_DATAOFFSET+2; + /* Add source */ + *(ch->ch_optr++) = (ch->ch_myaddr>>8); + *(ch->ch_optr++) = (ch->ch_myaddr & 0xff); + /* Make checksum */ + cks = ch_checksum(&ch->ch_sbuf[DPCHUDP_DATAOFFSET],len); + *(ch->ch_optr++) = cks >> 8; + *(ch->ch_optr++) = cks & 0xff; + + chudp_outxfer(ch); /* Send it to DP */ + } else + panic("ch11_read: no output pointer available at CHXMT"); + val = (dvureg_t) ch->ch_myaddr; + break; + default: + panic("ch11_read: Unknown register %lo", (long)addr); + } + if (DVDEBUG(ch) > 4) + fprintf(DVDBF(ch), "[CH11 Read %#o => %o]\r\n", addr, val); + return val; +} + +/* Write CH11 registers. +*/ +static void +ch11_write(struct device *d, uint18 addr, register dvureg_t val) +{ + register struct ch11 *ch = (struct ch11 *)d; + + val &= MASK16; + if (DVDEBUG(ch) > 4) + fprintf(DVDBF(ch), "[CH11 Write %#o <= %#lo]\r\n", addr, (long)val); + + if (ch->ch_dp.dp_chpid == 0) /* if DP process is not running, start it */ + chudp_start(ch); + + switch (addr) { +/* case UB_CHMYN: /* My chaos address (read only) = CHWBF */ + case UB_CHRBF: /* Read buffer (read only) */ + case UB_CHRBC: /* Receive bit counter (read only) */ + case UB_CHXMT: /* Initiate transmit (read only) */ + panic("ch11_write: read-only address %lo", (long)addr); + break; + case UB_CHWBF: /* Write buffer (write only) */ + /* AIM628: TDN cleared when a word is written into the outgoingpacket buffer */ + REG(ch) &= ~CH_TDN; + if (ch->ch_optr) { /* #### range check too */ + *(ch->ch_optr++) = val>>8; /* write two bytes */ + *(ch->ch_optr++) = val&0xff; + } else { + panic("ch11_write: no output pointer available at CHWBF"); + } + return; + case UB_CHCSR: /* Command status reg */ + if (val & (CH_BSY | CH_TAB | CH_LOS | CH_ERR)) { + panic("ch11_write: writing read-only bits to CSR: %lo", (long)val); + break; + } + if (val & (CH_LUP | CH_SPY)) { + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CH11: loopback/spy being set - NYI]\r\n"); + /* AIM628: + LUP: the cable and transmitter are not used and the interface + is looped back to itself. This is for maintenance. + SPY: the interface will receive all packets regardless of + their destination. This is for maintenance and network + monitoring. + */ + /* LUP could possibly be usefully implemented, but SPY not, over UDP. */ + val &= ~(CH_LUP | CH_SPY); + } + if (val & CH_RST) { /* I/O reset */ + ch_clear(ch); + if (DVDEBUG(ch) > 4) + fprintf(DVDBF(ch),"[CH11 new CSR contents %#lo]\r\n", (long)REG(ch)); + return; + } + if (val & CH_RCL) { /* Clear and enable the receiver, it can now gobble another msg */ + /* AIM628: clears RDN and enables rcvr to receive another pkt */ + /* AIM628: resets the lost count to 0 */ + val &= ~(CH_RCL|CH_RDN); + /* reset buffer, bit count etc */ + ch_iclear(ch); + } + if (val & CH_TCL) { /* Clear the transmitter, making it ready */ + /* AIM628: stops transmitter and sets TDN */ + /* #### do we need to "stop transmitter" (dpchudp)? */ + val &= ~CH_TCL; + val |= CH_TDN; + /* Done below */ +/* ch_oclear(ch); */ + } + if (val & CH_RDN) { /* RCV DONE */ + val &= ~CH_RDN; + ch_iclear(ch); /* same as RCL? */ + } + + REG(ch) |= val; /* save bits */ + + if (val & CH_TDN) /* Transmit Done. Set when transmitter is done */ + ch_oclear(ch); /* same effect as TCL? (cf SYSTEM;CHAOS, CHXBK7+4) */ + + /* AIM628: when both xDN and xEN are set, the computer is interrupted */ + if ((val & CH_TEN) && (val & CH_REN)) { + /* try to be clever */ + if (dp_xstest(dp_dpxfr(&ch->ch_dp))) { + ch_ogo(ch); /* sender's turn, process output first */ + ch_igo(ch); + } else { + ch_igo(ch); /* receiver's turn */ + ch_ogo(ch); + } + } else if (val & CH_TEN) { + REG(ch) &= ~CH_REN; + ch_iint_off(ch); + ch_ogo(ch); + } else if (val & CH_REN) { + REG(ch) &= ~CH_TEN; + ch_oint_off(ch); + ch_igo(ch); + } else { + REG(ch) &= ~(CH_REN|CH_TEN); + ch_iint_off(ch); + ch_oint_off(ch); + } + + if (DVDEBUG(ch) > 4) + fprintf(DVDBF(ch),"[CH11 new CSR contents %#lo]\r\n", (long)REG(ch)); + + return; + default: + panic("ch11_write: Unknown address %lo", (long)addr); + } +} + +/* Generate CH11 output interrupt */ + +static void +ch_oint(register struct ch11 *ch) +{ + if (REG(ch) & CH_TEN) { + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CH11: output int]\r\n"); + ch->ch_opireq = TRUE; + (*ch->ch_dv.dv_pifun)(&ch->ch_dv, /* Put up interrupt */ + (int)ch->ch_dv.dv_brlev); + } +} + +/* Generate CH11 input interrupt */ + +static void +ch_iint(register struct ch11 *ch) +{ + if (REG(ch) & CH_REN) { + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CH11: input int]\r\n"); + ch->ch_ipireq = TRUE; + (*ch->ch_dv.dv_pifun)(&ch->ch_dv, /* Put up interrupt */ + (int)ch->ch_dv.dv_brlev); + } +} + +/* Activate input side - allow CHUDP input to be received and processed. +*/ +static void +ch_igo(register struct ch11 *ch) +{ + ch->ch_inactf = TRUE; /* OK to start reading input! */ + + if (chudp_incheck(ch)) { /* Do initial check for input */ + chudp_inxfer(ch); /* Have input! Go snarf it! */ + ch_idone(ch); /* Finish up CH input done */ + } +} + +/* CH input done - called to finish up CHUDP input +*/ +static void +ch_idone(register struct ch11 *ch) +{ + ch->ch_inactf = FALSE; /* don't read another just yet */ + + ch_iint(ch); /* Send input interrupt! */ +} + +static int +ch_outcheck(register struct ch11 *ch) +{ + /* check if there's some output to send */ + return ch->ch_outactf && (((ch->ch_optr - ch->ch_sbuf) - DPCHUDP_DATAOFFSET) > 0); +} + +/* Activate output side - send a message to the CHUDP. +** If can't do it because an outbound message is already in progress, +** complain and cause an error. +*/ +static void +ch_ogo(register struct ch11 *ch) +{ + if (ch_outcheck(ch)) { + if (!chudp_outxfer(ch)) { + /* Couldn't output, so abort and done immediately */ + REG(ch) |= (CH_TAB|CH_TDN); /* #### */ + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CH11 out err - overrun]\r\n"); + } + ch_oint(ch); + return; + /* DPCHUDP will call ch_evhsdon() when ready for more output. */ + } else { + ch_oint(ch); /* ITS seems to need this? */ + return; + } +} + +/* CH output done - called to finish up CHUDP output +*/ +static void +ch_odone(register struct ch11 *ch) +{ + REG(ch) |= CH_TDN; /* Note it's done */ + ch->ch_outactf = TRUE; /* OK to send another */ + ch_oint(ch); /* Send output interrupt! */ +} + +/* VIRTUAL CHUDP ROUTINES +*/ + +/* Utility routine */ + +static void +showpkt(FILE *f, char *id, unsigned char *buf, int cnt) +{ + char linbuf[200]; + register int i; + int once = 0; + register char *cp; + + while (cnt > 0) { + cp = linbuf; + if (once++) *cp++ = '\t'; + else sprintf(cp, "%6s: ", id), cp += 8; + + for (i = 16; --i >= 0;) { + sprintf(cp, " %3o", *buf++); + cp += 4; + if (--cnt <= 0) break; + } + *cp = 0; + fprintf(f, "%s\r\n", linbuf); + } +} + + +static void ch_evhrwak(struct device *d, struct dvevent_s *evp); +static void ch_evhsdon(struct device *d, struct dvevent_s *evp); + +/* static */ +int +chudp_init(register struct ch11 *ch, FILE *of) +{ + register struct dpchudp_s *dpc; + struct dvevent_s ev; + size_t junk; + + ch->ch_dpstate = FALSE; + if (!dp_init(&ch->ch_dp, sizeof(struct dpchudp_s), + DP_XT_MSIG, SIGUSR1, (size_t)CHUDPBUFSIZ, /* in */ + DP_XT_MSIG, SIGUSR1, (size_t)CHUDPBUFSIZ)) { /* out */ + if (of) fprintf(of, "CHUDP subproc init failed!\n"); + return FALSE; + } + ch->ch_sbuf = dp_xsbuff(&(ch->ch_dp.dp_adr->dpc_todp), &junk); + ch->ch_rbuf = dp_xrbuff(&(ch->ch_dp.dp_adr->dpc_frdp), &junk); + + ch->ch_dv.dv_dpp = &(ch->ch_dp); /* Tell CPU where our DP struct is */ + + /* Set up DPCHUDP-specific part of shared DP memory */ + dpc = (struct dpchudp_s *) ch->ch_dp.dp_adr; + dpc->dpchudp_dpc.dpc_debug = ch->ch_dpdbg; /* Init DP debug flag */ + if (cpu.mm_locked) /* Lock DP mem if CPU is */ + dpc->dpchudp_dpc.dpc_flags |= DPCF_MEMLOCK; + + dpc->dpchudp_ver = DPCHUDP_VERSION; + dpc->dpchudp_attrs = 0; + + dpc->dpchudp_backlog = ch->ch_backlog; /* Pass on backlog value */ + dpc->dpchudp_dedic = ch->ch_dedic; /* Pass on dedicated flag */ + + if (ch->ch_ifnam) /* Pass on interface name if any */ + strncpy(dpc->dpchudp_ifnam, ch->ch_ifnam, sizeof(dpc->dpchudp_ifnam)-1); + else + dpc->dpchudp_ifnam[0] = '\0'; /* No specific interface */ + + dpc->dpchudp_myaddr = ch->ch_myaddr; /* Set our Chaos address */ + dpc->dpchudp_port = ch->ch_chudp_port; + + /* copy chip table */ + for (junk = 0; junk < ch->ch_chip_tlen; junk++) { + memset(&dpc->dpchudp_chip_tbl[junk], 0, sizeof(struct dpchudp_chip)); + dpc->dpchudp_chip_tbl[junk].dpchudp_chip_chaddr = ch->ch_chip_tbl[junk].ch_chip_chaddr; + dpc->dpchudp_chip_tbl[junk].dpchudp_chip_ipport = ch->ch_chip_tbl[junk].ch_chip_ipport; + memcpy(&dpc->dpchudp_chip_tbl[junk].dpchudp_chip_ipaddr, + &ch->ch_chip_tbl[junk].ch_chip_ipaddr, sizeof(struct in_addr)); + } + dpc->dpchudp_chip_tlen = ch->ch_chip_tlen; + + /* Register ourselves with main KLH10 loop for DP events */ + + ev.dvev_type = DVEV_DPSIG; /* Event = Device Proc signal */ + ev.dvev_arg.eva_int = SIGUSR1; + ev.dvev_arg2.eva_ip = &(ch->ch_dp.dp_adr->dpc_todp.dpx_donflg); + if (!(*ch->ch_dv.dv_evreg)((struct device *)ch, ch_evhsdon, &ev)) { + if (of) fprintf(of, "CHUDP event reg failed!\n"); + return FALSE; + } + + ev.dvev_type = DVEV_DPSIG; /* Event = Device Proc signal */ + ev.dvev_arg.eva_int = SIGUSR1; + ev.dvev_arg2.eva_ip = &(ch->ch_dp.dp_adr->dpc_frdp.dpx_wakflg); + if (!(*ch->ch_dv.dv_evreg)((struct device *)ch, ch_evhrwak, &ev)) { + if (of) fprintf(of, "CHUDP event reg failed!\n"); + return FALSE; + } + return TRUE; +} + +static int +chudp_start(register struct ch11 *ch) +{ + register int res; + + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[chudp_start: starting DP \"%s\"...", + ch->ch_dpname); + + /* HORRIBLE UGLY HACK: for AXP OSF/1 and perhaps other systems, + ** the virtual-runtime timer of setitimer() remains in effect even + ** for the child process of a fork()! To avoid this, we must + ** temporarily turn the timer off, then resume it after the fork + ** is safely out of the way. + ** + ** Otherise, the timer would go off and the unexpected signal would + ** chop down the DP subproc without any warning! + ** + ** Later this should be done in DPSUP.C itself, when I can figure a + ** good way to tell whether the code is part of the KLH10 or a DP + ** subproc. + */ + clk_suspend(); /* Clear internal clock if one */ + res = dp_start(&ch->ch_dp, ch->ch_dpname); + clk_resume(); /* Resume internal clock if one */ + + if (!res) { + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), " failed!]\r\n"); + else + fprintf(DVDBF(ch), "[chudp_start: Start of DP \"%s\" failed!]\r\n", + ch->ch_dpname); + return FALSE; + } + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), " started!]\r\n"); + + REG(ch) |= CH_REN; + ch_iint(ch); /* Kick start */ + + return TRUE; +} + +/* CHUDP_STOP - Stops CHUDP and drops Host Ready by killing CHUDP subproc, +** but allow restarting. +*/ +static void +chudp_stop(register struct ch11 *ch) +{ + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CHUDP: stopping..."); + + dp_stop(&ch->ch_dp, 1); /* Say to kill and wait 1 sec for synch */ + + ch->ch_dpstate = FALSE; /* No longer there and ready */ + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), " stopped]\r\n"); +} + + +/* CHUDP_KILL - Kill CHUDP process permanently, no restart. +*/ +static void +chudp_kill(register struct ch11 *ch) +{ + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CHUDP kill]\r\n"); + + ch->ch_dpstate = FALSE; + (*ch->ch_dv.dv_evreg)( /* Flush all event handlers for device */ + (struct device *)ch, + NULLPROC, + (struct dvevent_s *)NULL); + dp_term(&(ch->ch_dp), 0); /* Flush all subproc overhead */ + ch->ch_sbuf = NULL; /* Clear pointers no longer meaningful */ + ch->ch_rbuf = NULL; +} + + +/* CH_EVHRWAK - Invoked by INSBRK event handling when +** signal detected from DP saying "wake up"; the DP is sending +** us an input packet. +*/ +static void +ch_evhrwak(struct device *d, struct dvevent_s *evp) +{ + register struct ch11 *ch = (struct ch11 *)d; + + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CH11 input wakeup: %d]", + (int)dp_xrtest(dp_dpxfr(&ch->ch_dp))); + + /* Always check CHUDP input in order to process any non-data messages + ** regardless of whether CH is actively reading, + ** then invoke general CH check to do data transfer if OK. + */ + if (chudp_incheck(ch)) { + if (!ch->ch_inactf) { + ch->ch_lost++; /* received, but busy */ + dp_xrdone(dp_dpxfr(&ch->ch_dp)); /* ack to DP! */ + } else { + chudp_inxfer(ch); /* Have input! Go snarf it! */ + ch_idone(ch); /* Finish up CH input done */ + } + } +} + +static void +ch_evhsdon(struct device *d, struct dvevent_s *evp) +{ + register struct ch11 *ch = (struct ch11 *)d; + register struct dpx_s *dpx = dp_dpxto(&ch->ch_dp); + + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[ch_evhsdon: %d]", (int)dp_xstest(dpx)); + + if (dp_xstest(dpx)) { /* Verify message is done */ + ch_odone(ch); /* Say CH output done */ + } +} + + +/* Start CHUDP output. +*/ +static int +chudp_outxfer(register struct ch11 *ch) +{ + register int cnt; + register struct dpx_s *dpx = dp_dpxto(&ch->ch_dp); + + /* Make sure we can output message and fail if not */ + if (!dp_xstest(dpx)) { + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CHUDP: DP out blocked]\r\n"); + return 0; + } + + ch->ch_outactf = FALSE; /* Don't send another just yet */ + + /* Output xfer requested! */ + register struct dpchudp_s *dpc = (struct dpchudp_s *) ch->ch_dp.dp_adr; +#if 1 /* #### Debug */ + int chlen = ((ch->ch_sbuf[DPCHUDP_DATAOFFSET+2] & 0xf) << 4) | ch->ch_sbuf[DPCHUDP_DATAOFFSET+3]; +#endif + cnt = (ch->ch_optr - ch->ch_sbuf) - DPCHUDP_DATAOFFSET; +#if 1 /* #### Debug */ + if ((cnt % 2) == 1) + fprintf(stderr,"\r\n[CH11 sending odd number of bytes (%d. data len %d.)]\r\n", + cnt, chlen); + if ((chlen + CHAOS_HEADERSIZE + CHAOS_HW_TRAILERSIZE) > cnt) + fprintf(stderr,"\r\n[CH11 sending less than packet: sending %d, expected %d (Chaos data len %d)]\r\n", + cnt, CHAOS_HEADERSIZE + chlen + CHAOS_HW_TRAILERSIZE, chlen); +#endif + if (DVDEBUG(ch) & DVDBF_DATSHO) /* Show data? */ + showpkt(DVDBF(ch), "PKTOUT", ch->ch_sbuf + DPCHUDP_DATAOFFSET, cnt); + + dpc->dpchudp_outoff = DPCHUDP_DATAOFFSET; + + REG(ch) &= ~(CH_TAB|CH_TDN); /* Not done yet, not aborted */ + + dp_xsend(dpx, DPCHUDP_SPKT, (size_t)cnt + DPCHUDP_DATAOFFSET); + + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CHUDP: Out %d]\r\n", cnt); + + return 1; +} + +static int +chudp_incheck(register struct ch11 *ch) +{ + register struct dpx_s *dpx = dp_dpxfr(&ch->ch_dp); + + if (dp_xrtest(dpx)) { /* Verify there's a message for us */ + switch (dp_xrcmd(dpx)) { + case DPCHUDP_INIT: + ch->ch_dpstate = TRUE; + dp_xrdone(dpx); /* ACK it */ + return 0; /* No actual input */ + + case DPCHUDP_RPKT: /* Input packet ready! */ + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CHUDP: inbuf %ld]\r\n", + (long) dp_xrcnt(dpx)); + return 1; + + default: + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CHUDP: R %d flushed]", dp_xrcmd(dpx)); + dp_xrdone(dpx); /* just ACK it */ + return 0; + } + } + + return 0; +} + + +/* CHUDP_INXFER - CHUDP Input. +** For time being, don't worry about partial transfers (sigh), +** which might have left part of a previous message still lying +** around waiting for the next read request. +*/ +static void +chudp_inxfer(register struct ch11 *ch) +{ + register int err, cnt, cks; + register struct dpx_s *dpx = dp_dpxfr(&ch->ch_dp); + register struct dpchudp_s *dpc = (struct dpchudp_s *) ch->ch_dp.dp_adr; + register unsigned char *pp; + + /* Assume this is ONLY called after verification by chudp_incheck that + ** an input message is actually ready. + */ + cnt = dp_xrcnt(dpx); + + /* Adjust for possible offset */ +/* cnt -= dpc->dpchudp_inoff; */ + pp = ch->ch_rbuf + dpc->dpchudp_inoff; + + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CHUDP: In %d]\r\n", cnt); + if (DVDEBUG(ch) & DVDBF_DATSHO) /* Show data? */ + showpkt(DVDBF(ch), "PKTIN ", pp, cnt); + + /* Message already read in! */ + if (cnt & 01) { /* If msg len not multiple of 2, pad out */ + pp[cnt+1] = '\0'; + } + + ch->ch_rcnt = cnt; + ch->ch_iptr = pp; + + /* check hw trailer: dest, checksum */ + if ((((pp[cnt-6]<<8) | pp[cnt-5]) != ch->ch_myaddr)) { + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CHUDP: not for my address: destination %o]\r\n", + ((pp[cnt-6]<<8) | pp[cnt-5])); + } else { + cks = ch_checksum(pp,cnt); + if (cks != 0) { + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CHUDP: bad checksum 0x%x]\r\n", cks); +#if 1 /* 0 for testing */ + REG(ch) |= CH_ERR; +#endif + } + REG(ch) |= CH_RDN; /* Note it's done! */ + } + ch->ch_inactf = TRUE; /* OK to read another */ + dp_xrdone(dpx); /* Done, can now ACK */ } #endif /* KLH10_DEV_CH11 */ diff --git a/src/dvch11.h b/src/dvch11.h index 7185543..5e671f8 100644 --- a/src/dvch11.h +++ b/src/dvch11.h @@ -107,4 +107,9 @@ extern struct device *dvch11_init(FILE *, char *); #define UB_CH11END (UB_CHXMT+2) /* First addr not used by CH11 regs */ + +#define CHAOS_HW_TRAILERSIZE (6) /* "hw" trailer bytes */ +#define CHAOS_HEADERSIZE (4*4) /* chaos packet header size */ +#define CHAOS_MAXDATA 488 /* Max data bytes per packet #### incl header? */ + #endif /* ifndef DVCH11_INCLUDED */ diff --git a/src/dvch11.txt b/src/dvch11.txt new file mode 100644 index 0000000..e893deb --- /dev/null +++ b/src/dvch11.txt @@ -0,0 +1,111 @@ +================================ + CHAOS is restored! + or + CH11 support for ITS in KLH10 +================================ + +The ch11 device in KLH10 for ITS, which was originally just a dummy +inteface to keep ITS running, is now a functional Unibus Chaosnet +device, which tunnels Chaosnet packets over UDP. It does not support +SPY (promiscuous) mode, LUP (loopback), or broadcast, but ITS doesn't +seem to use/need these. + +Latest update: 2005-04-23 + +Why Chaos when we have the IMP? +* Some ITS services use Chaos but not IP, e.g. the DIR device. +* The IMP needs to run as root on most Unix flavors, but the UDP + tunnel can go over unprivileged ports. (This can also make it avoid + firewalls.) +* It's historically interesting, and makes KLH10 more complete. +* It's FUN! + +Like other KLH10 network devices, ch11 works together with a Device +Process which communicates with the device and reads/writes network +data over UDP. A 4-byte protocol header is added, with only one +defined function code yet (sending a Chaos packet). The "original" +Chaos trailer (destination, source, and checksum) is also added. + +The protocol, CHUDP, by default uses UDP port 42042, but this can be +configured in case, e.g., that port doesn't pass through your +firewall. + +The code is a (very) stripped-down version of dvlhdh.c and dpimp.c, +and includes minimal patches to the makefile and to kn10dev.c. +It can be downloaded via http://victor.se/bjorn/its. I would +appreciate if you send me an email if you do, e.g. so I have an idea +about who to notify about updates. + +To configure the CH11 device, as a minimum, use + devdef chaos ub3 ch11 addr=764140 br=6 vec=270 myaddr=3132 +where the new parameter is "myaddr", which is the octal Chaosnet +address of the system. ITS seems to think the "br" param should be 6, +not 5 as used in the old dummy device definition (see CHXCHN in ITS). + +It uses the generic Unibus parameters "addr", "br", "vec" etc, plus +additional parameters: + chudpport=50043 + changes the UDP port used + chip=3131/10.0.1.42:50011 + defines a Chaos/IP mapping for Chaos host 3131, which should + be contacted at IP 10.0.1.42 at UDP port 50011. You can use + hostnames in place of IP addresses, which will use the first + address returned by gethostbyname(), if it is an IPv4 address. + The port suffix is optional and defaults to 42042. You can + define up to 10 such Chaos/IP mappings (without changing the + constants in the code). + +If a CHUDP packet is received from an unknown host, a Chaos/IP mapping +is dynamically added to the table, so return traffic can find its +way. This is useful e.g. if you accept connections from other ITSes +without static IP addresses. (There is currently no way to avoid +potentially filling your table this way.) + +To make use of the interface, you need to recompile ITS after defining +CHAOSP, MYCHAD, NINDX, and CH11P in SYSTEM;CONFIG (and following the +instructions in distrib.its, of course). See also BEWARE note below. +Don't forget to compile SYSENG;@CHAOS and install as DEVICE;ATSIGN +CHAOS. Don't forget to keep names of your friends' ITSes in +SYSEN2;MLDEV, SYSENG;@DEV, etc. More notes on the web page. + +I would appreciate very much to get feedback and testing, especially +from people who actually *know* how the original interface was +supposed to act. I've discovered many things by reading SYSTEM;CHAOS +and by testing locally, but I can't say I know exactly how it *should* +work... + +*BEWARE*: +If you have both CHAOSP and IMPP, you run into a bug which results in +all non-local IP packets being routed via Chaosnet... (unfortunately +noone is listenting at Chaos address 3040). +The bug is caused by the IPMASK macro defined and used in +SYSTEM;CONFIG, which results in the netmask NM%CHA being zero, which +matches anything. The easy fix is to change the line +IFN CHAOSP,DEFOPT NM%CHA== ; Set default netmask for it +to +IFN CHAOSP,DEFOPT NM%CHA== ; Set default netmask for it +but the real fix would be to the IPMASK macro. + +Ideas for future work: +- Better routing support. +- Use DNS to find Chaos addresses - this seems more awkward than helpful. +- Implement loopback (not LUP, but Chaos pkts to self). Currently + they are in practice dropped. I'm not sure ITS handles this very + well anyway. +- Chaos broadcast can easily be implemented by UDP broadcast, but it's + probably not very useful. Another implementation would be to send + (individually) to all known hosts (in the CHIP table) on the same + subnet, but this is probably not very useful either. (Does any ITS + program do broadcast?) +- More CHUDP protocol functions? +- Security? +- Chaosnet library for Linux/Unix??? + +Cheers, +-- +Bjorn Victor Bjorn.Victor@it.uu.se +Dept. of Information Technology tel: +46 18 471 3169 +Uppsala University mobile: +46 70 425 0239 +Box 337 fax: +46 18 511925 +S-751 05 Uppsala, SWEDEN web: http://user.it.uu.se/~victor + diff --git a/src/kn10dev.c b/src/kn10dev.c index 7021c3e..2b087eb 100644 --- a/src/kn10dev.c +++ b/src/kn10dev.c @@ -293,7 +293,7 @@ extern struct device *dvlhdh_create(FILE *f, char *s); extern struct device *dvdz11_init(FILE *f, char *s); #endif #if KLH10_DEV_CH11 -extern struct device *dvch11_init(FILE *f, char *s); +extern struct device *dvch11_create(FILE *f, char *s); #endif /* Table binding static device driver modules with their names @@ -329,7 +329,7 @@ static struct dvdrv_s dvdrvtab[KLH10_DEVMAX+1] = { { "dz11", dvdz11_init, NULL, NULL, "DZ11 dummy (Unibus)" }, #endif #if KLH10_DEV_CH11 - { "ch11", dvch11_init, NULL, NULL, "Chaosnet dummy (Unibus)" }, + { "ch11", dvch11_create, NULL, NULL, "Chaosnet interface (Unibus)" }, #endif { NULL, NULL, NULL } };