sources-request@mirror.UUCP (08/25/86)
Submitted by: pyramid!decwrl!imagen!geof (Geof Cooper) Mod.sources: Volume 7, Issue 1 Archive-name: tinytcp [ I have not tried to use this code. --r$ ] #!/bin/sh : "This is a shell archive, meaning: " : "1. Remove everything above the #! /bin/sh line. " : "2. Save the resulting test in a file. " : "3. Execute the file with /bin/sh (not csh) to create the files:" : " README" : " arp.c" : " sed.c" : " sed.h" : " tinyftp.c" : " tinytcp.c" : " tinytcp.h" : "This archive created: Tue Jul 22 09:59:35 PDT 1986 " echo file: README sed 's/^X//' >README << 'END-of-README' X X TinyTcp Public Domain Release X XThe files in this release contain a simple implementation of TCP & FTP, Xsuitable for burning into ROM. It is, in effect, a big hack put together Xin two or three days. It works for us, though, and you might like it, Xtoo. We use it to boot our image processor by retrieving a load file Xusing the standard FTP server. X XThe code is intended to use the buffers from the Ethernet interface, Xalthough shadow buffers could be hidden in the driver with no problem. XOn one home-brew board here and didn't support byte-mode access to it. XHence, the code takes pains to fool our local compiler into never Xgenerating byte- or bit-mode instructions. X XSince we have already burned the TCP into ROM, it is unlikely that Xany further development will take place at IMAGEN. I would be willing Xto act as a clearinghouse for future improvements to the code. X XWarning: the code was intended for a 68000, and doesn't have Xany byte swapping support in it. Shouldn't be too hard to add Xif you want to run it on a little-endian machine. X XPlease note (and honor) the copyright on each file: X X Copyright (C) 1986, IMAGEN Corporation X "This code may be duplicated in whole or in part provided that [1] there X is no commercial gain involved in the duplication, and [2] that this X copyright notice is preserved on all copies. Any other duplication X requires written notice of the author (Geoffrey H. Cooper)." X X...in other words, do what you want with the code, but don't sell it and Xgive IMAGEN and me credit on whatever you produce. If you develop a product Xbased on this code and want to sell it, give me a call and I'll be reasonable. X XThis code is distributed "as is." Neither the author nor IMAGEN Corporation Xguarantees that it works or meets any particular specifications. The act Xof distributing this software does not represent a commitment by either the Xauthor nor IMAGEN Corporation to provide maintenance or consulting services Xrelated to this software. X XBut feel free to ask me any questions that you can't answer for yourself. X X - Geof Cooper X Imagen Corporation X (408)986 9400 X [imagen!geof@decwrl.dec.com, {decwrl,saber}!imagen!geof] X April 16, 1986 X XThe package requires some system support: X X clock_ValueRough() - should be a procedure that returns the current X value of a millisecond clock. The procedure is called frequently, X so that interrupts are not needed to service the clock. Our X implementation polls the real time timer and assumes that it is X called frequently enough so that it doesn't miss clock ticks (Since X the timer is only used for network timeouts, it doesn't really matter X if it does miss clock ticks, of course). Systems without a clock X could probably get by with a procedure that increments a static X variable and returns it, by adjusting the timeout constants in the X program. X X Network driver - some network interface driver is needed. A driver for a X 3Com multibus (ethernet) board is included; this board isn't made X anymore (at least not by 3Com, there are still compatible boards X being made, but I don't know of any being popularly distributed), X so you'll probably need to write a driver for the board in your system. X X XGuide to source files: X X sed.c - Simple Ethernet Driver - Driver for 3Com multibus card. If you X have another type of Ethernet board, you can use this driver as X a template. X X sed.h - header file for the above. X X arp.c - Implementation of Address Resolution Protocol. Note that there X is no arp "mapping" per se. The higher level code (tcp, in this X case) is required to keep track of internet and ethernet addresses. X X tinytcp.c - Implementation of TCP. X X tinytcp.h - Header file for above, and for everything else. X X tinyftp.c - Implementation of FTP, only allows files to be retrieved, X not sent. END-of-README echo file: arp.c sed 's/^X//' >arp.c << 'END-of-arp.c' X/* X * SAR: Simple Address Resolution Protocol Implementation X * Written by Geoffrey Cooper, September 27, 1983 X * X * This package implements a very simple version of the Plummer Address X * Resolution Protocol (RFC 826). It allows clients to resolve Internet X * addresses into Ethernet addresses, and knows how to respond to an X * address resolution request (when the transmit buffer is free). X * X * Routines: X * X * sar_CheckPacket( pb ) => 1, if ARP packet and processed, 0 otherwise X * sar_MapIn2Eth( ina, ethap ) => 1 if did it, 0 if couldn't. X * X * Copyright (C) 1983, 1986 IMAGEN Corporation X * "This code may be duplicated in whole or in part provided that [1] there X * is no commercial gain involved in the duplication, and [2] that this X * copyright notice is preserved on all copies. Any other duplication X * requires written notice of the author (Geoffrey H. Cooper)." X * X */ X#include "tinytcp.h" X Xsar_CheckPacket(ap) X register arp_Header *ap; X{ X register arp_Header *op; X X if ( ap->hwType != arp_TypeEther || /* have ethernet hardware, */ X ap->protType != 0x800 || /* and internet software, */ X ap->opcode != ARP_REQUEST || /* and be a resolution req. */ X ap->dstIPAddr != sin_lclINAddr /* for my addr. */ X ) return ( 0 ); /* .... or we ignore it. */ X X /* format response. */ X op = (arp_Header *)sed_FormatPacket(ap->srcEthAddr, 0x806); X op->hwType = arp_TypeEther; X op->protType = 0x800; X op->hwProtAddrLen = (sizeof(eth_HwAddress) << 8) + sizeof(in_HwAddress); X op->opcode = ARP_REPLY; X op->srcIPAddr = sin_lclINAddr; X MoveW(sed_lclEthAddr, op->srcEthAddr, sizeof(eth_HwAddress)); X ap->dstIPAddr = op->srcIPAddr; X MoveW(ap->srcEthAddr, op->dstEthAddr, sizeof(eth_HwAddress)); X X sed_Send(sizeof(arp_Header)); X X return ( 1 ); X} X X/* X * Do an address resolution bit. X */ Xsar_MapIn2Eth(ina, ethap) X longword ina; X eth_HwAddress *ethap; X{ X register arp_Header *op; X extern in_HwAddress sin_lclINAddr; X register i; X longword endTime; X longword rxMitTime; X X sed_Receive( 0 ); X endTime = clock_ValueRough() + 2000; X while ( endTime > clock_ValueRough() ) { X op = (arp_Header *)sed_FormatPacket(&sed_ethBcastAddr[0], 0x806); X op->hwType = arp_TypeEther; X op->protType = 0x800; X op->hwProtAddrLen = (sizeof(eth_HwAddress) << 8) + sizeof(in_HwAddress); X op->opcode = ARP_REQUEST; X op->srcIPAddr = sin_lclINAddr; X MoveW(sed_lclEthAddr, op->srcEthAddr, sizeof(eth_HwAddress)); X op->dstIPAddr = ina; X X /* ...and send the packet */ X sed_Send( sizeof(arp_Header) ); X X rxMitTime = clock_ValueRough() + 250; X while ( rxMitTime > clock_ValueRough() ) { X op = (arp_Header *)sed_IsPacket(); X if ( op ) { X if ( sed_CheckPacket(op, 0x806) == 1 && X op->protType == 0x800 && X op->srcIPAddr == ina && X op->opcode == ARP_REPLY ) { X MoveW(op->srcEthAddr, ethap, sizeof(eth_HwAddress)); X return ( 1 ); X } X sed_Receive(op); X } X } X } X return ( 0 ); X} END-of-arp.c echo file: sed.c sed 's/^X//' >sed.c << 'END-of-sed.c' X/* X * Ethernet Driver. X * A Very Simple set of ethernet driver primitives. The ethernet (3com Mbus) X * interface is controlled by busy-waiting, the application is handed the X * location of on-board packet buffers, and allowed to fill in the X * transmit buffer directly. The interface is entirely blocking. X * X * Written March, 1986 by Geoffrey Cooper X * X * Copyright (C) 1986, IMAGEN Corporation X * "This code may be duplicated in whole or in part provided that [1] there X * is no commercial gain involved in the duplication, and [2] that this X * copyright notice is preserved on all copies. Any other duplication X * requires written notice of the author (Geoffrey H. Cooper)." X * X * Primitives: X * sed_Init() -- Initialize the package X * sed_FormatPacket( destEAddr ) => location of transmit buffer X * sed_Send( pkLength ) -- send the packet that is in the transmit buffer X * sed_Receive( recBufLocation ) -- enable receiving packets. X * sed_IsPacket() => location of packet in receive buffer X * sed_CheckPacket( recBufLocation, expectedType ) X * X * Global Variables: X * sed_lclEthAddr -- Ethernet address of this host. X * sed_ethBcastAddr -- Ethernet broadcast address. X */ X X#include "tinytcp.h" X#include "sed.h" X X#define en10pages ((en10size) >> pageshift) X Xoctet *sed_va; /* virtual address of ethernet card */ Xeth_HwAddress sed_lclEthAddr; /* local ethernet address */ Xeth_HwAddress sed_ethBcastAddr; /* Ethernet broadcast address */ XBOOL sed_respondARPreq; /* controls responses to ARP req's */ Xchar bufAinUse, bufBinUse; /* tell whether bufs are in use */ X X/* X * Initialize the Ethernet Interface, and this package. Enable input on X * both buffers. X */ Xsed_Init() X{ X int recState; X register i, j; X X recState = 7; /* == mine + broad - errors */ X X /* Map the Ethernet Interface in, and initialize sed_va */ X sed_va = (octet *)SED3CVA; /* our virtual addr */ X X /* Map memory for 3Com board (must be 8k boundary) */ X /* INSERT CODE HERE */ X map_ethernet_board(); X X /* Reset the Ethernet controller */ X MECSR(sed_va) = RESET; X for (i=0; i<10; i++); /* delay a bit... */ X X /* just copy on-board ROM to on-board RAM, to use std. address */ X Move(MEAROM(sed_va), sed_lclEthAddr, 6); X Move(sed_lclEthAddr, MEARAM(sed_va), 6); X X MECSR(sed_va) |= AMSW; /* and tell board we did it */ X X /* X * and initialize the exported variable which gives the Eth broadcast X * address, for everyone else to use. X */ X for (i=0; i<3; i++) sed_ethBcastAddr[i] = 0xFFFF; X X /* accept packets addressed for us and broadcast packets */ X X MECSR(sed_va) = (MECSR(sed_va)&~PA) | recState; X X /* declare both buffers in use... */ X bufAinUse = bufBinUse = TRUE; X X} X X/* X * Format an ethernet header in the transmit buffer, and say where it is. X * Note that because of the way the 3Com interface works, we need to know X * how long the packet is before we know where to put it. The solution is X * that we format the packet at the BEGINNING of the transmit buffer, and X * later copy it (carefully) to where it belongs. Another hack would be X * to be inefficient about the size of the packet to be sent (always send X * a larger ethernet packet than you need to, but copying should be ok for X * now. X */ Xoctet * Xsed_FormatPacket( destEAddr, ethType ) X register octet *destEAddr; X{ X register octet *xMitBuf; X X xMitBuf = &((octet *)MEXBUF(sed_va))[-0x800]; X Move( destEAddr, xMitBuf, 6 ); X Move( sed_lclEthAddr, xMitBuf + 6, 6 ); X *((short *)(xMitBuf+12)) = ethType; X return ( xMitBuf+14 ); X} X X/* X * Send a packet out over the ethernet. The packet is sitting at the X * beginning of the transmit buffer. The routine returns when the X * packet has been successfully sent. X */ Xsed_Send( pkLengthInOctets ) X register int pkLengthInOctets; X{ X register octet *fromO, *toO; X register pkLength; X register csr; X X pkLengthInOctets += 14; /* account for Ethernet header */ X pkLengthInOctets = (pkLengthInOctets + 1) & (~1); X X if (pkLengthInOctets < E10P_MIN) X pkLengthInOctets = E10P_MIN; /* and min. ethernet len */ X X /* and copy the packet where it belongs */ X pkLength = pkLengthInOctets; X fromO = &((octet *)MEXBUF(sed_va))[-0x800] + pkLength; X toO = ((octet *)MEXBUF(sed_va)); X X while ( pkLength-- ) *--toO = *--fromO; X X /* send the packet */ X X MEXHDR(sed_va) = 2048 - pkLengthInOctets; X MECSR(sed_va) |= TBSW; X X /* and wait until it has really been sent. */ X X for (pkLength=0; pkLength < 15; pkLength++) { X while ( (! ((csr = MECSR(sed_va)) & JAM)) && (csr & TBSW) ) X ; X if (csr & JAM ) { X /* Ethernet Collision detected... */ X#ifdef DEBUG X printf("sed: JAM: MECSR=%x\n", csr); X#endif X MEBACK(sed_va) = clock_ValueRough(); X MECSR(sed_va) |= JAM; X } else break; X } X if ( pkLength == 15 ) SysBug("Go and Buy a New Ethernet Interface."); X X /* else we sent the packet ok. */ X} X X/* X * Enable the Ethernet interface to receive packets from the network. If X * the argument is zero, enable both buffers. If the argument is nonzero, X * take it as the address of the buffer to be enabled. X */ Xsed_Receive( recBufLocation ) X octet *recBufLocation; X{ X word enables = 0; X X if (recBufLocation == 0) { X bufAinUse = FALSE; X bufBinUse = FALSE; X enables = (ABSW | BBSW); X } X recBufLocation -= 16; X if (recBufLocation == ((octet *)MEAHDR(sed_va))) { X bufAinUse = FALSE; X enables = ABSW; X } X if (recBufLocation == ((octet *)MEBHDR(sed_va))) { X bufBinUse = FALSE; X enables = BBSW; X } X X MECSR (sed_va) |= enables; X} X X/* X * Test for the arrival of a packet on the Ethernet interface. The packet may X * arrive in either buffer A or buffer B; the location of the packet is X * returned. If no packet is returned withing 'timeout' milliseconds, X * then the routine returns zero. X * X * Note: ignores ethernet errors. may occasionally return something X * which was received in error. X */ X Xoctet * Xsed_IsPacket() X{ X register oldStatus; X register octet *pb; X X pb = 0; X if ( ! bufAinUse && (MECSR(sed_va)&ABSW) == 0 ) X pb = (octet *)MEAHDR(sed_va); X if ( ! pb && ! bufBinUse && (MECSR(sed_va)&BBSW) == 0 ) X pb = (octet *)MEBHDR(sed_va); X X if ( pb ) { X if ( ((octet *)pb) == ((octet *)MEAHDR(sed_va)) ) bufAinUse = 1; X else bufBinUse = 1; X pb += 16; /* get past the ethernet header */ X } X X return ( pb ); X} X X/* X * Check to make sure that the packet that you received was the one that X * you expected to get. X */ Xsed_CheckPacket( recBufLocation, expectedType ) X word *recBufLocation; X word expectedType; X{ X register recHeader = recBufLocation[-8]; X if ( (recHeader&R_ERROR) != 0 || X (recHeader&R_OFFSET) < E10P_MIN ) { X return ( -1 ); X } X if ( recBufLocation[-1] != expectedType ) { X return ( 0 ); X } X return (1); X} END-of-sed.c echo file: sed.h sed 's/^X//' >sed.h << 'END-of-sed.h' X/* X * Header file for very simple ethernet driver, based on 3Com Multibus X * board. X * X * Copyright (C) 1986, IMAGEN Corporation X * "This code may be duplicated in whole or in part provided that [1] there X * is no commercial gain involved in the duplication, and [2] that this X * copyright notice is preserved on all copies. Any other duplication X * requires written notice of the author (Geoffrey H. Cooper)." X */ X X#define en10size (8*1024) /* size of interface memory */ X#define en10pages ((en10size) >> pageshift) X#define E10P_MIN 60 /* Minimum Ethernet packet size */ X X/* X * The position of the 3Com interface in virtual memory. If we're X * Running the bootloader function, then it must be in the last 8k X * of virtual addresses. X */ X#ifdef BOOTLOADER X#define SED3CVA vm_3ComAdr /* hack, only need pb68.h if bootloader */ X#endif X#ifndef SED3CVA X#define SED3CVA 0x1c000 X#endif X X/* 10Mb Ethernet interface addresses */ X X#define MECSR(eth_va) *(word*)(((octet *) eth_va) + 0x0) X#define MEBACK(eth_va) *(word*)(((octet *) eth_va) + 0x2) X#define MEAROM(eth_va) (word*)(((octet *) eth_va) + 0x400) X#define MEARAM(eth_va) (word*)(((octet *) eth_va) + 0x600) X#define MEXHDR(eth_va) *(word*)(((octet *) eth_va) + 0x800) X#define MEXBUF(eth_va) (word*)(((octet *) eth_va) + 0x1000) X#define MEAHDR(eth_va) (word*)(((octet *) eth_va) + 0x1000) X#define MEBHDR(eth_va) (word*)(((octet *) eth_va) + 0x1800) X X/* control/status register fields */ X X#define BBSW 0x8000 /* Buffer B belongs to Network */ X#define ABSW 0x4000 /* Buffer A belongs to Network */ X#define TBSW 0x2000 /* Transmit buffer belongs to Network */ X#define JAM 0x1000 /* Set when transmit collision */ X#define AMSW 0x0800 /* X#define RBBA 0x0400 /* Oldest received packet is in B */ X/*#define UNUSED 0x0200 */ X#define RESET 0x0100 /* Reset the controller */ X#define BINT 0x0080 /* Interrupt when BBSW=>0 (packet in B) */ X#define AINT 0x0040 /* Interrupt when ABSW=>0 (packet in A) */ X#define TINT 0x0020 /* Interrupt when TBSW=>0 (transmit done) */ X#define JINT 0x0010 /* Enable interrupts when JAM=>1 */ X#define PA 0x000F /* Which packets should be received? */ X#define INTENABLS 0x00F0 X X/* X * Receiver Header Fields: X * The receiver header is the first (short) word of the receive buffer. It X * includes such information as how big the packet is, whether it was a X * broadcast, whether there was an error in receiving it, etc. X */ X X#define R_FCS 0x8000 /* fcs error */ X#define R_BCAST 0x4000 /* packet was NOT a broadcast */ X#define R_RANGE 0x2000 /* range error (size of pkt?) */ X#define R_MATCH 0x1000 /* packet is multicast (i.e., address X received is not that of the interface) */ X#define R_FRAME 0x0800 /* framing error */ X#define R_ERROR 0x8800 /* was there any error */ X#define R_OFFSET 0x07FF /* packet length + 1 word */ X Xextern octet *sed_FormatPacket(), *sed_WaitPacket(); X X#ifdef BOOTLOADER X#define ConsPrintf printf X#endif END-of-sed.h echo file: tinyftp.c sed 's/^X//' >tinyftp.c << 'END-of-tinyftp.c' X/* X * tinyftp.c - user ftp built on tinytcp.c X * X * Written March 31, 1986 by Geoffrey Cooper X * X * Copyright (C) 1986, IMAGEN Corporation X * "This code may be duplicated in whole or in part provided that [1] there X * is no commercial gain involved in the duplication, and [2] that this X * copyright notice is preserved on all copies. Any other duplication X * requires written notice of the author (Geoffrey H. Cooper)." X */ X#include "tinytcp.h" X Xtcp_Socket ftp_ctl, ftp_data, ftp_data2; Xbyte ftp_cmdbuf[120]; Xint ftp_cmdbufi; X Xbyte ftp_outbuf[80]; Xint ftp_outbufix, ftp_outbuflen; X Xshort ftp_rcvState; X#define ftp_StateGETCMD 0 /* get a command from the user */ X#define ftp_StateIDLE 1 /* idle connection */ X#define ftp_StateINCOMMAND 2 /* command sent, awaiting response */ X#define ftp_StateRCVRESP 3 /* received response, need more data */ X Xchar *ftp_script[7]; Xint ftp_scriptline; Xchar ftp_retrfile[80]; XBOOL ftp_echoMode; X Xftp_ctlHandler(s, dp, len) X tcp_Socket *s; X byte *dp; X int len; X{ X byte c, *bp, data[80]; X int i; X X if ( dp == 0 ) { X tcp_Abort(&ftp_data); X return; X } X X do { X i = len; X if ( i > sizeof data ) i = sizeof data; X MoveW(dp, data, i); X len -= i; X bp = data; X while ( i-- > 0 ) { X c = *bp++; X if ( c != '\r' ) { X if ( c == '\n' ) { X ftp_cmdbuf[ftp_cmdbufi] = 0; X ftp_commandLine(); X ftp_cmdbufi = 0; X } else if ( ftp_cmdbufi < (sizeof ftp_cmdbuf)-1 ) { X ftp_cmdbuf[ftp_cmdbufi++] = c; X } X } X } X } while ( len > 0 ); X} X Xftp_commandLine() X{ X printf("> %s\n", ftp_cmdbuf); X switch(ftp_rcvState) { X case ftp_StateIDLE: X if ( ftp_cmdbuf[3] == '-' ) X ftp_rcvState = ftp_StateRCVRESP; X break; X X case ftp_StateINCOMMAND: X if ( ftp_cmdbuf[3] == '-' ) X ftp_rcvState = ftp_StateRCVRESP; X case ftp_StateRCVRESP: X if ( ftp_cmdbuf[3] == ' ' ) X ftp_rcvState = ftp_StateIDLE; X break; X } X} X Xftp_Abort() X{ X tcp_Abort(&ftp_ctl); X tcp_Abort(&ftp_data); X} X X Xftp_application() X{ X char *s; X char *dp; X int i; X X i = -1; X if ( isina() ) { X i = busyina() & 0177; X#ifdef DEBUG X if ( i == ('D' & 037) ) SysBug("Pause to DDT"); X#endif X if ( i == ('C' & 037) ) { X printf("Closing...\n"); X tcp_Close(&ftp_ctl); X } X } X X switch (ftp_rcvState) { X case ftp_StateGETCMD: X getcmd:if ( i != -1 ) { X ftp_outbuf[ftp_outbuflen] = 0; X switch (i) { X case 'H' & 037: X case 0177: X if ( ftp_outbuflen > 0 ) { X ftp_outbuflen--; X printf("\010 \010"); X } X break; X X case 'R' & 037: X if ( ftp_echoMode ) X printf("\nFtpCmd> %s", ftp_outbuf); X break; X X case 033: X ftp_echoMode = ! ftp_echoMode; X break; X X case '\r': X case '\n': X busyouta('\n'); X dp = &ftp_outbuf[ftp_outbuflen]; X goto docmd; X X default: X if ( i >= ' ' && ftp_outbuflen < sizeof ftp_outbuf ) { X ftp_outbuf[ftp_outbuflen++] = i; X if ( ftp_echoMode ) busyouta(i); X } X } X } X break; X X case ftp_StateIDLE: X if ( ftp_scriptline < 0 ) { X ftp_rcvState = ftp_StateGETCMD; X ftp_echoMode = true; X ftp_outbuflen = 0; X printf("FtpCmd> "); X goto getcmd; X } X s = ftp_script[ftp_scriptline]; X if ( s == NIL ) X break; X ftp_scriptline++; X printf("%s\n", s); X dp = ftp_outbuf; X while ( *dp++ = *s++ ) ; X dp--; X docmd: *dp++ = '\r'; X *dp++ = '\n'; X ftp_outbuflen = dp - ftp_outbuf; X ftp_outbufix = 0; X ftp_rcvState = ftp_StateINCOMMAND; X /* fall through */ X case ftp_StateINCOMMAND: X i = ftp_outbuflen - ftp_outbufix; X if ( i > 0 ) { X i = tcp_Write(&ftp_ctl, &ftp_outbuf[ftp_outbufix], i); X ftp_outbufix += i; X tcp_Flush(&ftp_ctl); X } X /* fall through */ X case ftp_StateRCVRESP: X break; X } X X} X Xftp(host, fn, dataHandler) X in_HwAddress host; X char *fn; X procref dataHandler; X{ X word port; X char filecmd[80]; X X port = (sed_lclEthAddr[2] + clock_ValueRough()) | 0x8000; X X if ( fn ) { X /* set up the script for this session */ X ftp_script[0] = "user foo"; X ftp_script[1] = "pass foo"; X ftp_script[2] = "type i"; X sprintf(filecmd, "retr %s", fn); X ftp_script[3] = filecmd; X ftp_script[4] = "quit"; X ftp_script[5] = 0; X ftp_scriptline = 0; X } else { X ftp_scriptline = -1; /* interactive mode */ X ftp_echoMode = true; X } X X /* set up state variables */ X ftp_rcvState = ftp_StateRCVRESP; X ftp_cmdbufi = 0; X tcp_Listen(&ftp_data, port, dataHandler, 0); X tcp_Open(&ftp_ctl, port, host, 21, ftp_ctlHandler); X tcp(ftp_application); X} END-of-tinyftp.c echo file: tinytcp.c sed 's/^X//' >tinytcp.c << 'END-of-tinytcp.c' X/* X * tinytcp.c - Tiny Implementation of the Transmission Control Protocol X * X * Written March 28, 1986 by Geoffrey Cooper, IMAGEN Corporation. X * X * This code is a small implementation of the TCP and IP protocols, suitable X * for burning into ROM. The implementation is bare-bones and represents X * two days' coding efforts. A timer and an ethernet board are assumed. The X * implementation is based on busy-waiting, but the tcp_handler procedure X * could easily be integrated into an interrupt driven scheme. X * X * IP routing is accomplished on active opens by broadcasting the tcp SYN X * packet when ARP mapping fails. If anyone answers, the ethernet address X * used is saved for future use. This also allows IP routing on incoming X * connections. X * X * The TCP does not implement urgent pointers (easy to add), and discards X * segments that are received out of order. It ignores the received window X * and always offers a fixed window size on input (i.e., it is not flow X * controlled). X * X * Special care is taken to access the ethernet buffers only in word X * mode. This is to support boards that only allow word accesses. X * X * Copyright (C) 1986, IMAGEN Corporation X * "This code may be duplicated in whole or in part provided that [1] there X * is no commercial gain involved in the duplication, and [2] that this X * copyright notice is preserved on all copies. Any other duplication X * requires written notice of the author (Geoffrey H. Cooper)." X */ X X#include "tinytcp.h" X X/* X * Local IP address X */ Xin_HwAddress sin_lclINAddr; X X/* X * IP identification numbers X */ Xint tcp_id; X Xtcp_Socket *tcp_allsocs; X X/* Timer definitions */ X#define tcp_RETRANSMITTIME 1000 /* interval at which retransmitter is called */ X#define tcp_LONGTIMEOUT 31000 /* timeout for opens */ X#define tcp_TIMEOUT 10000 /* timeout during a connection */ X X#ifdef DEBUG X/* X * Primitive logging facility X */ X#define tcp_LOGPACKETS 1 /* log packet headers */ Xword tcp_logState; X#endif X X/* X * Initialize the tcp implementation X */ Xtcp_Init() X{ X extern eth_HwAddress sed_lclEthAddr; X X /* initialize ethernet interface */ X sed_Init(); X X tcp_allsocs = NIL; X#ifdef DEBUG X tcp_logState = 0; X#endif X tcp_id = 0; X X /* hack - assume the network number */ X sin_lclINAddr = 0x7d000000 + (*((longword *)&sed_lclEthAddr[1]) & 0xFFFFFF); X} X X/* X * Actively open a TCP connection to a particular destination. X */ Xtcp_Open(s, lport, ina, port, datahandler) X tcp_Socket *s; X in_HwAddress ina; X word lport, port; X procref datahandler; X{ X extern eth_HwAddress sed_ethBcastAddr; X X s->state = tcp_StateSYNSENT; X s->timeout = tcp_LONGTIMEOUT; X if ( lport == 0 ) lport = clock_ValueRough(); X s->myport = lport; X if ( ! sar_MapIn2Eth(ina, &s->hisethaddr[0]) ) { X printf("tcp_Open of 0x%x: defaulting ethernet address to broadcast\n", ina); X Move(&sed_ethBcastAddr[0], &s->hisethaddr[0], sizeof(eth_HwAddress)); X } X s->hisaddr = ina; X s->hisport = port; X s->seqnum = 0; X s->dataSize = 0; X s->flags = tcp_FlagSYN; X s->unhappy = true; X s->dataHandler = datahandler; X s->next = tcp_allsocs; X tcp_allsocs = s; X tcp_Send(s); X} X X/* X * Passive open: listen for a connection on a particular port X */ Xtcp_Listen(s, port, datahandler, timeout) X tcp_Socket *s; X word port; X procref datahandler; X{ X s->state = tcp_StateLISTEN; X if ( timeout == 0 ) s->timeout = 0x7ffffff; /* forever... */ X else s->timeout = timeout; X s->myport = port; X s->hisport = 0; X s->seqnum = 0; X s->dataSize = 0; X s->flags = 0; X s->unhappy = 0; X s->dataHandler = datahandler; X s->next = tcp_allsocs; X tcp_allsocs = s; X} X X/* X * Send a FIN on a particular port -- only works if it is open X */ Xtcp_Close(s) X tcp_Socket *s; X{ X if ( s->state == tcp_StateESTAB || s->state == tcp_StateSYNREC ) { X s->flags = tcp_FlagACK | tcp_FlagFIN; X s->state = tcp_StateFINWT1; X s->unhappy = true; X } X} X X/* X * Abort a tcp connection X */ Xtcp_Abort(s) X tcp_Socket *s; X{ X if ( s->state != tcp_StateLISTEN && s->state != tcp_StateCLOSED ) { X s->flags = tcp_FlagRST | tcp_FlagACK; X tcp_Send(s); X } X s->unhappy = 0; X s->dataSize = 0; X s->state = tcp_StateCLOSED; X s->dataHandler(s, 0, -1); X tcp_Unthread(s); X} X X/* X * Retransmitter - called periodically to perform tcp retransmissions X */ Xtcp_Retransmitter() X{ X tcp_Socket *s; X BOOL x; X X for ( s = tcp_allsocs; s; s = s->next ) { X x = false; X if ( s->dataSize > 0 || s->unhappy ) { X tcp_Send(s); X x = true; X } X if ( x || s->state != tcp_StateESTAB ) X s->timeout -= tcp_RETRANSMITTIME; X if ( s->timeout <= 0 ) { X if ( s->state == tcp_StateTIMEWT ) { X printf("Closed. \n"); X s->state = tcp_StateCLOSED; X s->dataHandler(s, 0, 0); X tcp_Unthread(s); X } else { X printf("Timeout, aborting\n"); X tcp_Abort(s); X } X } X } X} X X/* X * Unthread a socket from the socket list, if it's there X */ Xtcp_Unthread(ds) X tcp_Socket *ds; X{ X tcp_Socket *s, **sp; X X sp = &tcp_allsocs; X for (;;) { X s = *sp; X if ( s == ds ) { X *sp = s->next; X break; X } X if ( s == NIL ) break; X sp = &s->next; X } X} X X/* X * busy-wait loop for tcp. Also calls an "application proc" X */ Xtcp(application) X procref application; X{ X in_Header *ip; X longword timeout, start; X int x; X X sed_Receive(0); X X timeout = 0; X while ( tcp_allsocs ) { X start = clock_ValueRough(); X ip = sed_IsPacket(); X if ( ip == NIL ) { X if ( clock_ValueRough() > timeout ) { X tcp_Retransmitter(); X timeout = clock_ValueRough() + tcp_RETRANSMITTIME; X } X X application(); X X continue; X } X X if ( sed_CheckPacket(ip, 0x806) == 1 ) { X /* do arp */ X sar_CheckPacket(ip); X X } else if ( sed_CheckPacket(ip, 0x800) == 1 ) { X /* do IP */ X if ( ip->destination == sin_lclINAddr && X in_GetProtocol(ip) == 6 && X checksum(ip, in_GetHdrlenBytes(ip)) == 0xFFFF ) { X tcp_Handler(ip); X } X } X /* recycle buffer */ X sed_Receive(ip); X X x = clock_ValueRough() - start; X timeout -= x; X } X X return ( 1 ); X} X X/* X * Write data to a connection. X * Returns number of bytes written, == 0 when connection is not in X * established state. X */ Xtcp_Write(s, dp, len) X tcp_Socket *s; X byte *dp; X int len; X{ X int x; X X if ( s->state != tcp_StateESTAB ) len = 0; X if ( len > (x = tcp_MaxData - s->dataSize) ) len = x; X if ( len > 0 ) { X Move(dp, &s->data[s->dataSize], len); X s->dataSize += len; X tcp_Flush(s); X } X X return ( len ); X} X X/* X * Send pending data X */ Xtcp_Flush(s) X tcp_Socket *s; X{ X if ( s->dataSize > 0 ) { X s->flags |= tcp_FlagPUSH; X tcp_Send(s); X } X} X X/* X * Handler for incoming packets. X */ Xtcp_Handler(ip) X in_Header *ip; X{ X tcp_Header *tp; X tcp_PseudoHeader ph; X int len; X byte *dp; X int x, diff; X tcp_Socket *s; X word flags; X X len = in_GetHdrlenBytes(ip); X tp = (tcp_Header *)((byte *)ip + len); X len = ip->length - len; X X /* demux to active sockets */ X for ( s = tcp_allsocs; s; s = s->next ) X if ( s->hisport != 0 && X tp->dstPort == s->myport && X tp->srcPort == s->hisport && X ip->source == s->hisaddr ) break; X if ( s == NIL ) { X /* demux to passive sockets */ X for ( s = tcp_allsocs; s; s = s->next ) X if ( s->hisport == 0 && tp->dstPort == s->myport ) break; X } X if ( s == NIL ) { X#ifdef DEBUG X if ( tcp_logState & tcp_LOGPACKETS ) tcp_DumpHeader(ip, tp, "Discarding"); X#endif X return; X } X X#ifdef DEBUG X if ( tcp_logState & tcp_LOGPACKETS ) X tcp_DumpHeader(ip, tp, "Received"); X#endif X X /* save his ethernet address */ X MoveW(&((((eth_Header *)ip) - 1)->source[0]), &s->hisethaddr[0], sizeof(eth_HwAddress)); X X ph.src = ip->source; X ph.dst = ip->destination; X ph.mbz = 0; X ph.protocol = 6; X ph.length = len; X ph.checksum = checksum(tp, len); X if ( checksum(&ph, sizeof ph) != 0xffff ) X printf("bad tcp checksum, received anyway\n"); X X flags = tp->flags; X if ( flags & tcp_FlagRST ) { X printf("connection reset\n"); X s->state = tcp_StateCLOSED; X s->dataHandler(s, 0, -1); X tcp_Unthread(s); X return; X } X X switch ( s->state ) { X X case tcp_StateLISTEN: X if ( flags & tcp_FlagSYN ) { X s->acknum = tp->seqnum + 1; X s->hisport = tp->srcPort; X s->hisaddr = ip->source; X s->flags = tcp_FlagSYN | tcp_FlagACK; X tcp_Send(s); X s->state = tcp_StateSYNREC; X s->unhappy = true; X s->timeout = tcp_TIMEOUT; X printf("Syn from 0x%x#%d (seq 0x%x)\n", s->hisaddr, s->hisport, tp->seqnum); X } X break; X X case tcp_StateSYNSENT: X if ( flags & tcp_FlagSYN ) { X s->acknum++; X s->flags = tcp_FlagACK; X s->timeout = tcp_TIMEOUT; X if ( (flags & tcp_FlagACK) && tp->acknum == (s->seqnum + 1) ) { X printf("Open\n"); X s->state = tcp_StateESTAB; X s->seqnum++; X s->acknum = tp->seqnum + 1; X s->unhappy = false; X } else { X s->state = tcp_StateSYNREC; X } X } X break; X X case tcp_StateSYNREC: X if ( flags & tcp_FlagSYN ) { X s->flags = tcp_FlagSYN | tcp_FlagACK; X tcp_Send(s); X s->timeout = tcp_TIMEOUT; X printf(" retransmit of original syn\n"); X } X if ( (flags & tcp_FlagACK) && tp->acknum == (s->seqnum + 1) ) { X s->flags = tcp_FlagACK; X tcp_Send(s); X s->seqnum++; X s->unhappy = false; X s->state = tcp_StateESTAB; X s->timeout = tcp_TIMEOUT; X printf("Synack received - connection established\n"); X } X break; X X case tcp_StateESTAB: X if ( (flags & tcp_FlagACK) == 0 ) return; X /* process ack value in packet */ X diff = tp->acknum - s->seqnum; X if ( diff > 0 ) { X Move(&s->data[diff], &s->data[0], diff); X s->dataSize -= diff; X s->seqnum += diff; X } X s->flags = tcp_FlagACK; X tcp_ProcessData(s, tp, len); X break; X X case tcp_StateFINWT1: X if ( (flags & tcp_FlagACK) == 0 ) return; X diff = tp->acknum - s->seqnum - 1; X s->flags = tcp_FlagACK | tcp_FlagFIN; X if ( diff == 0 ) { X s->state = tcp_StateFINWT2; X s->flags = tcp_FlagACK; X printf("finack received.\n"); X } X tcp_ProcessData(s, tp, len); X break; X X case tcp_StateFINWT2: X s->flags = tcp_FlagACK; X tcp_ProcessData(s, tp, len); X break; X X case tcp_StateCLOSING: X if ( tp->acknum == (s->seqnum + 1) ) { X s->state = tcp_StateTIMEWT; X s->timeout = tcp_TIMEOUT; X } X break; X X case tcp_StateLASTACK: X if ( tp->acknum == (s->seqnum + 1) ) { X s->state = tcp_StateCLOSED; X s->unhappy = false; X s->dataSize = 0; X s->dataHandler(s, 0, 0); X tcp_Unthread(s); X printf("Closed. \n"); X } else { X s->flags = tcp_FlagACK | tcp_FlagFIN; X tcp_Send(s); X s->timeout = tcp_TIMEOUT; X printf("retransmitting FIN\n"); X } X break; X X case tcp_StateTIMEWT: X s->flags = tcp_FlagACK; X tcp_Send(s); X } X} X X/* X * Process the data in an incoming packet. X * Called from all states where incoming data can be received: established, X * fin-wait-1, fin-wait-2 X */ Xtcp_ProcessData(s, tp, len) X tcp_Socket *s; X tcp_Header *tp; X int len; X{ X int diff, x; X word flags; X byte *dp; X X flags = tp->flags; X diff = s->acknum - tp->seqnum; X if ( flags & tcp_FlagSYN ) diff--; X x = tcp_GetDataOffset(tp) << 2; X dp = (byte *)tp + x; X len -= x; X if ( diff >= 0 ) { X dp += diff; X len -= diff; X s->acknum += len; X s->dataHandler(s, dp, len); X if ( flags & tcp_FlagFIN ) { X s->acknum++; X#ifdef DEBUG X printf("consumed fin.\n"); X#endif X switch(s->state) { X case tcp_StateESTAB: X /* note: skip state CLOSEWT by automatically closing conn */ X x = tcp_StateLASTACK; X s->flags |= tcp_FlagFIN; X s->unhappy = true; X#ifdef DEBUG X printf("sending fin.\n"); X#endif X break; X case tcp_StateFINWT1: X x = tcp_StateCLOSING; X break; X case tcp_StateFINWT2: X x = tcp_StateTIMEWT; X break; X } X s->state = x; X } X } X s->timeout = tcp_TIMEOUT; X tcp_Send(s); X} X X/* X * Format and send an outgoing segment X */ Xtcp_Send(s) X tcp_Socket *s; X{ X tcp_PseudoHeader ph; X struct _pkt { X in_Header in; X tcp_Header tcp; X longword maxsegopt; X } *pkt; X byte *dp; X X pkt = (struct _pkt *)sed_FormatPacket(&s->hisethaddr[0], 0x800); X dp = &pkt->maxsegopt; X X pkt->in.length = sizeof(in_Header) + sizeof(tcp_Header) + s->dataSize; X X /* tcp header */ X pkt->tcp.srcPort = s->myport; X pkt->tcp.dstPort = s->hisport; X pkt->tcp.seqnum = s->seqnum; X pkt->tcp.acknum = s->acknum; X pkt->tcp.window = 1024; X pkt->tcp.flags = s->flags | 0x5000; X pkt->tcp.checksum = 0; X pkt->tcp.urgentPointer = 0; X if ( s->flags & tcp_FlagSYN ) { X pkt->tcp.flags += 0x1000; X pkt->in.length += 4; X pkt->maxsegopt = 0x02040578; /* 1400 bytes */ X dp += 4; X } X MoveW(s->data, dp, s->dataSize); X X /* internet header */ X pkt->in.vht = 0x4500; /* version 4, hdrlen 5, tos 0 */ X pkt->in.identification = tcp_id++; X pkt->in.frag = 0; X pkt->in.ttlProtocol = (250<<8) + 6; X pkt->in.checksum = 0; X pkt->in.source = sin_lclINAddr; X pkt->in.destination = s->hisaddr; X pkt->in.checksum = ~checksum(&pkt->in, sizeof(in_Header)); X X /* compute tcp checksum */ X ph.src = pkt->in.source; X ph.dst = pkt->in.destination; X ph.mbz = 0; X ph.protocol = 6; X ph.length = pkt->in.length - sizeof(in_Header); X ph.checksum = checksum(&pkt->tcp, ph.length); X pkt->tcp.checksum = ~checksum(&ph, sizeof ph); X X#ifdef DEBUG X if ( tcp_logState & tcp_LOGPACKETS ) X tcp_DumpHeader(&pkt->in, &pkt->tcp, "Sending"); X#endif X X sed_Send(pkt->in.length); X} X X/* X * Do a one's complement checksum X */ Xchecksum(dp, length) X word *dp; X int length; X{ X int len; X longword sum; X X len = length >> 1; X sum = 0; X while ( len-- > 0 ) sum += *dp++; X if ( length & 1 ) sum += (*dp & 0xFF00); X sum = (sum & 0xFFFF) + ((sum >> 16) & 0xFFFF); X sum = (sum & 0xFFFF) + ((sum >> 16) & 0xFFFF); X X return ( sum ); X} X X/* X * Dump the tcp protocol header of a packet X */ Xtcp_DumpHeader( ip, tp, mesg ) X in_Header *ip; X char *mesg; X{ X register tcp_Header *tp = (tcp_Header *)((byte *)ip + in_GetHdrlenBytes(ip)); X static char *flags[] = { "FIN", "SYN", "RST", "PUSH", "ACK", "URG" }; X int len; X word f; X X len = ip->length - ((tcp_GetDataOffset(tp) + in_GetHdrlen(ip)) << 2); X printf("TCP: %s packet:\nS: %x; D: %x; SN=%x ACK=%x W=%d DLen=%d\n", X mesg, tp->srcPort, tp->dstPort, tp->seqnum, tp->acknum, X tp->window, len); X printf("DO=%d, C=%x U=%d", X tcp_GetDataOffset(tp), tp->checksum, tp->urgentPointer); X /* output flags */ X f = tp->flags; X for ( len = 0; len < 6; len++ ) X if ( f & (1 << len) ) printf(" %s", flags[len]); X printf("\n"); X} X X/* X * Move bytes from hither to yon X */ XMove( src, dest, numbytes ) X register byte *src, *dest; X register numbytes; X{ X if ( numbytes <= 0 ) return; X if ( src < dest ) { X src += numbytes; X dest += numbytes; X do { X *--dest = *--src; X } while ( --numbytes > 0 ); X } else X do { X *dest++ = *src++; X } while ( --numbytes > 0 ); X} END-of-tinytcp.c echo file: tinytcp.h sed 's/^X//' >tinytcp.h << 'END-of-tinytcp.h' X/* X * tinytcp.h - header file for tinytcp.c X * X * Copyright (C) 1986, IMAGEN Corporation X * "This code may be duplicated in whole or in part provided that [1] there X * is no commercial gain involved in the duplication, and [2] that this X * copyright notice is preserved on all copies. Any other duplication X * requires written notice of the author (Geoffrey H. Cooper)." X * X * Note: the structures herein must guarantee that the X * code only performs word fetches, since the X * imagenether card doesn't accept byte accesses. X */ X X#define TRUE 1 X#define true 1 X#define FALSE 0 X#define false 0 X#define NULL 0 /* An empty value */ X#define NIL 0 /* The distinguished empty pointer */ X X/* Useful type definitions */ Xtypedef int (*procref)(); Xtypedef short BOOL; /* boolean type */ X X/* Canonically-sized data */ Xtypedef unsigned long longword; /* 32 bits */ Xtypedef unsigned short word; /* 16 bits */ Xtypedef unsigned char byte; /* 8 bits */ Xtypedef byte octet; /* 8 bits, for TCP */ X X#ifdef DDT Xextern longword MsecClock(); X#define clock_ValueRough() MsecClock() X#else Xextern longword clock_MS; X#define clock_ValueRough() clock_MS X#endif X X/* protocol address definitions */ Xtypedef longword in_HwAddress; Xtypedef word eth_HwAddress[3]; X X/* The Ethernet header */ Xtypedef struct { X eth_HwAddress destination; X eth_HwAddress source; X word type; X} eth_Header; X X/* The Internet Header: */ Xtypedef struct { X word vht; /* version, hdrlen, tos */ X word length; X word identification; X word frag; X word ttlProtocol; X word checksum; X in_HwAddress source; X in_HwAddress destination; X} in_Header; X#define in_GetVersion(ip) (((ip)->vht >> 12) & 0xf) X#define in_GetHdrlen(ip) (((ip)->vht >> 8) & 0xf) X#define in_GetHdrlenBytes(ip) (((ip)->vht >> 6) & 0x3c) X#define in_GetTos(ip) ((ip)->vht & 0xff) X X#define in_GetTTL(ip) ((ip)->ttlProtocol >> 8) X#define in_GetProtocol(ip) ((ip)->ttlProtocol & 0xff) X X Xtypedef struct { X word srcPort; X word dstPort; X longword seqnum; X longword acknum; X word flags; X word window; X word checksum; X word urgentPointer; X} tcp_Header; X X X#define tcp_FlagFIN 0x0001 X#define tcp_FlagSYN 0x0002 X#define tcp_FlagRST 0x0004 X#define tcp_FlagPUSH 0x0008 X#define tcp_FlagACK 0x0010 X#define tcp_FlagURG 0x0020 X#define tcp_FlagDO 0xF000 X#define tcp_GetDataOffset(tp) ((tp)->flags >> 12) X X/* The TCP/UDP Pseudo Header */ Xtypedef struct { X in_HwAddress src; X in_HwAddress dst; X octet mbz; X octet protocol; X word length; X word checksum; X} tcp_PseudoHeader; X X/* X * TCP states, from tcp manual. X * Note: close-wait state is bypassed by automatically closing a connection X * when a FIN is received. This is easy to undo. X */ X#define tcp_StateLISTEN 0 /* listening for connection */ X#define tcp_StateSYNSENT 1 /* syn sent, active open */ X#define tcp_StateSYNREC 2 /* syn received, synack+syn sent. */ X#define tcp_StateESTAB 3 /* established */ X#define tcp_StateFINWT1 4 /* sent FIN */ X#define tcp_StateFINWT2 5 /* sent FIN, received FINACK */ X/*#define tcp_StateCLOSEWT 6 /* received FIN waiting for close */ X#define tcp_StateCLOSING 6 /* sent FIN, received FIN (waiting for FINACK) */ X#define tcp_StateLASTACK 7 /* fin received, finack+fin sent */ X#define tcp_StateTIMEWT 8 /* dally after sending final FINACK */ X#define tcp_StateCLOSED 9 /* finack received */ X X/* X * TCP Socket definition X */ X#define tcp_MaxData 32 /* maximum bytes to buffer on output */ X Xtypedef struct _tcp_socket { X struct _tcp_socket *next; X short state; /* connection state */ X procref dataHandler; /* called with incoming data */ X eth_HwAddress hisethaddr; /* ethernet address of peer */ X in_HwAddress hisaddr; /* internet address of peer */ X word myport, hisport;/* tcp ports for this connection */ X longword acknum, seqnum; /* data ack'd and sequence num */ X int timeout; /* timeout, in milliseconds */ X BOOL unhappy; /* flag, indicates retransmitting segt's */ X word flags; /* tcp flags word for last packet sent */ X short dataSize; /* number of bytes of data to send */ X byte data[tcp_MaxData]; /* data to send */ X} tcp_Socket; X Xextern eth_HwAddress sed_lclEthAddr; Xextern eth_HwAddress sed_ethBcastAddr; Xextern in_HwAddress sin_lclINAddr; X X/* X * ARP definitions X */ X#define arp_TypeEther 1 /* ARP type of Ethernet address * X X/* harp op codes */ X#define ARP_REQUEST 1 X#define ARP_REPLY 2 X X/* X * Arp header X */ Xtypedef struct { X word hwType; X word protType; X word hwProtAddrLen; /* hw and prot addr len */ X word opcode; X eth_HwAddress srcEthAddr; X in_HwAddress srcIPAddr; X eth_HwAddress dstEthAddr; X in_HwAddress dstIPAddr; X} arp_Header; X X/* X * Ethernet interface: X * sed_WaitPacket(0) => ptr to packet (beyond eth header) X * or NIL if no packet ready. X * sed_Receive(ptr) - reenables receive on input buffer X * sed_FormatPacket(ðdest, ethtype) => ptr to packet buffer X * sed_Send(packet_length) - send the buffer last formatted. X */ Xbyte *sed_IsPacket(), *sed_FormatPacket(); END-of-tinytcp.h exit