diff --git a/.gitignore b/.gitignore index a8a6bf2..1d68a27 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,7 @@ new src/tags src/config.h.in tmp*/* +#*# +*/#*# +*.~* +*/*.~* diff --git a/doc/install.txt b/doc/install.txt index 0c85f42..7e08b2c 100644 --- a/doc/install.txt +++ b/doc/install.txt @@ -1330,9 +1330,10 @@ DZ11 (TTY MUX): (KS only) that this device will respond to IO instructions addressed to it, but no TTY I/O actually happens. -CH11 (CHAOSNET): (KS only) +CH11 (CHAOSNET): (KS only, for ITS only) - This is another dummy device, only for ITS. + This is a Chaosnet device which can use Chaosnet on Ethernet + or tunnelling Chaosnet in UDP. See src/dvch11.txt. As for the DZ11, the generic Unibus parameters must be provided. HOST (Native Host): (KL & KS) diff --git a/src/Makefile.mk b/src/Makefile.mk index e13aab1..862600e 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 dpchudp +DPROCS_KSITS = dprpxx dptm03 dpimp dpchaos # Base utility programs, independent of KN10 @@ -290,8 +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 ${KLH10_HOME}/dpchaos ]; then \ + mv ${KLH10_HOME}/dpchaos ${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 @@ -299,7 +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 dpchaos ]; then cp -p dpchaos ${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 @@ -551,11 +551,11 @@ kl0i-rtmopt: # --------- CHUDP subprocess (ITS KS only; counterpart for dvch11) # -dpchudp.o: $(SRC)/dpchudp.c $(SRC)/dpchudp.h $(SRC)/dpsup.h - $(BUILDMOD) $(SRC)/dpchudp.c +dpchaos.o: $(SRC)/dpchaos.c $(SRC)/dpchaos.h $(SRC)/dpsup.h + $(BUILDMOD) $(SRC)/dpchaos.c -dpchudp: dpchudp.o dpsup.o - $(LINKER) $(LDFLAGS) $(LDOUTF) dpchudp dpchudp.o dpsup.o $(LIBS) +dpchaos: dpchaos.o dpsup.o + $(LINKER) $(LDFLAGS) $(LDOUTF) dpchaos dpchaos.o dpsup.o $(LIBS) $(NETLIBS) #################################################################### @@ -699,7 +699,7 @@ dpsup.o: $(SRC)/dpsup.c $(SRC)/dpsup.h \ $(SRC)/osdsup.h $(SRC)/word10.h $(BLDSRC)/config.h $(BUILDMOD) $(SRC)/dpsup.c -dvch11.o: $(SRC)/dvch11.c $(SRC)/dvch11.h $(BLDSRC)/config.h +dvch11.o: $(SRC)/dvch11.c $(SRC)/dvch11.h $(SRC)/dpchaos.h $(BLDSRC)/config.h $(BUILDMOD) $(SRC)/dvch11.c dvcty.o: $(SRC)/dvcty.c $(SRC)/dvcty.h $(BLDSRC)/config.h diff --git a/src/dpchaos.c b/src/dpchaos.c new file mode 100644 index 0000000..020826b --- /dev/null +++ b/src/dpchaos.c @@ -0,0 +1,1578 @@ +/* DPCHAOS.C - Chaos link implementation (over Ethernet or using UDP) +*/ +/* Copyright © 2005, 2018 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 possibly 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 Chaosnet link-layer implementation. + +It can do that in two ways: + +Firstly, by implementing Chaosnet over Ethernet (protocol nr 0x0804) and handling +ARP for that protocol (cf RFC 826). This uses one of the packet filtering +implementations (currently only pcap) provided by osdnet. (Only Ethernet II +headers are supported, not 802.3.) +No routing is handled, that's done by ITS - this code uses the +Chaosnet trailer provided by ITS to decide where to send pkts. +(On Ethernet, no Chaosnet trailer is included.) + +Secondly, it can do it by using 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 packet is +sent in LSB first order, rather than network (MSB first) order. + +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 CH11 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 Ethernet or 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. +*/ +// TODO (some day): +// Make sure short construction by | is from known network order, otherwise use casting. +// Try to check ARP best practices (e.g retrans intervals?) + +#include +#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 0 /* Need more than IP stuff */ +#include "osdnet.h" /* OSD net defs, shared with DPNI20 and DPIMP */ + +#include /* For setpriority() */ +#include /* For mlockall() */ + +#include "dpchaos.h" /* DPCHAOS 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 chudpsock; /* UDP socket */ +struct osnpf npf; +struct pfdata pfdata; /* Packet-Filter state */ + +int myaddr; /* My chaos address */ +struct in_addr ihost_ip; /* My host IP addr, net order */ + + +#define ARP_PKTSIZ (sizeof(struct ether_header) + sizeof(struct ether_arp)) +static u_char eth_brd[ETHER_ADDR_LEN] = {255,255,255,255,255,255}; + +/* Debug flag reference. Use DBGFLG within functions that have "dpchaos"; + * 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 (dpchaos->dpchaos_dpc.dpc_debug) +#define DP_DBGFLG (((struct dpchaos_s *)dp.dp_adr)->dpchaos_dpc.dpc_debug) + +/* Local predeclarations */ + +void chaostohost(struct dpchaos_s *); +void hosttochaos(struct dpchaos_s *); + +void net_init(struct dpchaos_s *); +void dumppkt(unsigned char *, int); +void dumppkt_raw(unsigned char *ucp, int cnt); + +void ihl_hhsend(register struct dpchaos_s *dpchaos, int cnt, register unsigned char *pp); +void ip_write(struct in_addr *ipa, in_port_t ipport, unsigned char *buf, int len, struct dpchaos_s *dpchaos); +int hi_iproute(struct in_addr *ipa, in_port_t *ipport, unsigned char *lp, int cnt, struct dpchaos_s *dpchaos); + +char *ch_adrsprint(char *cp, unsigned char *ca); +void send_chaos_arp_reply(u_short dest_chaddr, u_char *dest_eth); +void send_chaos_arp_request(u_short chaddr); +void send_chaos_packet(unsigned char *ea, unsigned char *buf, int cnt); +u_char *find_arp_entry(u_short daddr); +void print_arp_table(void); + + +/* Error and diagnostic output */ + +static const char progname_i[] = "dpchaos"; +static const char progname_r[] = "dpchaos-R"; +static const char progname_w[] = "dpchaos-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; + +void +htons_buf(u_short *ibuf, u_short *obuf, int len) +{ + int i; + for (i = 0; i < len; i += 2) + *obuf++ = htons(*ibuf++); +} +void +ntohs_buf(u_short *ibuf, u_short *obuf, int len) +{ + int i; + for (i = 0; i < len; i += 2) + *obuf++ = ntohs(*ibuf++); +} + +int +main(int argc, char **argv) +{ + register struct dpchaos_s *dpchaos; /* 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!"); + } + dpchaos = (struct dpchaos_s *)dp.dp_adr; /* Make for easier refs */ + + /* Verify that the structure version is compatible */ + if (dpchaos->dpchaos_ver != DPCHAOS_VERSION) { + efatal(1, "Wrong version of DPCHAOS: ch11=%0lx dpchaos=%0lx", + (long)dpchaos->dpchaos_ver, (long)DPCHAOS_VERSION); + } + + /* Now can access DP args! + From here on we can use DBGFLG, which is actually a shared + memory reference that dpchaos 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 (!(dpchaos->dpchaos_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 = dpchaos->dpchaos_myaddr) == 0) /* My chaos address */ + efatal(1, "no CHAOS address specified"); + + /* Initialize various network info */ + net_init(dpchaos); + + /* Make this a status (rather than debug) printout? */ + if (swstatus) { + int i; + char ipbuf[OSN_IPSTRSIZ]; + + dbprintln("ifc \"%s\" => chaos %lo", + dpchaos->dpchaos_ifnam, (long)myaddr); + for (i = 0; i < dpchaos->dpchaos_chip_tlen; i++) + dbprintln(" chaos %6o => ip %s:%d", + dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_chaddr, + ip_adrsprint(ipbuf, + (unsigned char *)&dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_ipaddr), + dpchaos->dpchaos_chip_tbl[i].dpchaos_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 */ + chaostohost(dpchaos); /* Child process handles input */ + } + progname = progname_w; /* Reset progname to indicate identity */ + + hosttochaos(dpchaos); /* Parent process handles output */ + + return 1; /* Never returns, but placate compiler */ +} + +/* NET_INIT - Initialize net-related variables, +** given network interface we'll use. +*/ +struct ifent *osn_iflookup(char *ifnam); + +void net_init_pf(struct dpchaos_s *dpchaos) +{ + // based on dpni20.c + /* Set up packet filter. This also returns in "ihost_ea" + the ethernet address for the selected interface. + */ + + npf.osnpf_ifnam = dpchaos->dpchaos_ifnam; + npf.osnpf_ifmeth = dpchaos->dpchaos_ifmeth; + npf.osnpf_dedic = dpchaos->dpchaos_dedic; + //npf.osnpf_rdtmo = dpchaos->dpchaos_rdtmo; + npf.osnpf_backlog = dpchaos->dpchaos_backlog; + //npf.osnpf_ip.ia_addr = ehost_ip; + //npf.osnpf_tun.ia_addr = tun_ip; + + + // Tell dvch11 at what offset in the shared buffer our data is. + dpchaos->dpchaos_inoff = sizeof(struct ether_header); + dpchaos->dpchaos_outoff = dpchaos->dpchaos_inoff; // DPCHUDP_DATAOFFSET + + + (void)osn_iftab_init(); + + // finding the interface (and its ethernet address) is necessary for the more clever packet filter, + // which isn't enabled for now, so... + if (npf.osnpf_ifnam == NULL || npf.osnpf_ifnam[0] == '\0') { + // requires ip4 address, but better than nothing? + struct ifent *ife = osn_ipdefault(); + if (ife && (strlen(ife->ife_name) < sizeof(dpchaos->dpchaos_ifnam))) { + dbprintln("Using default interface '%s'", ife->ife_name); + strcpy(dpchaos->dpchaos_ifnam, ife->ife_name); + } else + if (DBGFLG) dbprintln("Can't find default interface."); + } else + if (DBGFLG) dbprintln("Interface '%s' specified.", npf.osnpf_ifnam); + + /* Ether addr is both a potential arg and a returned value; + the packetfilter open may use and/or change it. + */ + if ((memcmp(dpchaos->dpchaos_eth,"\0\0\0\0\0\0", ETHER_ADDR_LEN) == 0) && npf.osnpf_ifnam[0] != '\0') { + // we need the ea for the pf, so find it already here + /* Now get our interface's ethernet address. */ + if (osn_ifealookup(npf.osnpf_ifnam, (unsigned char *) &npf.osnpf_ea) == 0) { + struct ifent *ife = (struct ifent *)osn_iflookup(npf.osnpf_ifnam); + dbprintln("Can't find EA for \"%s\"", npf.osnpf_ifnam); + if (ife) { + dbprintln("Found interface '%s', ea %s", ife->ife_name, + (ife->ife_ea != NULL && memcmp(ife->ife_ea,"\0\0\0\0\0\0", ETHER_ADDR_LEN) == 0) ? "found" : "not found"); + } else + dbprintln("Can't find interface '%s'!", npf.osnpf_ifnam); + } else { + if (DP_DBGFLG) dbprintln("Found EA for \"%s\"", npf.osnpf_ifnam); + } + } else + ea_set(&npf.osnpf_ea, dpchaos->dpchaos_eth); /* Set requested ea if any */ + if (DP_DBGFLG) { + char eastr[OSN_EASTRSIZ]; + + dbprintln("EN addr for \"%s\" = %s", + npf.osnpf_ifnam, eth_adrsprint(eastr, (unsigned char *)&npf.osnpf_ea)); + } + ea_set(dpchaos->dpchaos_eth, (char *)&npf.osnpf_ea); /* Copy ether addr (so pfbuild can use it)*/ + // pfdata, osnpf, pfarg; pfbuild(pfarg, ehost_ip) + osn_pfinit(&pfdata, &npf, (void *)dpchaos); /* Will abort if fails */ + ea_set(dpchaos->dpchaos_eth, &npf.osnpf_ea); /* Copy actual ea */ + + /* Now set any return info values in shared struct. + */ +#if 0 // already done above + ea_set(dpchaos->dpchaos_eth, (char *)&ihost_ea); /* Copy ether addr */ +#endif +} + +void net_init_chudp(struct dpchaos_s *dpchaos) +{ + struct sockaddr_in mysin; + + // Tell dvch11 at what offset in the shared buffer our data is. + dpchaos->dpchaos_inoff = DPCHAOS_CHUDP_DATAOFFSET; + dpchaos->dpchaos_outoff = dpchaos->dpchaos_inoff; // DPCHAOS_CHUDP_DATAOFFSET + + if ((chudpsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + efatal(1,"socket failed: %s", dp_strerror(errno)); + + mysin.sin_family = AF_INET; + mysin.sin_port = htons(dpchaos->dpchaos_port); + mysin.sin_addr.s_addr = INADDR_ANY; +#if 0 + if (dpchaos->dpchaos_ifnam[0] && ihost_ip.s_addr != 0) + mysin.sin_addr.s_addr = ihost_ip.s_addr; +#endif + + if (bind(chudpsock, (struct sockaddr *)&mysin, sizeof(mysin)) < 0) + efatal(1,"bind failed: %s", dp_strerror(errno)); +} + +void +net_init(register struct dpchaos_s *dpchaos) +{ + struct ifreq ifr; + + /* Ensure network device name, if specified, isn't too long */ + if (dpchaos->dpchaos_ifnam[0] && (strlen(dpchaos->dpchaos_ifnam) + >= sizeof(ifr.ifr_name))) { + esfatal(0, "interface name \"%s\" too long - max %d", + dpchaos->dpchaos_ifnam, (int)sizeof(ifr.ifr_name)); + } + + /* Set up appropriate net fd. + */ + if (dpchaos->dpchaos_ifmeth_chudp) { + // CHUDP case + net_init_chudp(dpchaos); + } else { + // Ethernet case + net_init_pf(dpchaos); + } +} + +/* CHAOSTOHOST - Child-process main loop for pumping packets from CHAOS to HOST. +** Reads packets from net and feeds CHAOS 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 describe_eth_pkt_hdr(unsigned char *buffp) +{ + char ethstr1[OSN_EASTRSIZ],ethstr2[OSN_EASTRSIZ]; + dbprintln("Ether header:\r\n" + " dest %s\r\n" + " src %s\r\n" + " prot 0x%04x", + eth_adrsprint(ethstr1, buffp), eth_adrsprint(ethstr2, &buffp[ETHER_ADDR_LEN]), + (buffp[ETHER_ADDR_LEN*2] << 8) | buffp[ETHER_ADDR_LEN*2+1]); +} + +void describe_arp_pkt(struct arphdr *arp, unsigned char *buffp) +{ + char ethstr[OSN_EASTRSIZ]; + char prostr[OSN_IPSTRSIZ]; + + dbprintln("ARP message, protocol 0x%04x (%s)", + ntohs(arp->ar_pro), (arp->ar_pro == htons(ETHERTYPE_IP) ? "IPv4" : + (arp->ar_pro == htons(ETHERTYPE_CHAOS) ? "Chaos" : "?"))); + dbprintln(" HW addr len %d\r\n Proto addr len %d\r\n ARP command %d (%s)", + arp->ar_hln, arp->ar_pln, ntohs(arp->ar_op), + arp->ar_op == htons(ARPOP_REQUEST) ? "Request" : + (arp->ar_op == htons(ARPOP_REPLY) ? "Reply" : + (arp->ar_op == htons(ARPOP_RREQUEST) ? "Reverse request" : + (arp->ar_op == htons(ARPOP_RREPLY) ? "Reverse reply" : "?")))); + dbprintln(" src: HW %s proto %s", eth_adrsprint(ethstr, &buffp[sizeof(struct arphdr)]), + (arp->ar_pro == htons(ETHERTYPE_IP) ? + ip_adrsprint(prostr, &buffp[sizeof(struct arphdr)+arp->ar_hln]) : + (arp->ar_pro == htons(ETHERTYPE_CHAOS) ? + ch_adrsprint(prostr, &buffp[sizeof(struct arphdr)+arp->ar_hln]) : + "?whatevs?"))); + dbprintln(" dst: HW %s proto %s", eth_adrsprint(ethstr, &buffp[sizeof(struct arphdr)+arp->ar_hln+arp->ar_pln]), + (arp->ar_pro == htons(ETHERTYPE_IP) ? + ip_adrsprint(prostr, &buffp[sizeof(struct arphdr)+arp->ar_hln+arp->ar_pln+arp->ar_hln]) : + (arp->ar_pro == htons(ETHERTYPE_CHAOS) ? + ch_adrsprint(prostr, &buffp[sizeof(struct arphdr)+arp->ar_hln+arp->ar_pln+arp->ar_hln]) : + "?whatevs?"))); +} + +void maybe_add_to_arp(struct dpchaos_s *dpchaos, u_short cha, u_char *eth) +{ + int i, found = 0; + for (i = 0; i < dpchaos->charp_len && i < CHARP_MAX; i++) + if (dpchaos->charp_list[i].charp_chaddr == cha) { + found = 1; + dpchaos->charp_list[i].charp_age = time(NULL); // update age + if (memcmp(&dpchaos->charp_list[i].charp_eaddr, eth, ETHER_ADDR_LEN) != 0) { + memcpy(&dpchaos->charp_list[i].charp_eaddr, eth, ETHER_ADDR_LEN); + if (DBGFLG & 4) { + dbprintln("ARP: Changed MAC addr for %#o", cha); + // print_arp_table(); + } + } else + if (DBGFLG & 4) { + dbprintln("ARP: Updated age for %#o", cha); + // print_arp_table(); + } + break; + } + /* It's not in the list already, is there room? */ + // @@@@ should reuse outdated entries + if (!found) { + if (dpchaos->charp_len < CHARP_MAX) { + if (DBGFLG & 4) dbprintln("ARP: Adding new entry for Chaos %#o", cha); + dpchaos->charp_list[dpchaos->charp_len].charp_chaddr = cha; + dpchaos->charp_list[dpchaos->charp_len].charp_age = time(NULL); + memcpy(&dpchaos->charp_list[dpchaos->charp_len].charp_eaddr, eth, ETHER_ADDR_LEN); + dpchaos->charp_len++; + if (DBGFLG & 4) print_arp_table(); + } else { + dbprintln("ARP: table full! Please increase size from %d and/or implement GC", CHARP_MAX); + } + } +} + +// handle an ARP pkt +void chaostohost_pf_arp(struct dpchaos_s *dpchaos, unsigned char *buffp, int cnt) +{ + struct arphdr *arp = (struct arphdr *)buffp; + if (DBGFLG & 4) describe_arp_pkt(arp, buffp); + + if (arp->ar_pro != htons(ETHERTYPE_CHAOS)) { + if (DBGFLG) + dbprintln("Unexpected ARP protocol %#x received", ntohs(arp->ar_pro)); + return; + } + + u_short schad = ntohs((buffp[sizeof(struct arphdr)+arp->ar_hln]<<8) | + buffp[sizeof(struct arphdr)+arp->ar_hln+1]); + u_char *sead = &buffp[sizeof(struct arphdr)]; + u_short dchad = ntohs((buffp[sizeof(struct arphdr)+arp->ar_hln+arp->ar_hln+arp->ar_pln]<<8) | + buffp[sizeof(struct arphdr)+arp->ar_hln+arp->ar_hln+arp->ar_pln+1]); + + // don't create a storm + if (memcmp(sead, eth_brd, ETHER_ADDR_LEN) == 0) + return; + + if (arp->ar_op == htons(ARPOP_REQUEST)) { + if (dchad == dpchaos->dpchaos_myaddr) { + if (DBGFLG & 4) dbprintln("ARP: Sending reply for %#o (me) to %#o", dchad, schad); + send_chaos_arp_reply(schad, sead); /* Yep. */ + } + //return; // no, maybe update table first, also for requests + } + /* Now see if we should add this to our Chaos ARP list */ + maybe_add_to_arp(dpchaos, schad, sead); +} + +void chaostohost_pf_ether(struct dpchaos_s *dpchaos, unsigned char *buffp, int cnt) +{ + + u_char *pp = buffp+sizeof(struct ether_header); + int pcnt = cnt - sizeof(struct ether_header); + if ((pcnt - CHAOS_HEADERSIZE) < 0) { + // not even a Chaosnet header was read + if (DBGFLG) + dbprintln("Less than a Chaos header read!"); + return; + } + // Chaos data length + int ccnt = ((pp[3] & 0xf) << 4) | pp[2]; /* un-swapped */ + + if (DBGFLG & 1) { + dbprintln("Read Chaos pkt len %d (incl EH), offset %d, %d (pkt len), %d (Chaos data len)", + cnt, dpchaos->dpchaos_inoff, pcnt, ccnt); + } + if (pcnt != (CHAOS_HEADERSIZE + ccnt + (ccnt%2) + CHAOS_HW_TRAILERSIZE)) { + // @@@@ I still don't know why this happens, but it does - pcap packets are often longer + if (DBGFLG & 0x2) { + dbprintln("Expected %d+%d+%d = %d bytes, got %d", + CHAOS_HEADERSIZE, ccnt+(ccnt%2), CHAOS_HW_TRAILERSIZE, + CHAOS_HEADERSIZE + ccnt+(ccnt%2) + CHAOS_HW_TRAILERSIZE, + pcnt); + dumppkt_raw(buffp, cnt); + } + + } + if (pcnt < (CHAOS_HEADERSIZE + ccnt)) { + dbprintln("Incomplete Chaosnet packet read: %d but expected at least %d+%d=%d (ignoring pkt)", + pcnt, CHAOS_HEADERSIZE, ccnt, CHAOS_HEADERSIZE+ccnt); + return; + } + + // ITS expects (and delivers) the Chaosnet hardware trailer (destination, source, checksum). + // On Ethernet, this is not needed since it is replaced by the + // Ethernet packet data (e.g. Ether addresses, CRC.) + // (LambdaDelta and real Symbolics hardware do not include the Chaosnet trailer + // when sending Chaosnet pkts on Ethernet.) + + // Even if ITS doesn't really use the contents of the trailer, it + // needs to be there, so why not fill it in as best as we can. + + // Clear last byte (and add to length) before adding trailer (cf CHSRC6 in ITS). + if ((ccnt % 2) == 1) { + if (DBGFLG & 1) + dbprintln("Odd Chaos data len %d, rounding up and zeroing extra byte", ccnt); + // @@@@ make sure this zeros the correct byte - this is OK on little-endian + buffp[sizeof(struct ether_header)+CHAOS_HEADERSIZE+ccnt] = '\0'; + } + if (DBGFLG & 1) + dbprintln("Clearing/re-creating hw trailer at index %d, resetting size to %d", + (sizeof(struct ether_header))+CHAOS_HEADERSIZE+ccnt+(ccnt%2), + (CHAOS_HEADERSIZE + ccnt+(ccnt%2) + CHAOS_HW_TRAILERSIZE) + sizeof(struct ether_header)); + memset(buffp+(sizeof(struct ether_header))+CHAOS_HEADERSIZE+ccnt+(ccnt%2), 0, CHAOS_HW_TRAILERSIZE); + // reset sizes + cnt = (CHAOS_HEADERSIZE + ccnt+(ccnt%2) + CHAOS_HW_TRAILERSIZE) + sizeof(struct ether_header); + pcnt = (CHAOS_HEADERSIZE + ccnt+(ccnt%2) + CHAOS_HW_TRAILERSIZE); + + // Construct a trailer. Could find the actual hw sender from the Ethernet sender + ARP table, + // but ITS doesn't care. + u_short *hddest = (u_short *)&pp[4]; + u_short *hdsrc = (u_short *)&pp[8]; + u_short *hdd = (u_short *)&pp[pcnt-CHAOS_HW_TRAILERSIZE]; *hdd = *hddest; + u_short *hds = (u_short *)&pp[pcnt-CHAOS_HW_TRAILERSIZE+2]; *hds = *hdsrc; + + u_short cks = ch_checksum(pp,pcnt-2); + u_short *hdc = (u_short *)&pp[pcnt-CHAOS_HW_TRAILERSIZE+4]; *hdc = htons(cks); + + // byte swap to net order + htons_buf((u_short *)pp, (u_short *)pp, pcnt); + + if (DBGFLG & 0x2) { + dumppkt(buffp,cnt); + } + // "assume we're already in shared buffer" (so buffp arg is not used) + ihl_hhsend(dpchaos, cnt, buffp); + + return; +} + +void chaostohost_pf(struct dpchaos_s *dpchaos, unsigned char *buffp) +{ + register int cnt; + // @@@@ check destination address (mine or broadcast) + // @@@@ in case not using pcap_compile, and interface is promisc + + /* OK, now do a blocking read on packetfilter input! */ + cnt = osn_pfread(&pfdata, buffp, DPCHAOS_MAXLEN); + + if (DBGFLG) + dbprintln("Read=%d", cnt); + + if (cnt == 0) + return; + + if (((struct ether_header *)buffp)->ether_type == htons(ETHERTYPE_ARP)) { + if (DBGFLG & 4) + dbprintln("Got ARP"); + chaostohost_pf_arp(dpchaos, buffp+(sizeof(struct ether_header)), cnt-sizeof(struct ether_header)); + + return; + } else if (((struct ether_header *)buffp)->ether_type == htons(ETHERTYPE_CHAOS)) { + if (cnt <= sizeof(sizeof(struct ether_header))) { + // Not even an Ethernet header read +#if 0 + /* If call timed out, should return 0 */ + if (cnt == 0 && dpchaos->dpchaos_rdtmo) + return; /* Just try again */ +#endif + if (DBGFLG) + dbprintln("ERead=%d, Err=%d", cnt, errno); + + if (cnt >= 0) { + dbprintln("Eread = %d, %s", cnt, + (cnt > 0) ? "no ether data" : "no packet"); + return; + } + + /* System call error of some kind */ + if (errno == EINTR) /* Ignore spurious signals */ + return; + + syserr(errno, "Eread = %d, errno %d", cnt, errno); + return; /* For now... */ + } + // At least an ethernet header was read! + chaostohost_pf_ether(dpchaos, buffp, cnt); + } else { + if (DBGFLG) { + dbprintln("Unexpected ether type %#x", ntohs(((struct ether_header *)buffp)->ether_type)); + (void) dumppkt_raw(buffp, (cnt > 8*8) ? 8*8 : cnt); + } + } +} + +void chaostohost_chudp(struct dpchaos_s *dpchaos, unsigned char *buffp) +{ + struct sockaddr_in ip_sender; + socklen_t iplen; + register int cnt; + + /* 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)); + iplen = sizeof(ip_sender); /* Supply size of ip_sender, and get actual stored length */ + cnt = recvfrom(chudpsock, buffp, DPCHAOS_MAXLEN, 0, (struct sockaddr *)&ip_sender, &iplen); + // buff now has a CHUDP pkt + if (cnt <= DPCHAOS_CHUDP_DATAOFFSET) { + if (DBGFLG) + fprintf(stderr, "[dpchaos-R: ERead=%d, Err=%d]\r\n", + cnt, errno); + + if (cnt < 0 && (errno == EINTR)) /* Ignore spurious signals */ + return; + + /* Error of some kind */ + fprintf(stderr, "[dpchaos-R: Eread = %d, ", cnt); + if (cnt < 0) { + 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"); + + return; /* For now... */ + } + if (DBGFLG) { + if (DBGFLG & 2) { + fprintf(stderr, "\r\n[dpchaos-R: Read=%d\r\n", cnt); + dumppkt(buffp, cnt); + fprintf(stderr, "]"); + } + else + fprintf(stderr, "[dpchaos-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); + return; + } + 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); + return; + } + + /* 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[DPCHAOS_CHUDP_DATAOFFSET+2] & 0xf)<<4) | buffp[DPCHAOS_CHUDP_DATAOFFSET+3]; + int datalen = (DPCHAOS_CHUDP_DATAOFFSET + CHAOS_HEADERSIZE + chalen); + int chafrom = (buffp[cnt-4]<<8) | buffp[cnt-3]; + char *ip = inet_ntoa(ip_sender.sin_addr); + in_port_t port = ntohs(ip_sender.sin_port); + time_t now = time(NULL); + int i, cks; + if ((cks = ch_checksum(buffp+DPCHAOS_CHUDP_DATAOFFSET,cnt-DPCHAOS_CHUDP_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 (dpchaos->dpchaos_ifmeth_chudp) { +#if 0 + if (DBGFLG) { + dbprintln("Rcv from chaos %o = ip %s port %d., %d. bytes (datalen %d)", + chafrom, + ip, port, + cnt, datalen); + dumppkt(buffp,cnt); + } +#endif + if ((cks == 0) && (chafrom != myaddr) && (dpchaos->dpchaos_chip_tlen < DPCHAOS_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 < dpchaos->dpchaos_chip_tlen; i++) + if (chafrom == dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_chaddr) { + if (dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_lastrcvd != 0) { + if (now - dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_lastrcvd > + DPCHAOS_CHIP_DYNAMIC_AGE_LIMIT) { + /* Old, update it (in case he moved) */ + if (1 || DBGFLG) + dbprintln("Updating CHIP entry %d for %o/%s:%d", + i, chafrom, ip, port); + dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_ipport = port; + memcpy(&dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_ipaddr, &ip_sender.sin_addr, IP_ADRSIZ); + } + /* update timestamp */ + dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_lastrcvd = now; + } + chafrom = -chafrom; /* Note 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 = dpchaos->dpchaos_chip_tlen; + if (1 || DBGFLG) + dbprintln("Adding CHIP entry %d for %o/%s:%d", + i, chafrom, ip, port); + dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_chaddr = chafrom; + dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_ipport = port; + dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_lastrcvd = now; + memcpy(&dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_ipaddr, &ip_sender.sin_addr, IP_ADRSIZ); + dpchaos->dpchaos_chip_tlen++; + } +#if 0 + else if (0 && DBGFLG) { + dbprintln("Already know chaos %o",chafrom); + } +#endif + } + else if ((dpchaos->dpchaos_chip_tlen >= DPCHAOS_CHIP_MAX) && DBGFLG) { + dbprintln("CHIP table full - cannot add %o", chafrom); + } + } + } + + ihl_hhsend(dpchaos, cnt, buffp); +} + + +void +chaostohost(register struct dpchaos_s *dpchaos) +{ + register struct dpx_s *dpx = dp_dpxfr(&dp); + unsigned char *inibuf; + unsigned char *buffp; + size_t max; + + 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, DPCHAOS_INIT, 0); /* Send INIT */ + + if (DBGFLG) + fprintf(stderr, "[dpchaos-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, "[dpchaos-R: InWait]\r\n"); + + /* Set up buffer and initialize offsets */ + buffp = inibuf; + + if (!dpchaos->dpchaos_ifmeth_chudp) { + // non-CHUDP case (Ethernet) + chaostohost_pf(dpchaos, buffp); + } else { // CHUDP case + chaostohost_chudp(dpchaos, buffp); + } + } +} + + +/* Send regular message from CHAOS to HOST. +*/ + +void +ihl_hhsend(register struct dpchaos_s *dpchaos, + 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); + + dp_xsend(dpx, DPCHAOS_RPKT, cnt-dpchaos->dpchaos_inoff); + + if (DBGFLG) + fprintf(stderr, "[dpchaos-R: sent RPKT %d+%d]", dpchaos->dpchaos_inoff, cnt-dpchaos->dpchaos_inoff); + +} + +/* HOSTTOCHAOS - Parent main loop for pumping packets from HOST to CHAOS. +** Reads CHAOS message from DP superior +** and interprets it. If a regular message, bundles it up and +** outputs to NET. +*/ +void +hosttochaos(register struct dpchaos_s *dpchaos) +{ + 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, "[dpchaos-W: Starting loop]\r\n"); + + for (;;) { + if (DBGFLG) + fprintf(stderr, "[dpchaos-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, "[dpchaos: Unknown cmd %d]\r\n", dp_xrcmd(dpx)); + dp_xrdone(dpx); + continue; + + case DPCHAOS_SPKT: /* Send regular packet */ + rcnt = dp_xrcnt(dpx); + /* does rcnt include chudp header size? yes (the output offset) */ + buff = inibuf; + if (DBGFLG) { + if (DBGFLG & 0x2) { + fprintf(stderr, "\r\n[dpchaos-W: Sending %d\r\n", rcnt); + dumppkt(buff, rcnt); + fprintf(stderr, "]"); + } + else + fprintf(stderr, "[dpchaos-W: SPKT %d]", rcnt); + } + break; + } + + /* Come here to handle output packet */ + int chlen = ((buff[dpchaos->dpchaos_outoff+2] & 0xf) << 4) | buff[dpchaos->dpchaos_outoff+3]; // DPCHAOS_CHUDP_DATAOFFSET + + if ((chlen+(chlen%2) + CHAOS_HEADERSIZE + dpchaos->dpchaos_outoff + 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 + dpchaos->dpchaos_outoff + CHAOS_HW_TRAILERSIZE), + chlen); + dp_xrdone(dpx); /* ack it to dvch11, but */ + continue; /* don't send it! */ + } + + if (!dpchaos->dpchaos_ifmeth_chudp) { + // Ethernet case + u_char *ch = &buff[dpchaos->dpchaos_outoff]; // DPCHAOS_CHUDP_DATAOFFSET + // length excluding trailer + u_short cpklen = dpchaos->dpchaos_outoff+CHAOS_HEADERSIZE+chlen+(chlen%2); + + // Use hardware dest, which is based on ITS routing table. + // But remove trailer before sending on Ethernet. + u_short *hdchad = (u_short *)&buff[cpklen]; + u_short dchad = htons(*hdchad); + if (DBGFLG & 2) + dbprintln("Found dest addr %#o (%#x) at offset %d+%d+%d+%d = %d", + dchad, dchad, dpchaos->dpchaos_outoff, CHAOS_HEADERSIZE, chlen, chlen%2, + cpklen); + if (dchad == 0) { /* broadcast */ + if (DBGFLG & 02) dbprintln("Broadcasting on ether"); + send_chaos_packet((u_char *)ð_brd, &buff[dpchaos->dpchaos_outoff], cpklen-dpchaos->dpchaos_outoff); + } else if (dchad == myaddr) { + if (DBGFLG & 02) dbprintln("Send to self ignored (ITS config bug?)"); + } else { + u_char *eaddr = find_arp_entry(dchad); + if (eaddr != NULL) { + if (DBGFLG & 02) dbprintln("Sending on ether to %#o", dchad); + send_chaos_packet(eaddr, &buff[dpchaos->dpchaos_outoff], cpklen-dpchaos->dpchaos_outoff); + } else { + if (DBGFLG) dbprintln("Don't know %#o, sending ARP request", dchad); + if (DBGFLG & 02) { + dumppkt(buff,rcnt); + } + send_chaos_arp_request(dchad); + // Chaos sender will retransmit, surely. + } + } + } else { + // CHUDP case + memcpy(buff, (void *)&chuhdr, sizeof(chuhdr)); /* put in CHUDP hdr */ + /* find IP destination given chaos packet */ + if (hi_iproute(&ipdest, &ipport, &buff[dpchaos->dpchaos_outoff], rcnt - dpchaos->dpchaos_outoff, dpchaos)) { + ip_write(&ipdest, ipport, buff, rcnt, dpchaos); + } + } + + /* Command done, tell 10 we're done with it */ + if (DBGFLG) + fprintf(stderr, "[dpchaos-W: CmdDone]"); + + dp_xrdone(dpx); + } +} + +int +hi_lookup(struct dpchaos_s *dpchaos, + 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 < dpchaos->dpchaos_chip_tlen; i++) + if ( + ((chdest == dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_chaddr) || + /* handle subnet matching */ + (sbnmatch && ((chdest >> 8) == (dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_chaddr >> 8)))) + && dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_ipaddr.s_addr != 0) { + memcpy(ipport, &dpchaos->dpchaos_chip_tbl[i].dpchaos_chip_ipport, sizeof(in_port_t)); + memcpy((void *)ipa, &dpchaos->dpchaos_chip_tbl[i].dpchaos_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 trailer. +*/ + +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 dpchaos_s *dpchaos) +{ + unsigned int chdest; + unsigned int hwdest; + + if (cnt < CHAOS_HEADERSIZE) { + error("Chaos packet too short: %d", cnt); + return FALSE; + } + + /* Derive destination IP address from Chaos header */ + chdest = lp[DPCHAOS_CH_DESTOFF]<<8 | lp[DPCHAOS_CH_DESTOFF+1]; + /* Use hw trailer - ITS knows routing and uses the trailer to tell us where to send it */ + hwdest = lp[cnt-6] << 8 | lp[cnt-5]; + if (DBGFLG) + dbprintln("looking for route to chaos %o (hw %o) (FC %d.)", chdest, hwdest, + lp[DPCHAOS_CH_FC] >> 4); + if (hi_lookup(dpchaos, ipa, ipport, chdest, 0) + /* try subnet match */ + || ((chdest != hwdest) && hi_lookup(dpchaos, ipa, ipport, hwdest, 0)) // ITS knows the route + || hi_lookup(dpchaos, ipa, ipport, chdest, 1) // maybe ITS thinks it's directly connected + || ((chdest != hwdest) && hi_lookup(dpchaos, ipa, ipport, hwdest, 1)) // last resort + ) { + 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 dpchaos_s *dpchaos) +{ + 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(chudpsock, buf, len, 0, (struct sockaddr *) &sin, (socklen_t) (sizeof(sin))) != len) { + error("sendto failed - %s", dp_strerror(errno)); + } +} + +/* **** Chaos-over-Ethernet functions **** [based on cbridge + dpni20] */ + +void +send_chaos_packet(unsigned char *ea, unsigned char *buf, int cnt) +{ + unsigned char pktbuf[ARP_PKTSIZ]; + struct dpchaos_s *dpc = (struct dpchaos_s *)dp.dp_adr; + u_char *my_ea = dpc->dpchaos_eth; + u_short chatyp = htons(ETHERTYPE_CHAOS); + + // construct ether header + ea_set(&pktbuf[0], ea); /* dest ea */ + ea_set(&pktbuf[ETHER_ADDR_LEN], my_ea); /* src ea */ + memcpy(&pktbuf[ETHER_ADDR_LEN*2], &chatyp, 2); /* pkt type */ + + memcpy(&pktbuf[ETHER_ADDR_LEN*2+2], buf, cnt); + + // byte swap for network + htons_buf((u_short *)&pktbuf[ETHER_ADDR_LEN*2+2], (u_short *)&pktbuf[ETHER_ADDR_LEN*2+2], cnt); + (void) osn_pfwrite(&pfdata, pktbuf, cnt+ETHER_ADDR_LEN*2+2); +} + +#if KLH10_NET_BPF + +#define BPF_MTU CH_PK_MAXLEN // (BPF_WORDALIGN(1514) + BPF_WORDALIGN(sizeof(struct bpf_hdr))) + +// See dpimp.c +/* Packet byte offsets for interesting fields (in network order) */ +#define PKBOFF_EDEST 0 /* 1st shortword of Ethernet destination */ +#define PKBOFF_ETYPE 12 /* Shortwd offset to Ethernet packet type */ +#define PKBOFF_ARP_PTYPE (sizeof(struct ether_header)+sizeof(u_short)) /* ARP protocol type */ + +/* BPF simple Loads */ +#define BPFI_LD(a) bpf_stmt(BPF_LD+BPF_W+BPF_ABS,(a)) /* Load word P[a:4] */ +#define BPFI_LDH(a) bpf_stmt(BPF_LD+BPF_H+BPF_ABS,(a)) /* Load short P[a:2] */ +#define BPFI_LDB(a) bpf_stmt(BPF_LD+BPF_B+BPF_ABS,(a)) /* Load byte P[a:1] */ + +/* BPF Jumps and skips */ +#define BPFI_J(op,k,t,f) bpf_jump(BPF_JMP+(op)+BPF_K,(k),(t),(f)) +#define BPFI_JEQ(k,n) BPFI_J(BPF_JEQ,(k),(n),0) /* Jump if A == K */ +#define BPFI_JNE(k,n) BPFI_J(BPF_JEQ,(k),0,(n)) /* Jump if A != K */ +#define BPFI_JGT(k,n) BPFI_J(BPF_JGT,(k),(n),0) /* Jump if A > K */ +#define BPFI_JLE(k,n) BPFI_J(BPF_JGT,(k),0,(n)) /* Jump if A <= K */ +#define BPFI_JGE(k,n) BPFI_J(BPF_JGE,(k),(n),0) /* Jump if A >= K */ +#define BPFI_JLT(k,n) BPFI_J(BPF_JGE,(k),0,(n)) /* Jump if A < K */ +#define BPFI_JDO(k,n) BPFI_J(BPF_JSET,(k),(n),0) /* Jump if A & K */ +#define BPFI_JDZ(k,n) BPFI_J(BPF_JSET,(k),0,(n)) /* Jump if !(A & K) */ + +#define BPFI_CAME(k) BPFI_JEQ((k),1) /* Skip if A == K */ +#define BPFI_CAMN(k) BPFI_JNE((k),1) /* Skip if A != K */ +#define BPFI_CAMG(k) BPFI_JGT((k),1) /* Skip if A > K */ +#define BPFI_CAMLE(k) BPFI_JLE((k),1) /* Skip if A <= K */ +#define BPFI_CAMGE(k) BPFI_JGE((k),1) /* Skip if A >= K */ +#define BPFI_CAML(k) BPFI_JLT((k),1) /* Skip if A < K */ +#define BPFI_TDNN(k) BPFI_JDO((k),1) /* Skip if A & K */ +#define BPFI_TDNE(k) BPFI_JDZ((k),1) /* Skip if !(A & K) */ + +/* BPF Returns */ +#define BPFI_RET(n) bpf_stmt(BPF_RET+BPF_K, (n)) /* Return N bytes */ +#define BPFI_RETFAIL() BPFI_RET(0) /* Failure return */ +#define BPFI_RETWIN() BPFI_RET((u_int)-1) /* Success return */ + +// My addition +#define BPFI_SKIP(n) BPFI_J(BPF_JA,0,(n),(n)) /* skip n instructions */ + +struct bpf_insn bpf_stmt(unsigned short code, bpf_u_int32 k) +{ + struct bpf_insn ret; + ret.code = code; + ret.jt = 0; + ret.jf = 0; + ret.k = k; + return ret; +} +struct bpf_insn bpf_jump(unsigned short code, bpf_u_int32 k, + unsigned char jt, unsigned char jf) +{ + struct bpf_insn ret; + ret.code = code; + ret.jt = jt; + ret.jf = jf; + ret.k = k; + return ret; +} +#endif // KLH10_NET_BPF + +#if (KLH10_NET_PCAP || KLH10_NET_BPF) + +// Here is the BPF program, simple +#define BPF_PFMAX 50 +struct bpf_insn bpf_pftab[BPF_PFMAX]; +struct bpf_program bpf_pfilter = {0, bpf_pftab}; + +static void pfshow(struct bpf_program *pf); + +// this gets called from osn_pfinit +// make a filter for both Chaos and ARP (for Chaos), have the read method handle ARP +struct bpf_program * +pfbuild(void *arg, struct in_addr *ipa) +{ + struct dpchaos_s *dpc = (struct dpchaos_s *)arg; + struct bpf_program *pfp = &bpf_pfilter; + + + // Check the ethernet type field +#if KLH10_NET_PCAP +#define MAX_PFLEN 136 // really need only 130 + u_char bpfilter[MAX_PFLEN]; + char eastr[OSN_EASTRSIZ]; + // must also check for address since interface may be in promisc mode although we didn't ask for it + sprintf(bpfilter, + // arp pkt for Chaosnet + "(ether proto 0x0806 && arp[2:2] = 0x0804) || " + // or Chaosnet pkt for me or broadcast + "(ether proto 0x0804 && (ether dst %s || ether dst ff:ff:ff:ff:ff:ff))", + eth_adrsprint(eastr, dpc->dpchaos_eth)); + // Never mind about netmask, this is not IP anyway. + if (DP_DBGFLG > 1) + dbprintln("compiling pcap program \"%s\"", bpfilter); + if (pcap_compile(pfdata.pf_handle, pfp, bpfilter, 1, PCAP_NETMASK_UNKNOWN) != 0) { + efatal(1, "pcap_compile failed: %s", pcap_geterr(pfdata.pf_handle)); + } +#else + // simplistic, only checks types, not addresses + struct bpf_insn *p = pfp->bf_insns; + // if etype == arp && arp_ptype == chaos then win + // if etype == chaos then win + *p++ = BPFI_LDH(PKBOFF_ETYPE); /* Load ethernet type field */ + *p++ = BPFI_CAMN(ETHERTYPE_CHAOS); /* Win if CHAOS */ + *p++ = BPFI_RETWIN(); + *p++ = BPFI_CAME(ETHERTYPE_ARP); /* Skip if ARP */ + *p++ = BPFI_RETFAIL(); /* Not ARP, ignore */ + // For ARP, check the protocol type + *p++ = BPFI_LDH(PKBOFF_ARP_PTYPE); /* Check the ARP type */ + *p++ = BPFI_CAME(ETHERTYPE_CHAOS); + *p++ = BPFI_RETFAIL(); /* Not Chaos, ignore */ + // Never mind about destination here, if we get other ARP info that's nice? + *p++ = BPFI_RETWIN(); + pfp->bf_len = p - pfp->bf_insns; /* Set # of items on list */ +#endif // pcap/not + if (DP_DBGFLG > 1) /* If debugging, print out resulting filter */ + pfshow(pfp); + return pfp; +} + + +// from dpni20 +/* Debug auxiliary to print out packetfilter we composed. +*/ +static void +pfshow(struct bpf_program *pf) +{ + int i; + + fprintf(stderr, "[%s: kernel packetfilter len %d:\r\n", + progname, pf->bf_len); + for (i = 0; i < pf->bf_len; ++i) + fprintf(stderr, "%04X %2d %2d %0X\r\n", + pf->bf_insns[i].code, + pf->bf_insns[i].jt, + pf->bf_insns[i].jf, + pf->bf_insns[i].k); + fprintf(stderr, "]\r\n"); +} + +#endif /* KLH10_NET_PCAP || KLH10_NET_BPF */ + +/* **** Chaosnet ARP functions **** */ + +void init_arp_table() +{ + struct dpchaos_s *dpc = (struct dpchaos_s *)dp.dp_adr; + memset((char *)dpc->charp_list, 0, sizeof(struct charp_ent)*CHARP_MAX); + dpc->charp_len = 0; +} + +void print_arp_table() +{ + int i; + time_t age; + struct dpchaos_s *dpc = (struct dpchaos_s *)dp.dp_adr; + if (dpc->charp_len > 0) { + printf("Chaos ARP table:\r\n" + "Chaos\tEther\t\t\tAge (s)\r\n"); + for (i = 0; i < dpc->charp_len; i++) { + age = (time(NULL) - dpc->charp_list[i].charp_age); + printf("%#o\t\%02X:%02X:%02X:%02X:%02X:%02X\t%lu\t%s\r\n", + dpc->charp_list[i].charp_chaddr, + dpc->charp_list[i].charp_eaddr[0], + dpc->charp_list[i].charp_eaddr[1], + dpc->charp_list[i].charp_eaddr[2], + dpc->charp_list[i].charp_eaddr[3], + dpc->charp_list[i].charp_eaddr[4], + dpc->charp_list[i].charp_eaddr[5], + age, age > CHARP_MAX_AGE ? "(old)" : ""); + } + } +} + +u_char *find_arp_entry(u_short daddr) +{ + int i; + struct dpchaos_s *dpc = (struct dpchaos_s *)dp.dp_adr; + u_short my_chaddr = dpc->dpchaos_myaddr; + + if (DP_DBGFLG) dbprintln("Looking for ARP entry for %#o, ARP table len %d", daddr, dpc->charp_len); + if (daddr == my_chaddr) { + if (DP_DBGFLG > 1) dbprintln("#### Looking up ARP for my own address, BUG!"); + return NULL; + } + for (i = 0; i < dpc->charp_len && i < CHARP_MAX; i++) + if (dpc->charp_list[i].charp_chaddr == daddr) { + if ((dpc->charp_list[i].charp_age != 0) + && ((time(NULL) - dpc->charp_list[i].charp_age) > CHARP_MAX_AGE)) { + if (DP_DBGFLG) dbprintln("Found ARP entry for %#o but it is too old (%lu s)", + daddr, (time(NULL) - dpc->charp_list[i].charp_age)); + return NULL; + } + if (DP_DBGFLG & 040) dbprintln("Found ARP entry for %#o", daddr); + return dpc->charp_list[i].charp_eaddr; + } + if (DP_DBGFLG) + print_arp_table(); + return NULL; +} + + +// from cbridge, rewritten reading arp_myreply from dpni20 +void +send_chaos_arp_pkt(u_short atyp, u_short dest_chaddr, u_char *dest_eth) +{ + unsigned char pktbuf[ARP_PKTSIZ]; + struct dpchaos_s *dpc = (struct dpchaos_s *)dp.dp_adr; + u_short my_chaddr = dpc->dpchaos_myaddr; + u_char req[sizeof(struct arphdr)+(ETHER_ADDR_LEN+2)*2]; + struct arphdr *arp = (struct arphdr *)&req; + u_short arptyp = htons(ETHERTYPE_ARP); + + // first ethernet header + memcpy(&pktbuf[0], dest_eth, ETHER_ADDR_LEN); /* dest ether */ + ea_set(&pktbuf[ETHER_ADDR_LEN], dpc->dpchaos_eth); /* source ether = me */ + memcpy(&pktbuf[ETHER_ADDR_LEN*2], &arptyp, 2); /* ether pkt type */ + + // now arp pkt + memset(&req, 0, sizeof(req)); + arp->ar_hrd = htons(ARPHRD_ETHER); /* Want ethernet address */ + arp->ar_pro = htons(ETHERTYPE_CHAOS); /* of a Chaosnet address */ + arp->ar_hln = ETHER_ADDR_LEN; + arp->ar_pln = sizeof(dest_chaddr); + arp->ar_op = htons(atyp); + memcpy(&req[sizeof(struct arphdr)], dpc->dpchaos_eth, ETHER_ADDR_LEN); /* my ether */ + memcpy(&req[sizeof(struct arphdr)+ETHER_ADDR_LEN], &my_chaddr, sizeof(my_chaddr)); /* my chaos */ + /* his ether */ + if (atyp == ARPOP_REPLY) + memcpy(&req[sizeof(struct arphdr)+ETHER_ADDR_LEN+2], dest_eth, ETHER_ADDR_LEN); + /* his chaos */ + memcpy(&req[sizeof(struct arphdr)+ETHER_ADDR_LEN+2+ETHER_ADDR_LEN], &dest_chaddr, sizeof(my_chaddr)); + + // and merge them + memcpy(&pktbuf[ETHER_ADDR_LEN*2+2], req, sizeof(req)); + + if (DP_DBGFLG & 4) { + dbprintln("Sending ARP pkt"); + describe_eth_pkt_hdr(pktbuf); + describe_arp_pkt(arp, req); + //describe_arp_pkt((struct arphdr *)&pktbuf[0], &pktbuf[ETHER_ADDR_LEN*2+2]); + } + + // and send it + osn_pfwrite(&pfdata, pktbuf, sizeof(pktbuf)); +} + +void +send_chaos_arp_request(u_short chaddr) +{ + send_chaos_arp_pkt(ARPOP_REQUEST, chaddr, (u_char *)ð_brd); +} + +void +send_chaos_arp_reply(u_short dest_chaddr, u_char *dest_eth) +{ + send_chaos_arp_pkt(ARPOP_REPLY, dest_chaddr, dest_eth); +} + + +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"; + } + +char * +ch_char(unsigned char x, char *buf) { + if (x < 32) + sprintf(buf,"^%c", x+64); + else if (x == 127) + sprintf(buf,"^?"); + else if (x < 127) + sprintf(buf,"%2c",x); + else + sprintf(buf,"%2x",x); + return buf; +} + +void +dumppkt(unsigned char *ucp, int cnt) +{ + int i, row; + struct dpchaos_s *dpc = (struct dpchaos_s *)dp.dp_adr; + + int off = dpc->dpchaos_outoff; + char b1[3],b2[3]; + + if (off == DPCHAOS_CHUDP_DATAOFFSET) + fprintf(stderr,"CHUDP version %d, function %d, ", ucp[0], ucp[1]); + fprintf(stderr,"Chaos pkt len %d\r\n", cnt); + ucp += off; + cnt -= off; + fprintf(stderr,"Opcode: %#o (%s), unused: %o\r\nFC: %d, Nbytes %d.\r\n", + ucp[0], ch_opcode(ucp[0]), + ucp[1], ucp[2]>>4, ((ucp[2]&0xf)<<4) | ucp[3]); + fprintf(stderr,"Dest host: %#o, index %#o\r\nSource host: %#o, index %#o\r\n", + (ucp[4]<<8)|ucp[5], (ucp[6]<<8)|ucp[7], + (ucp[8]<<8)|ucp[9], (ucp[10]<<8)|ucp[11]); + fprintf(stderr,"Packet #%d.\r\nAck #%d.\r\n", + (ucp[12]<<8)|ucp[13], (ucp[14]<<8)|ucp[15]); + fprintf(stderr,"Data:\r\n"); + + /* Skip headers */ + ucp += CHAOS_HEADERSIZE; + /* Show only data portion */ + cnt -= 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 1 + for (i = 0; (i < 8) && (i+row*8 < cnt); i++) { + fprintf(stderr, " %2s", ch_char(ucp[i+row*8], (char *)&b1)); + fprintf(stderr, "%2s", ch_char(ucp[(++i)+row*8], (char *)&b2)); + } + fprintf(stderr, " (chars)\r\n"); + for (i = 0; (i < 8) && (i+row*8 < cnt); i++) { + fprintf(stderr, " %2s", ch_char(ucp[i+1+row*8], (char *)&b1)); + fprintf(stderr, "%2s", ch_char(ucp[(i++)+row*8], (char *)&b2)); + } + 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: %#x\r\n", + (ucp[cnt]<<8)|ucp[cnt+1],(ucp[cnt+2]<<8)|ucp[cnt+3],(ucp[cnt+4]<<8)|ucp[cnt+5]); +} + + +char * +ch_adrsprint(char *cp, unsigned char *ca) +{ + sprintf(cp, "%#o", (ca[1]<<8) | ca[0]); /* unswapped */ + return cp; +} + +/* Add OSDNET shared code here */ + +#include "osdnet.c" diff --git a/src/dpchaos.h b/src/dpchaos.h new file mode 100644 index 0000000..dd4a68f --- /dev/null +++ b/src/dpchaos.h @@ -0,0 +1,133 @@ +/* DPCHAOS.H - Definitions for CHAOS 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 DPCHAOS_INCLUDED +#define DPCHAOS_INCLUDED 1 + +#ifndef DPSUP_INCLUDED +# include "dpsup.h" +#endif +#ifndef OSDNET_INCLUDED +# include "osdnet.h" +#endif + +/* Version of DPCHAOS-specific shared memory structure */ +#define DPCHAOS_VERSION DPC_VERSION(2,0,0) /* 2.0.0 */ + +#define IFNAM_LEN PATH_MAX /* at least IFNAMSIZ! */ + +#ifndef DPCHAOS_CHIP_MAX +# define DPCHAOS_CHIP_MAX 10 +#endif + +/* Chaos ARP list */ +// @@@@ implement something to show the table +#define CHARP_MAX 16 +#define CHARP_MAX_AGE (60*5) // ARP cache limit +struct charp_ent { + u_char charp_eaddr[ETHER_ADDR_LEN]; + u_short charp_chaddr; + time_t charp_age; +}; + +/* If a dynamically added CHIP entry is older than this (seconds), it can get updated */ +#ifndef DPCHAOS_CHIP_DYNAMIC_AGE_LIMIT +# define DPCHAOS_CHIP_DYNAMIC_AGE_LIMIT (60*5) +#endif + +/* Chaos/IP mapping entry - see ch_chip */ +struct dpchaos_chip { + unsigned int dpchaos_chip_chaddr; /* Chaos address */ + struct in_addr dpchaos_chip_ipaddr; /* IP address */ + in_port_t dpchaos_chip_ipport; /* IP port */ + time_t dpchaos_chip_lastrcvd; /* When last received, if dynamically added */ +}; + +/* DPCHAOS-specific stuff */ + /* C = controlling parent sets, D = Device proc sets */ + /* If both, 1st letter indicates inital setter */ +struct dpchaos_s { + struct dpc_s dpchaos_dpc; /* CD Standard DPC portion */ + int dpchaos_ver; /* C Version of shared struct */ + int dpchaos_attrs; /* C Attribute flags */ + char dpchaos_ifnam[IFNAM_LEN]; /* CD Interface name if any */ + char dpchaos_ifmeth[16]; /* C Interface method */ + int dpchaos_ifmeth_chudp; /* C Interface method is CHUDP? */ + unsigned short dpchaos_myaddr; /* C my Chaos address */ + unsigned char dpchaos_eth[6]; /* CD Ethernet address of interface */ + /* probably not used */ + int dpchaos_inoff; /* C Offset in buffer of input (I->H) data */ + int dpchaos_outoff; /* D Offset in buffer of output (H->I) data */ + int dpchaos_backlog; /* C Max sys backlog of rcvd packets */ + int dpchaos_dedic; /* C TRUE if dedicated ifc, else shared */ + in_port_t dpchaos_port; /* C port for CHUDP protocol */ + /* Chaos/IP mapping */ + int dpchaos_chip_tlen; /* C table length */ + struct dpchaos_chip dpchaos_chip_tbl[DPCHAOS_CHIP_MAX]; + // ARP table + struct charp_ent charp_list[CHARP_MAX]; /* D arp table */ + int charp_len; /* D arp table length */ +}; + + +/* 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 */ + +#ifndef ETHERTYPE_CHAOS +# define ETHERTYPE_CHAOS 0x0804 +#endif +// old names for new, new names for old? +#ifndef ARPOP_RREQUEST +#define ARPOP_RREQUEST ARPOP_REVREQUEST // 3 /* request protocol address given hardware */ +#endif +#ifndef ARPOP_RREPLY +#define ARPOP_RREPLY ARPOP_REVREPLY // 4 /* response giving protocol address */ +#endif + +#include "dvch11.h" +#define DPCHAOS_CHUDP_DATAOFFSET (sizeof(struct chudp_header)) // 4 bytes +#define DPCHAOS_ETHER_DATAOFFSET (sizeof(struct ether_header)) // 6+6+2=16 bytes +// room for protocol header + Chaos header + max Chaos data + Chaos hw trailer +#define DPCHAOS_MAXLEN (DPCHAOS_ETHER_DATAOFFSET+CHAOS_HEADERSIZE+CHAOS_MAXDATA+CHAOS_HW_TRAILERSIZE+42) /* some slack */ + +#define DPCHAOS_CH_DESTOFF 4 /* offset to dest addr in chaos pkt */ +#define DPCHAOS_CH_FC 2 /* offset to forwarding count */ + +/* Commands to and from DP and KLH10 CH11 driver */ + + /* From 10 to DP */ +#define DPCHAOS_RESET 0 /* Reset DP */ +#define DPCHAOS_SPKT 1 /* Send data packet to ethernet */ + + /* From DP to 10 */ +#define DPCHAOS_INIT 1 /* DP->10 Finished init */ +#define DPCHAOS_RPKT 2 /* DP->10 Received data packet from net */ + +#endif /* ifndef DPCHUDP_INCLUDED */ diff --git a/src/dpchudp.c b/src/dpchudp.c deleted file mode 100644 index aae2077..0000000 --- a/src/dpchudp.c +++ /dev/null @@ -1,912 +0,0 @@ -/* 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 - -#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)); - iplen = sizeof(ip_sender); /* Supply size of ip_sender, and get actual stored length */ - 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]; - char *ip = inet_ntoa(ip_sender.sin_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 %s port %d., %d. bytes (datalen %d)", - chafrom, - ip, 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/%s:%d", - i, chafrom, ip, port); - dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_ipport = port; - memcpy(&dpchudp->dpchudp_chip_tbl[i].dpchudp_chip_ipaddr, &ip_sender.sin_addr, 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/%s:%d", - i, chafrom, ip, 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_sender.sin_addr, 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 deleted file mode 100644 index d39646a..0000000 --- a/src/dpchudp.h +++ /dev/null @@ -1,108 +0,0 @@ -/* 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 ccea801..7484b1d 100644 --- a/src/dvch11.c +++ b/src/dvch11.c @@ -2,7 +2,7 @@ */ /* $Id: dvch11.c,v 2.3 2001/11/10 21:28:59 klh Exp $ */ -/* Copyright © 2005 Björn Victor and Kenneth L. Harrenstien +/* Copyright © 2005, 2018 Björn Victor and Kenneth L. Harrenstien ** All Rights Reserved ** ** This file may become part of the KLH10 Distribution. Use, modification, and @@ -31,7 +31,7 @@ ** 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. +** Based on dvlhdh.c, with dpchaos.c based on dpimp.c. ** (Some things may still be irrelevant inheritage from dvlhdh, and could be cleaned up...) */ @@ -55,7 +55,7 @@ static int decosfcclossage; #include "dvuba.h" #include "dvch11.h" -#include "dpchudp.h" +#include "dpchaos.h" #ifndef KLH10_CH11_USE_GETHOSTBYNAME # define KLH10_CH11_USE_GETHOSTBYNAME 1 @@ -65,7 +65,7 @@ static int decosfcclossage; # include #endif -#define CHUDPBUFSIZ (DPCHUDP_MAXLEN+500) /* Plenty of slop */ +#define CHAOSBUFSIZ (DPCHAOS_MAXLEN+500) /* Plenty of slop */ #ifndef CH11_NSUP @@ -73,16 +73,16 @@ static int decosfcclossage; #endif #ifndef CH11_CHIP_MAX -# define CH11_CHIP_MAX 10 /* max Chaos/IP mappings - see DPCHUDP_CHIP_MAX */ +# define CH11_CHIP_MAX 10 /* max Chaos/IP mappings - see DPCHAOS_CHIP_MAX */ #endif -#if CH11_CHIP_MAX > DPCHUDP_CHIP_MAX -# error "DPCHUDP_CHIP_MAX must be at least CH11_CHIP_MAX" +#if CH11_CHIP_MAX > DPCHAOS_CHIP_MAX +# error "DPCHAOS_CHIP_MAX must be at least CH11_CHIP_MAX" #endif #define REG(u) ((u)->ch_reg) -/* Chaos/IP mapping entry - see dpchudp_chip */ +/* Chaos/IP mapping entry - see dpchaos_chip */ struct ch_chip { unsigned int ch_chip_chaddr; /* Chaos address */ struct in_addr ch_chip_ipaddr; /* IP address */ @@ -108,10 +108,11 @@ struct ch11 { /* Misc config info not set elsewhere */ char *ch_ifnam; /* Native platform's interface name */ + char *ch_ifmeth; /* Native platform's interface access method */ 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 */ + in_port_t ch_chudp_port; /* CHUDP port to use */ /* DP stuff */ char *ch_dpname; /* Pointer to dev process pathname */ @@ -153,15 +154,15 @@ 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 */ + /* Virtual CHAOS 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); +int chaos_init(struct ch11 *ch, FILE *of); +static int chaos_start(struct ch11 *ch); +static void chaos_stop(struct ch11 *ch); +static void chaos_kill(struct ch11 *ch); +static int chaos_incheck(struct ch11 *ch); +static void chaos_inxfer(struct ch11 *ch); +static int chaos_outxfer(struct ch11 *ch); /* Configuration Parameters */ @@ -180,8 +181,9 @@ static int chudp_outxfer(struct ch11 *ch); \ prmdef(CH11P_MYADDR, "myaddr"),/* My Chaosnet address */\ prmdef(CH11P_CHUPORT, "chudpport"), /* CHUDP port to use */\ - prmdef(CH11P_CHIP, "chip") /* Chaos/IP mapping */ - + prmdef(CH11P_CHIP, "chip"), /* Chaos/IP mapping */\ +\ + prmdef(CH11P_IFMETH, "ifmeth") /* Interface method (chudp, pcap, etc) */ enum { # define prmdef(i,s) i @@ -212,6 +214,7 @@ ch11_conf(FILE *f, char *s, struct ch11 *ch) struct prmstate_s prm; char buff[200]; long lval; + int chudp_params_set = 0; /* First set defaults for all configurable parameters Unfortunately there's currently no way to access the UBA # that @@ -224,7 +227,7 @@ ch11_conf(FILE *f, char *s, struct ch11 *ch) ch->ch_dedic = FALSE; ch->ch_dpidly = 0; ch->ch_dpdbg = FALSE; - ch->ch_dpname = "dpchudp"; /* Pathname of device subproc */ + ch->ch_dpname = "dpchaos"; /* Pathname of device subproc */ ch->ch_chip_tlen = 0; ch->ch_chudp_port = CHUDP_PORT; @@ -286,7 +289,8 @@ ch11_conf(FILE *f, char *s, struct ch11 *ch) case CH11P_MYADDR: /* Parse as octal number */ if (!prm.prm_val || !s_tonum(prm.prm_val, &lval)) break; - if ((lval < 1) || (lval >= 0xffff)) { + if (((lval & 0xff00) == 0) || ((lval & 0xff) == 0) || (lval >= 0xffff)) { + // subnet part must be nonzero, host part too, also max 16 bit fprintf(f, "CH11 MYADDR must be a valid Chaosnet address\n"); ret = FALSE; } else @@ -294,6 +298,7 @@ ch11_conf(FILE *f, char *s, struct ch11 *ch) continue; case CH11P_CHUPORT: + chudp_params_set = 1; if (!prm.prm_val || !s_todnum(prm.prm_val, &lval)) break; if ((lval < 1) || (lval >= 0xffff)) { @@ -304,6 +309,7 @@ ch11_conf(FILE *f, char *s, struct ch11 *ch) continue; case CH11P_CHIP: + chudp_params_set = 1; if (ch->ch_chip_tlen >= CH11_CHIP_MAX) { fprintf(f,"CH11 Chaos/IP table full\n"); break; @@ -404,6 +410,12 @@ ch11_conf(FILE *f, char *s, struct ch11 *ch) ch->ch_dpname = s_dup(prm.prm_val); continue; + case CH11P_IFMETH: /* Parse as simple string */ + if (!prm.prm_val) + break; + ch->ch_ifmeth = s_dup(prm.prm_val); + continue; + } ret = FALSE; fprintf(f, "CH11 param \"%s\": ", prm.prm_name); @@ -429,6 +441,20 @@ ch11_conf(FILE *f, char *s, struct ch11 *ch) "CH11 param \"myaddr\" must be set\n"); return FALSE; } + if (ch->ch_ifmeth) { + if ((strcasecmp(ch->ch_ifmeth,"chudp") != 0) && + (strcasecmp(ch->ch_ifmeth,"pcap") != 0)) { + fprintf(f,"CH11 param \"ifmeth\" only supports \"chudp\" and \"pcap\" for now\n"); + return FALSE; + } + } else if (chudp_params_set) { + // backwards compat + fprintf(f, "CH11 assuming \"chudp\" interface method\n"); + ch->ch_ifmeth = s_dup("chudp"); + } else { + fprintf(f, "CH11 does not know which interface method to use, none specified?\n"); + return FALSE; + } return ret; } @@ -491,9 +517,9 @@ dvch11_create(FILE *f, char *s) 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; + struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; + fprintf(of,"Old value: %d. New value: %d.\n", dpc->dpchaos_dpc.dpc_debug, val); + dpc->dpchaos_dpc.dpc_debug = val; } static void @@ -504,38 +530,74 @@ ch11_cmd_chiptable(struct ch11 *ch, FILE *of) 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; + struct dpchaos_chip *chip; + struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; if (!dpc) { fprintf(of,"Can't find DP!\n"); return; } - n = dpc->dpchudp_chip_tlen; + n = dpc->dpchaos_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; + chip = &dpc->dpchaos_chip_tbl[i]; + ip = (unsigned char *)&chip->dpchaos_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); + if (chip->dpchaos_chip_lastrcvd != 0) { + ltime = localtime(&chip->dpchaos_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, + chip->dpchaos_chip_chaddr, ipa, - chip->dpchudp_chip_ipport, + chip->dpchaos_chip_ipport, last); } } } +void ch11_cmd_print_arp_table(struct ch11 *ch, FILE *of) +{ + int i; + time_t age; + struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; + if (dpc->charp_len > 0) { + fprintf(of,"Chaos ARP table:\r\n" + "Chaos\tEther\t\t\tAge (s)\r\n"); + for (i = 0; i < dpc->charp_len; i++) { + age = (time(NULL) - dpc->charp_list[i].charp_age); + fprintf(of,"%#o\t\%02X:%02X:%02X:%02X:%02X:%02X\t%lu\t%s\r\n", + dpc->charp_list[i].charp_chaddr, + dpc->charp_list[i].charp_eaddr[0], + dpc->charp_list[i].charp_eaddr[1], + dpc->charp_list[i].charp_eaddr[2], + dpc->charp_list[i].charp_eaddr[3], + dpc->charp_list[i].charp_eaddr[4], + dpc->charp_list[i].charp_eaddr[5], + age, age > CHARP_MAX_AGE ? "(old)" : ""); + } + } else + fprintf(of,"Chaos ARP table empty\r\n"); +} + + 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); + struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; + + fprintf(of,"My CHAOS address: 0%o.\n", ch->ch_myaddr); + if (dpc->dpchaos_ifmeth_chudp) + fprintf(of, " CHUDP port: %d.\n", ch->ch_chudp_port); + else { + if (dpc->dpchaos_ifnam) + fprintf(of, " Using ifc=\"%s\"", dpc->dpchaos_ifnam); + fprintf(of, " ether addr %02X:%02X:%02X:%02X:%02X:%02X\n", + dpc->dpchaos_eth[0],dpc->dpchaos_eth[1],dpc->dpchaos_eth[2], + dpc->dpchaos_eth[3],dpc->dpchaos_eth[4],dpc->dpchaos_eth[5]); + } fprintf(of,"Status register: 0%o\n", REG(ch)); if (REG(ch) & CH_BSY) fprintf(of, " Transmit busy\n"); @@ -580,12 +642,15 @@ static int ch11_cmd(register struct device *d, FILE *of, char *cmd) { register struct ch11 *ch = (struct ch11 *)d; + struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; if (*cmd) while (*cmd == ' ') cmd++; if (*cmd && (strcmp(cmd,"chiptable") == 0)) ch11_cmd_chiptable(ch, of); + else if (*cmd && ((strcmp(cmd,"arptable") == 0) || (strcmp(cmd,"arp") == 0))) + ch11_cmd_print_arp_table(ch, of); else if (*cmd && (strcmp(cmd,"status") == 0)) ch11_cmd_status(ch, of); else if (*cmd && strncmp(cmd,"dpdebug",strlen("dpdebug")) == 0) { @@ -600,11 +665,15 @@ ch11_cmd(register struct device *d, FILE *of, char *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 */ + fprintf(of,"Commands:\n \"chiptable\" to show the Chaos/IP table\n \"arptable\" to show the Chaos ARP table\n \"status\" to show device status\n \"dpdebug x\" to set dpchaos debug level to x\n"); + } else { + /* No command, do all */ ch11_cmd_status(ch, of); - ch11_cmd_chiptable(ch, of); + if (dpc->dpchaos_ifmeth_chudp) + ch11_cmd_chiptable(ch, of); + else + ch11_cmd_print_arp_table(ch, of); + } return TRUE; } @@ -620,7 +689,7 @@ ch11_init(struct device *d, FILE *of) { register struct ch11 *ch = (struct ch11 *)d; - if (!chudp_init(ch, of)) + if (!chaos_init(ch, of)) return FALSE; ch_clear(ch); return TRUE; @@ -632,7 +701,7 @@ ch11_init(struct device *d, FILE *of) static void ch11_powoff(struct device *d) { - chudp_kill((struct ch11 *)d); + chaos_kill((struct ch11 *)d); } @@ -665,9 +734,11 @@ ch_oint_off(register struct ch11 *ch) static void ch_iclear(register struct ch11 *ch) { + struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; + ch_iint_off(ch); if (ch->ch_rbuf) - ch->ch_iptr = ch->ch_rbuf + DPCHUDP_DATAOFFSET; + ch->ch_iptr = ch->ch_rbuf + dpc->dpchaos_inoff; // DPCHUDP_DATAOFFSET; else ch->ch_iptr = NULL; ch->ch_rcnt = -1; @@ -680,9 +751,11 @@ ch_iclear(register struct ch11 *ch) static void ch_oclear(register struct ch11 *ch) { + struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; + ch_oint_off(ch); if (ch->ch_sbuf) - ch->ch_optr = ch->ch_sbuf + DPCHUDP_DATAOFFSET; + ch->ch_optr = ch->ch_sbuf + dpc->dpchaos_outoff; // DPCHUDP_DATAOFFSET; else ch->ch_optr = NULL; REG(ch) &= ~CH_TAB; /* Not aborted */ @@ -693,7 +766,7 @@ ch_oclear(register struct ch11 *ch) static void ch_clear(register struct ch11 *ch) { - chudp_stop(ch); /* Kill CHUDP process, ready line going down */ + chaos_stop(ch); /* Kill CHAOS process, ready line going down */ ch->ch_outactf = TRUE; /* Initialise to true */ @@ -732,13 +805,14 @@ static dvureg_t ch11_read(struct device *d, register uint18 addr) { register struct ch11 *ch = (struct ch11 *)d; + struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; 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); + chaos_start(ch); switch (addr) { /* case UB_CHWBF: /* Write buffer (write only) = CHMYN */ @@ -759,7 +833,7 @@ ch11_read(struct device *d, register uint18 addr) 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 ((ch->ch_iptr - ch->ch_rbuf) >= (ch->ch_rcnt + dpc->dpchaos_inoff)) { // DPCHUDP_DATAOFFSET if (DVDEBUG(ch) > 4) fprintf(DVDBF(ch), "[CH11 reading last word, clearing RDN]\r\n"); ch->ch_rcnt = -1; /* read last word */ @@ -782,16 +856,16 @@ ch11_read(struct device *d, register uint18 addr) 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; + len = (ch->ch_optr - ch->ch_sbuf)-dpc->dpchaos_outoff+2; // DPCHUDP_DATAOFFSET /* 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); + cks = ch_checksum(&ch->ch_sbuf[dpc->dpchaos_outoff],len); // DPCHUDP_DATAOFFSET *(ch->ch_optr++) = cks >> 8; *(ch->ch_optr++) = cks & 0xff; - chudp_outxfer(ch); /* Send it to DP */ + chaos_outxfer(ch); /* Send it to DP */ } else panic("ch11_read: no output pointer available at CHXMT"); val = (dvureg_t) ch->ch_myaddr; @@ -816,7 +890,7 @@ ch11_write(struct device *d, uint18 addr, register dvureg_t val) 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); + chaos_start(ch); switch (addr) { /* case UB_CHMYN: /* My chaos address (read only) = CHWBF */ @@ -868,7 +942,7 @@ ch11_write(struct device *d, uint18 addr, register dvureg_t val) } if (val & CH_TCL) { /* Clear the transmitter, making it ready */ /* AIM628: stops transmitter and sets TDN */ - /* #### do we need to "stop transmitter" (dpchudp)? */ + /* #### do we need to "stop transmitter" (dpchaos)? */ val &= ~CH_TCL; val |= CH_TDN; /* Done below */ @@ -923,8 +997,10 @@ static void ch_oint(register struct ch11 *ch) { if (REG(ch) & CH_TEN) { +#if 0 // too much noise if (DVDEBUG(ch)) fprintf(DVDBF(ch), "[CH11: output int]\r\n"); +#endif ch->ch_opireq = TRUE; (*ch->ch_dv.dv_pifun)(&ch->ch_dv, /* Put up interrupt */ (int)ch->ch_dv.dv_brlev); @@ -945,20 +1021,20 @@ ch_iint(register struct ch11 *ch) } } -/* Activate input side - allow CHUDP input to be received and processed. +/* Activate input side - allow CHAOS 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! */ + if (chaos_incheck(ch)) { /* Do initial check for input */ + chaos_inxfer(ch); /* Have input! Go snarf it! */ ch_idone(ch); /* Finish up CH input done */ } } -/* CH input done - called to finish up CHUDP input +/* CH input done - called to finish up CHAOS input */ static void ch_idone(register struct ch11 *ch) @@ -971,11 +1047,13 @@ ch_idone(register struct ch11 *ch) static int ch_outcheck(register struct ch11 *ch) { + struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; + /* check if there's some output to send */ - return ch->ch_outactf && (((ch->ch_optr - ch->ch_sbuf) - DPCHUDP_DATAOFFSET) > 0); + return ch->ch_outactf && (((ch->ch_optr - ch->ch_sbuf) - dpc->dpchaos_outoff) > 0); // DPCHUDP_DATAOFFSET } -/* Activate output side - send a message to the CHUDP. +/* Activate output side - send a message to the CHAOS. ** If can't do it because an outbound message is already in progress, ** complain and cause an error. */ @@ -983,7 +1061,7 @@ static void ch_ogo(register struct ch11 *ch) { if (ch_outcheck(ch)) { - if (!chudp_outxfer(ch)) { + if (!chaos_outxfer(ch)) { /* Couldn't output, so abort and done immediately */ REG(ch) |= (CH_TAB|CH_TDN); /* #### */ if (DVDEBUG(ch)) @@ -991,14 +1069,14 @@ ch_ogo(register struct ch11 *ch) } ch_oint(ch); return; - /* DPCHUDP will call ch_evhsdon() when ready for more output. */ + /* DPCHAOS 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 +/* CH output done - called to finish up CHAOS output */ static void ch_odone(register struct ch11 *ch) @@ -1008,11 +1086,12 @@ ch_odone(register struct ch11 *ch) ch_oint(ch); /* Send output interrupt! */ } -/* VIRTUAL CHUDP ROUTINES +/* VIRTUAL CHAOS ROUTINES */ /* Utility routine */ +#if 0 static void showpkt(FILE *f, char *id, unsigned char *buf, int cnt) { @@ -1035,6 +1114,85 @@ showpkt(FILE *f, char *id, unsigned char *buf, int cnt) fprintf(f, "%s\r\n", linbuf); } } +#else +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"; + } + +char * +ch_char(unsigned char x, char *buf) { + if (x < 32) + sprintf(buf,"^%c", x+64); + else if (x == 127) + sprintf(buf,"^?"); + else if (x < 127) + sprintf(buf,"%2c",x); + else + sprintf(buf,"%2x",x); + return buf; +} + +static void +showpkt(FILE *f, char *id, unsigned char *ucp, int cnt) +{ + int i, row; + char b1[3],b2[3]; + + fprintf(stderr,"%s pkt dump, len %d\r\n", id, cnt); + fprintf(stderr,"Opcode: %#o (%s), unused: %o\r\nFC: %d., Nbytes %d.\r\n", + ucp[0], ch_opcode(ucp[0]), + ucp[1], ucp[2]>>4, ((ucp[2]&0xf)<<4) | ucp[3]); + fprintf(stderr,"Dest host: %#o, index %#o\r\nSource host: %#o, index %#o\r\n", + (ucp[4]<<8)|ucp[5], (ucp[6]<<8)|ucp[7], + (ucp[8]<<8)|ucp[9], (ucp[10]<<8)|ucp[11]); + fprintf(stderr,"Packet #%o\r\nAck #%o\r\n", + (ucp[12]<<8)|ucp[13], (ucp[14]<<8)|ucp[15]); + fprintf(stderr,"Data:\r\n"); + + /* Skip headers */ + ucp += CHAOS_HEADERSIZE; + /* Show only data portion */ + cnt -= 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 1 + for (i = 0; (i < 8) && (i+row*8 < cnt); i++) { + fprintf(stderr, " %2s", ch_char(ucp[i+row*8], (char *)&b1)); + fprintf(stderr, "%2s", ch_char(ucp[(++i)+row*8], (char *)&b2)); + } + fprintf(stderr, " (chars)\r\n"); + for (i = 0; (i < 8) && (i+row*8 < cnt); i++) { + fprintf(stderr, " %2s", ch_char(ucp[i+1+row*8], (char *)&b1)); + fprintf(stderr, "%2s", ch_char(ucp[(i++)+row*8], (char *)&b2)); + } + 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: %#x\r\n", + (ucp[cnt]<<8)|ucp[cnt+1],(ucp[cnt+2]<<8)|ucp[cnt+3],(ucp[cnt+4]<<8)|ucp[cnt+5]); +} + +#endif static void ch_evhrwak(struct device *d, struct dvevent_s *evp); @@ -1042,17 +1200,17 @@ static void ch_evhsdon(struct device *d, struct dvevent_s *evp); /* static */ int -chudp_init(register struct ch11 *ch, FILE *of) +chaos_init(register struct ch11 *ch, FILE *of) { - register struct dpchudp_s *dpc; + register struct dpchaos_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"); + if (!dp_init(&ch->ch_dp, sizeof(struct dpchaos_s), + DP_XT_MSIG, SIGUSR1, (size_t)CHAOSBUFSIZ, /* in */ + DP_XT_MSIG, SIGUSR1, (size_t)CHAOSBUFSIZ)) { /* out */ + if (of) fprintf(of, "CH11 subproc init failed!\n"); return FALSE; } ch->ch_sbuf = dp_xsbuff(&(ch->ch_dp.dp_adr->dpc_todp), &junk); @@ -1060,35 +1218,50 @@ chudp_init(register struct ch11 *ch, FILE *of) 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 */ + /* Set up DPCHAOS-specific part of shared DP memory */ + dpc = (struct dpchaos_s *) ch->ch_dp.dp_adr; + dpc->dpchaos_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->dpchaos_dpc.dpc_flags |= DPCF_MEMLOCK; - dpc->dpchudp_ver = DPCHUDP_VERSION; - dpc->dpchudp_attrs = 0; + dpc->dpchaos_ver = DPCHAOS_VERSION; + dpc->dpchaos_attrs = 0; - dpc->dpchudp_backlog = ch->ch_backlog; /* Pass on backlog value */ - dpc->dpchudp_dedic = ch->ch_dedic; /* Pass on dedicated flag */ + dpc->dpchaos_backlog = ch->ch_backlog; /* Pass on backlog value */ + dpc->dpchaos_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); + strncpy(dpc->dpchaos_ifnam, ch->ch_ifnam, sizeof(dpc->dpchaos_ifnam)-1); else - dpc->dpchudp_ifnam[0] = '\0'; /* No specific interface */ + dpc->dpchaos_ifnam[0] = '\0'; /* No specific interface */ - dpc->dpchudp_myaddr = ch->ch_myaddr; /* Set our Chaos address */ - dpc->dpchudp_port = ch->ch_chudp_port; - + if (ch->ch_ifmeth) { /* Pass on interface access method */ + strncpy(dpc->dpchaos_ifmeth, ch->ch_ifmeth, sizeof(dpc->dpchaos_ifmeth)-1); + if (strcasecmp(dpc->dpchaos_ifmeth, "chudp") == 0) + dpc->dpchaos_ifmeth_chudp = 1; + else if (strcasecmp(dpc->dpchaos_ifmeth, "pcap") == 0) + dpc->dpchaos_ifmeth_chudp = 0; + else { + if (of) fprintf(of,"CH11: unsupported ifmeth '%s' (must be chudp or pcap)\n", + dpc->dpchaos_ifmeth); + return FALSE; + } + } + else + dpc->dpchaos_ifmeth[0] = '\0'; /* No specific access method */ + + dpc->dpchaos_myaddr = ch->ch_myaddr; /* Set our Chaos address */ + + dpc->dpchaos_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, + memset(&dpc->dpchaos_chip_tbl[junk], 0, sizeof(struct dpchaos_chip)); + dpc->dpchaos_chip_tbl[junk].dpchaos_chip_chaddr = ch->ch_chip_tbl[junk].ch_chip_chaddr; + dpc->dpchaos_chip_tbl[junk].dpchaos_chip_ipport = ch->ch_chip_tbl[junk].ch_chip_ipport; + memcpy(&dpc->dpchaos_chip_tbl[junk].dpchaos_chip_ipaddr, &ch->ch_chip_tbl[junk].ch_chip_ipaddr, sizeof(struct in_addr)); } - dpc->dpchudp_chip_tlen = ch->ch_chip_tlen; + dpc->dpchaos_chip_tlen = ch->ch_chip_tlen; /* Register ourselves with main KLH10 loop for DP events */ @@ -1096,7 +1269,7 @@ chudp_init(register struct ch11 *ch, FILE *of) 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"); + if (of) fprintf(of, "CH11 event reg failed!\n"); return FALSE; } @@ -1104,19 +1277,19 @@ chudp_init(register struct ch11 *ch, FILE *of) 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"); + if (of) fprintf(of, "CH11 event reg failed!\n"); return FALSE; } return TRUE; } static int -chudp_start(register struct ch11 *ch) +chaos_start(register struct ch11 *ch) { register int res; if (DVDEBUG(ch)) - fprintf(DVDBF(ch), "[chudp_start: starting DP \"%s\"...", + fprintf(DVDBF(ch), "[chaos_start: starting DP \"%s\"...", ch->ch_dpname); /* HORRIBLE UGLY HACK: for AXP OSF/1 and perhaps other systems, @@ -1140,7 +1313,7 @@ chudp_start(register struct ch11 *ch) if (DVDEBUG(ch)) fprintf(DVDBF(ch), " failed!]\r\n"); else - fprintf(DVDBF(ch), "[chudp_start: Start of DP \"%s\" failed!]\r\n", + fprintf(DVDBF(ch), "[chaos_start: Start of DP \"%s\" failed!]\r\n", ch->ch_dpname); return FALSE; } @@ -1153,14 +1326,14 @@ chudp_start(register struct ch11 *ch) return TRUE; } -/* CHUDP_STOP - Stops CHUDP and drops Host Ready by killing CHUDP subproc, +/* CHAOS_STOP - Stops CHAOS and drops Host Ready by killing CHAOS subproc, ** but allow restarting. */ static void -chudp_stop(register struct ch11 *ch) +chaos_stop(register struct ch11 *ch) { if (DVDEBUG(ch)) - fprintf(DVDBF(ch), "[CHUDP: stopping..."); + fprintf(DVDBF(ch), "[CH11: stopping..."); dp_stop(&ch->ch_dp, 1); /* Say to kill and wait 1 sec for synch */ @@ -1170,13 +1343,13 @@ chudp_stop(register struct ch11 *ch) } -/* CHUDP_KILL - Kill CHUDP process permanently, no restart. +/* CHAOS_KILL - Kill CHAOS process permanently, no restart. */ static void -chudp_kill(register struct ch11 *ch) +chaos_kill(register struct ch11 *ch) { if (DVDEBUG(ch)) - fprintf(DVDBF(ch), "[CHUDP kill]\r\n"); + fprintf(DVDBF(ch), "[CH11 kill]\r\n"); ch->ch_dpstate = FALSE; (*ch->ch_dv.dv_evreg)( /* Flush all event handlers for device */ @@ -1202,16 +1375,16 @@ ch_evhrwak(struct device *d, struct dvevent_s *evp) 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 + /* Always check CHAOS 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 (chaos_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! */ + chaos_inxfer(ch); /* Have input! Go snarf it! */ ch_idone(ch); /* Finish up CH input done */ } } @@ -1232,29 +1405,29 @@ ch_evhsdon(struct device *d, struct dvevent_s *evp) } -/* Start CHUDP output. +/* Start CHAOS output. */ static int -chudp_outxfer(register struct ch11 *ch) +chaos_outxfer(register struct ch11 *ch) { register int cnt; register struct dpx_s *dpx = dp_dpxto(&ch->ch_dp); + struct dpchaos_s *dpc = (struct dpchaos_s *)ch->ch_dp.dp_adr; /* 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"); + fprintf(DVDBF(ch), "[CH11: 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]; + int chlen = ((ch->ch_sbuf[dpc->dpchaos_outoff+2] & 0xf) << 4) | ch->ch_sbuf[dpc->dpchaos_outoff+3]; // DPCHUDP_DATAOFFSET #endif - cnt = (ch->ch_optr - ch->ch_sbuf) - DPCHUDP_DATAOFFSET; + cnt = (ch->ch_optr - ch->ch_sbuf) - dpc->dpchaos_outoff; // 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", @@ -1264,41 +1437,39 @@ chudp_outxfer(register struct ch11 *ch) 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; + showpkt(DVDBF(ch), "PKTOUT", ch->ch_sbuf + dpc->dpchaos_outoff, cnt); // DPCHUDP_DATAOFFSET REG(ch) &= ~(CH_TAB|CH_TDN); /* Not done yet, not aborted */ - dp_xsend(dpx, DPCHUDP_SPKT, (size_t)cnt + DPCHUDP_DATAOFFSET); + dp_xsend(dpx, DPCHAOS_SPKT, (size_t)cnt + dpc->dpchaos_outoff); // DPCHUDP_DATAOFFSET if (DVDEBUG(ch)) - fprintf(DVDBF(ch), "[CHUDP: Out %d]\r\n", cnt); + fprintf(DVDBF(ch), "[CH11: Out %d]\r\n", cnt); return 1; } static int -chudp_incheck(register struct ch11 *ch) +chaos_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: + case DPCHAOS_INIT: ch->ch_dpstate = TRUE; dp_xrdone(dpx); /* ACK it */ return 0; /* No actual input */ - case DPCHUDP_RPKT: /* Input packet ready! */ + case DPCHAOS_RPKT: /* Input packet ready! */ if (DVDEBUG(ch)) - fprintf(DVDBF(ch), "[CHUDP: inbuf %ld]\r\n", + fprintf(DVDBF(ch), "[CH11: inbuf %ld]\r\n", (long) dp_xrcnt(dpx)); return 1; default: if (DVDEBUG(ch)) - fprintf(DVDBF(ch), "[CHUDP: R %d flushed]", dp_xrcmd(dpx)); + fprintf(DVDBF(ch), "[CH11: R %d flushed]", dp_xrcmd(dpx)); dp_xrdone(dpx); /* just ACK it */ return 0; } @@ -1308,30 +1479,30 @@ chudp_incheck(register struct ch11 *ch) } -/* CHUDP_INXFER - CHUDP Input. +/* CHAOS_INXFER - CHAOS 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) +chaos_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 struct dpchaos_s *dpc = (struct dpchaos_s *) ch->ch_dp.dp_adr; register unsigned char *pp; - /* Assume this is ONLY called after verification by chudp_incheck that + /* Assume this is ONLY called after verification by chaos_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; +/* cnt -= dpc->dpchaos_inoff; */ + pp = ch->ch_rbuf + dpc->dpchaos_inoff; if (DVDEBUG(ch)) - fprintf(DVDBF(ch), "[CHUDP: In %d]\r\n", cnt); + fprintf(DVDBF(ch), "[CH11: In %d]\r\n", cnt); if (DVDEBUG(ch) & DVDBF_DATSHO) /* Show data? */ showpkt(DVDBF(ch), "PKTIN ", pp, cnt); @@ -1344,18 +1515,29 @@ chudp_inxfer(register struct ch11 *ch) ch->ch_iptr = pp; /* check hw trailer: dest, checksum */ - if ((((pp[cnt-6]<<8) | pp[cnt-5]) != ch->ch_myaddr)) { + // byte order warning... + u_short chlen = ((pp[2] & 0xf) << 4) | pp[3]; + u_short trdest = ((pp[cnt-6]<<8) | pp[cnt-5]); + u_short hddest = (pp[4] << 8) | pp[5]; + u_short cksm = (pp[cnt-2]<<8) | pp[cnt-1]; + + if (DVDEBUG(ch) && (CHAOS_HEADERSIZE + chlen + CHAOS_HW_TRAILERSIZE != cnt)) { + fprintf(DVDBF(ch), "[CH11: expected len %d+%d+%d = %d, got %d]\r\n", + CHAOS_HEADERSIZE , chlen , CHAOS_HW_TRAILERSIZE, + CHAOS_HEADERSIZE + chlen + CHAOS_HW_TRAILERSIZE, cnt); + } + // be conservative in what you generate, and liberal in what you accept + if ((trdest != 0) && (trdest != ch->ch_myaddr) && (hddest != 0) && (hddest != ch->ch_myaddr) && !(REG(ch) & CH_SPY)) { if (DVDEBUG(ch)) - fprintf(DVDBF(ch), "[CHUDP: not for my address: destination %o]\r\n", - ((pp[cnt-6]<<8) | pp[cnt-5])); + fprintf(DVDBF(ch), "[CH11: not for my address: trailer dest %#o, header dest %#o]\r\n", trdest, hddest); } 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 + if (cksm != 0) { + cks = ch_checksum(pp,cnt); + if (cks != 0) { + if (DVDEBUG(ch)) + fprintf(DVDBF(ch), "[CH11: bad checksum 0x%x]\r\n", cks); + REG(ch) |= CH_ERR; + } } REG(ch) |= CH_RDN; /* Note it's done! */ } diff --git a/src/dvch11.h b/src/dvch11.h index 5e671f8..3084783 100644 --- a/src/dvch11.h +++ b/src/dvch11.h @@ -37,11 +37,11 @@ extern struct device *dvch11_init(FILE *, char *); /* CH11 addresses & assignments for KS10: CH11 Address Vector UBA# BR-Level - #1 0764140 0270 3 4 or 5 (dunno which) + #1 0764140 0270 3 4 or 5 (dunno which) [6, according to CHXCHN in ITS] etc(?) (some) 0230 */ -#define UB_CH11_BR 5 +#define UB_CH11_BR 6 /* See CHXCHN in ITS */ #define UB_CH11_VEC 0270 /* CH11 Interrupt Vector */ #define UB_CH11 0764140 /* CH11 Unibus Address (on UBA #3) */ diff --git a/src/dvch11.txt b/src/dvch11.txt index e893deb..51c49aa 100644 --- a/src/dvch11.txt +++ b/src/dvch11.txt @@ -6,73 +6,75 @@ 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. +device, which supports Chaosnet-on-Ethernet and Chaosnet-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! +Latest update: 2018-04-15 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. +data. It can do it in two ways: + +Firstly, by implementing Chaosnet over Ethernet (protocol nr 0x0804) and handling +ARP for that protocol (cf RFC 826). This uses one of the packet filtering +implementations (currently only pcap) provided by osdnet. (Only Ethernet II +headers are supported, not 802.3.) +No routing is handled, that's done by ITS - this code uses the +Chaosnet trailer provided by ITS to decide where to send pkts. +(On Ethernet, no Chaosnet trailer is included.) + +Secondly, it can do it by using a Chaos-over-UDP tunnel. 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. +Configuration parameters: -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). +The basic configuration parameters: + devdef chaos ub3 ch11 addr=764140 br=6 vec=270 -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). +ITS seems to think the "br" param should be 6, not 5 as used in the +old dummy device definition (see CHXCHN in ITS). + +More parameters: + + myaddr=oooo + defines the octal Chaosnet address of the interface. + This needs to match the address configured in ITS (see MYCHAD + in SYSTEM;CONFIG >) + ifmeth=meth + where meth is "chudp" or "pcap", selects the link + implementation to use (Chaos-over-UDP or Chaos-on-Ethernet). +For "chudp": + chudpport=dddddd + sets the local UDP port to use for Chaos-over-UDP. The default is 42042. + chip=oooo/hhhhhhhh + defines a remote Chaos-over-UDP host with the octal Chaos address oooo, + and the IPv4 address or hostname hhhhhhhh. If a hostname is + used, the first IPv4 address returned is used. (Rather than + listing all your friends' ITS hosts directly, I suggest joining the + Global Chaosnet and just list its central router. See + https://aosnet.ch.) + +If chudpport or chip parameters are given, the ifmeth defaults to chudp. 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.) +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. (You can avoid this feature, and potentially +filling your table, by using a firewall.) 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... +SYSEN2;MLDEV, SYSENG;@DEV, etc. *BEWARE*: If you have both CHAOSP and IMPP, you run into a bug which results in @@ -87,25 +89,11 @@ 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??? +- Implement Chaos-over-DTLS to get some security, on this modern Internet. + +More info: +- see https://aosnet.ch 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 +Bjorn Victor (bjorn@victor.se)