TCP/IP over TTY lines (source)
Marc Elvy
elvy at harvard.UUCP
Sat Feb 25 07:34:09 AEST 1984
Since I submitted an article some time ago asking for advice on how to
implement TCP/IP through TTY ports, I received many letters -- some
offering advice and some wanting to know what I discovered. Rather
than respond to each of them in person, I am submitting here the sources
necessary to effect such a system.
One of our systems hackers (Robert Morris, harvard!morris) fixed up
the kernel on our 4.2 Vaxes so that they talked to one another using
TCP/IP stuff (we can rlogin, rwho, ruptime, rsh, telnet, ftp, etc.).
It is a very nice setup, considering we have two Vaxes right next to
one another, yet no Ethernet equipment. While (in empirical terms) it
is rather slow, it is quite usable if there are not too many people
on the system.
What follows are four files: README, if_itt.c, ittconf.c, tty_conf.c
The README file contains a description of how to modify the kernel
to run TCP/IP over tty lines on 4.2BSD systems. The files are separated
by:
****************************
filename
****************************
If you have any questions, send mail to me (or directly to harvard!morris).
Enjoy.
Marc
Enc.
-----------------
****************************
README
****************************
These are the changes needed to install tcp over tty lines on a 4.2
system.
Put if_itt.c in vaxif/if_itt.c
Add a line in conf/files reading
vaxif/if_itt.c optional itt inet
and config SYSTEM; cd ../SYSTEM; make depend
If you have made no changes to sys/tty_conf.c, just copy the one
supplied. Otherwise, add a line discipline (I assume it is number
5) which uses ittopen, ittclose, and ittrint.
Add lines in sys/init_main.c reading
#include "itt.h"
#if NITT > 0
ittattach();
#endif
after the lines reading (roughly)
#if NLOOP > 0
loattach();
#endif
cd ../SYSTEM ; make vmunix
(If it doesn't work, send me a note.)
Compile ittconf.c and install it someplace; it tells the itt driver
which tty line corresponds to which host number.
Add lines to /etc/rc.local for each non-local host reading
ittconf /dev/ttyxx <host> > /dev/console &
(use some appropriate pathname for ittconf, like /etc/ittconf)
where /dev/ttyxx is the tty line to the other host, and <host> is the
host number you have chosen (like 1, not 126.1).
ittconf opens the indicated device, changes the line discipline
to number five, and does an ioctl whose number is the host number
at the other end of the line, and then pause()s to keep the line
open and the line discipline set. If you kill it, you'll stop any
communication on the line and flush the packet queues for that line.
Right at the beginning of /etc/rc.local, add
/etc/ifconfig itt0 <net>.<localhost>
where <net> is the net number you have chosen (126 here), and <localhost>
is the host number on that net you have chosen for this host. One one vax
we have
/etc/ifconfig itt0 126.1
and on the other
/etc/ifconfig itt0 126.2
So our vaxes are hosts 126.1 and 126.2; you must add entries in /etc/hosts
also, of the form
vax1 126.1
vax2 126.2
...
The additions should be the same on all machines; they should all know
their local addresses as well as the addresses of other hosts.
The ifconfig lines must be AFTER the iffconf lines.
It should work. It has a few problems. It's pretty slow, but it's set
up so that if you have more than one line between two hosts (configured
by more than one ittconf command), it shares the load between them. But
for various reasons, it often hangs for a fraction of a second about every
1000 characters under full load. This is because I had to use timeouts
instead of wakeups to keep the tty output queues full, and the timeouts
have granularity problems. It's not been very thouroughly
tested. If you check back in a while I'll have
packet forwarding set up. (Ie, right now if two machines aren't directly
connected, they can't talk. The driver should know how to pass on packets
for a third party.)
****************************
if_itt.c
****************************
/* if_itt.c 6.1 83/07/29 */
#include "../machine/pte.h"
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/mbuf.h"
#include "../h/buf.h"
#include "../h/protosw.h"
#include "../h/socket.h"
#include "../h/vmmac.h"
#include "../h/ioctl.h"
#include "../h/errno.h"
#include "../net/if.h"
#include "../net/netisr.h"
#include "../net/route.h"
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/ip.h"
#include "../netinet/ip_var.h"
#include "../vax/cpu.h"
#include "../vax/mtpr.h"
#include "../vaxif/if_uba.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/ubavar.h"
#include "../h/tty.h"
/* don't increase this */
#define ITTMTU 350 /* Max transmission unit (bytes) */
#define ITTHIWAT 1000 /* tty total output usage */
#define ITTSLOWT ((ITTHIWAT * 60) / 900)
#define ITTFASTT (ITTSLOWT / 3)
/* escape codes for synchronization */
/* send ESC, ESC???, argument */
#define ESC 'E'
#define ESCBEGIN 'b' /* null argument */
#define ESCEND 'e' /* null argument */
#define ESCDEST 'd' /* argument is destination host # */
#define ESCHOP 'h' /* hop count, discard after a while */
/* state codes */
#define IDLE 0
#define DATA 1
/* other codes are escape codes; if state is ESCDEST, waiting for dest */
int ittattach(), ittrint();
int ittinit(), ittioctl(), ittoutput(), ittreset();
int ittwatch();
struct ifnet ittif;
int itterrIDLE, itterrBEGIN, itterrEND;
extern struct tty *ittdesttotp();
struct itt_header{
unsigned int itt_dest;
};
#define NITTL 5
struct ittl {
int itt_state;
struct tty *itt_tp;
int itt_host;
struct mbuf *itt_mbase, *itt_m;
unsigned int itt_dest, itt_hop;
} ittl[NITTL];
/*
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets.
*/
ittattach()
{
register struct ifnet *ifp = &ittif;
ifp->if_name = "itt";
ifp->if_mtu = ITTMTU;
ifp->if_init = ittinit;
ifp->if_output = ittoutput;
ifp->if_ioctl = ittioctl;
ifp->if_reset = ittreset;
if_attach(ifp);
timeout(ittwatch, (caddr_t) 0, 60);
}
/*
* Reset of interface after UNIBUS reset.
* If interface is on specified uba, reset its state.
*/
ittreset()
{
ittinit();
}
/*
* Initialization of interface; clear recorded pending
* operations, and reinitialize UNIBUS usage.
*/
ittinit()
{
struct sockaddr_in *sin;
int s;
sin = (struct sockaddr_in *)&ittif.if_addr;
if (sin->sin_addr.s_addr == 0)
return;
ittif.if_flags |= IFF_UP | IFF_RUNNING;
if_rtinit(&ittif, RTF_UP);
}
/*
* ITT output routine.
*/
ittoutput(ifp, m, dst)
struct ifnet *ifp;
struct mbuf *m;
struct sockaddr *dst;
{
struct tty *tp;
int s, error, i;
unsigned int dest;
struct mbuf *m2;
struct itt_header *ittp;
switch (dst->sa_family) {
#ifdef INET
case AF_INET:
dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr);
break;
#endif
default:
printf("itt%d: can't handle af%d\n", ifp->if_unit,
dst->sa_family);
error = EAFNOSUPPORT;
goto bad;
}
/* insert a fake header packet with destination in it;
* removed and examined by ittstart().
*/
if (m->m_off > MMAXOFF ||
MMINOFF + sizeof (struct itt_header) > m->m_off) {
m2 = m_get(M_DONTWAIT, MT_HEADER);
if (m2 == 0) {
printf("no bufs in output\n");
error = ENOBUFS;
goto bad;
}
m2->m_next = m;
m2->m_off = MMINOFF;
m2->m_len = sizeof (struct itt_header);
m = m2;
} else {
m->m_off -= sizeof (struct itt_header);
m->m_len += sizeof (struct itt_header);
}
ittp = mtod(m, struct itt_header *);
ittp->itt_dest = dest;
tp = ittdesttotp(dest);
if(tp == NULL && dest){
m_freem(m);
return(EHOSTUNREACH);
}
s = splimp();
if(dest == 0){
for(i = 0; i < 20; i++){
if((tp = ittdesttotp(i)) != NULL){
m2 = m_copy(m, 0, M_COPYALL);
mtod(m2, struct itt_header *)->itt_dest = i;
if(IF_QFULL(&ifp->if_snd)){
IF_DROP(&ifp->if_snd);
error = ENOBUFS;
splx(s);
m_freem(m);
m_freem(m2);
return(error);
}
IF_ENQUEUE(&ifp->if_snd, m2);
}
}
m_freem(m);
} else {
if(IF_QFULL(&ifp->if_snd)){
IF_DROP(&ifp->if_snd);
ifp->if_oerrors++;
error = ENOBUFS;
splx(s);
m_freem(m);
return(error);
}
IF_ENQUEUE(&ifp->if_snd, m);
}
while(ittoutq() < ITTHIWAT && ittstart())
;
splx(s);
return (0);
bad:
m_freem(m);
return(error);
}
/*
* Start or restart output on interface.
*/
ittstart()
{
u_char c;
int len;
u_char *cp;
int dest, s;
register struct mbuf *m, *mm;
struct mbuf *m2;
struct tty *tp;
s = splimp();
IF_DEQUEUE(&ittif.if_snd, m);
if(m == 0){
splx(s);
return(0);
}
mm = m;
dest = mtod(m, struct itt_header *)->itt_dest;
m->m_off += sizeof(struct itt_header);
m->m_len -= sizeof(struct itt_header);
tp = ittdesttotp(dest);
if(tp == NULL){
printf("itt dq %d?\n", dest);
m_freem(mm);
splx(s);
return(1);
}
ittsendesc(tp, ESCBEGIN, 0);
ittsendesc(tp, ESCDEST, dest);
while(m){
for(cp = mtod(m, u_char *); cp < mtod(m, u_char *) + m->m_len; cp++){
if(*cp == ESC) ittsend(tp, ESC);
ittsend(tp, *cp);
}
MFREE(m, m2);
m = m2;
}
m = mm;
ittsendesc(tp, ESCEND, 0);
ittif.if_opackets++;
ttstart(tp);
splx(s);
return(1);
}
ittrint(c, tp)
register struct tty *tp;
u_char c;
{
int i, s;
extern int tk_nin;
struct ifqueue *ipq;
struct ittl *ip;
tk_nin++;
for(i = 0; i < NITTL; i++){
if(ittl[i].itt_tp == tp) break;
}
if(i >= NITTL){
printf("ittrint %x?\n", tp);
splx(s); return;
}
ip = &ittl[i];
s = splimp();
switch(ip->itt_state){
case IDLE:
if(c == ESC){
ip->itt_state = ESC;
splx(s); return;
}
itterrIDLE++;
ittif.if_ierrors++;
splx(s); return;
case DATA:
if(c == ESC){
ip->itt_state = ESC;
splx(s); return;
}
break;
case ESC:
if(c == ESC){
/* escaped ESC */
ip->itt_state = DATA;
break;
}
ip->itt_state = c;
splx(s); return;
case ESCBEGIN:
ip->itt_state = DATA;
if(ip->itt_mbase){
ittif.if_ierrors++;
itterrBEGIN++;
m_freem(ip->itt_mbase);
ip->itt_mbase = ip->itt_m = 0;
}
ip->itt_dest = 0;
ip->itt_hop = 0;
splx(s); return;
case ESCEND:
if(ip->itt_mbase == NULL){
itterrEND++;
ittif.if_ierrors++;
splx(s); return;
}
if(ip->itt_hop > 20){
printf("itt: loop\n");
m_freem(ip->itt_mbase);
ip->itt_mbase = ip->itt_m = 0;
splx(s);
ittif.if_ierrors++;
return;
}
ipq = &ipintrq;
if(IF_QFULL(ipq)){
IF_DROP(ipq);
ittif.if_collisions++;
m_freem(ip->itt_mbase);
ip->itt_mbase = ip->itt_m = 0;
} else {
if(mbad(ip->itt_mbase)) panic("ittrint: mbad enq\n");
IF_ENQUEUE(ipq, ip->itt_mbase);
schednetisr(NETISR_IP);
ittif.if_ipackets++;
}
ip->itt_state = IDLE;
ip->itt_m = ip->itt_mbase = 0;
splx(s); return;
case ESCHOP:
ip->itt_hop = c;
splx(s); return;
case ESCDEST:
ip->itt_dest = c;
ip->itt_state = DATA;
splx(s); return;
default:
printf("ittrint: unk state 0%o\n", ip->itt_state);
ip->itt_state = 0;
splx(s); return;
}
if(ip->itt_mbase == NULL){
ip->itt_m = ip->itt_mbase = m_get(M_DONTWAIT, MT_DATA);
if(ip->itt_m == 0){
splx(s);
return;
}
ip->itt_m->m_len = 1;
ip->itt_m->m_dat[0] = c;
} else if((ip->itt_m->m_len + ip->itt_m->m_off) >= MMAXOFF){
ip->itt_m->m_next = m_get(M_DONTWAIT, MT_DATA);
if(ip->itt_m->m_next == 0){
splx(s);
return;
}
ip->itt_m = ip->itt_m->m_next;
ip->itt_m->m_len = 1;
ip->itt_m->m_dat[0] = c;
} else {
ip->itt_m->m_dat[ip->itt_m->m_len++] = c;
if(mbad(ip->itt_mbase)) panic("ittrint: after else mbad\n");
}
splx(s);
}
/*
* Process an ioctl request.
*/
ittioctl(ifp, cmd, data)
register struct ifnet *ifp;
int cmd;
caddr_t data;
{
struct ifreq *ifr = (struct ifreq *)data;
struct sockaddr_in *sin;
int s = splimp(), error = 0;
switch (cmd) {
case SIOCSIFADDR:
if (ifp->if_flags & IFF_RUNNING)
if_rtinit(ifp, -1); /* delete previous route */
sin = (struct sockaddr_in *)&ifr->ifr_addr;
ifp->if_addr = *(struct sockaddr *)sin;
ifp->if_net = in_netof(sin->sin_addr);
ifp->if_host[0] = in_lnaof(sin->sin_addr);
ifp->if_broadaddr = *(struct sockaddr *)sin;
sin = (struct sockaddr_in *)&ifp->if_broadaddr;
sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
ifp->if_flags |= IFF_BROADCAST;
if (ifp->if_flags & IFF_RUNNING)
if_rtinit(ifp, RTF_UP);
else
ittinit(ifp->if_unit);
break;
default:
error = EINVAL;
}
splx(s);
return (error);
}
extern int lbolt;
ittsend(tp, c)
struct tty *tp;
u_char c;
{
int s;
if(putc(c, &tp->t_outq)){
printf("ittsend: out of clist\n");
return;
}
}
ittopen(dev, tp)
dev_t dev;
register struct tty *tp;
{
return(0);
}
ittclose(tp)
register struct tty *tp;
{
int i;
for(i = 0; i < NITTL; i++){
if(ittl[i].itt_tp == tp) ittl[i].itt_tp = NULL;
}
return(0);
}
itttioctl(tp, cmd, addr)
struct tty *tp;
caddr_t addr;
{
int i, s;
for(i = 0; i < NITTL; i++){
if(ittl[i].itt_tp == 0){
break;
}
}
if(i >= NITTL){
return(ENXIO);
}
ittl[i].itt_tp = tp;
ittl[i].itt_host = cmd;
ittl[i].itt_state = 0;
return(0);
}
ittwatch()
{
while(ittoutq() < ITTHIWAT && ittstart())
;
if(ittoutq() >= ITTHIWAT){
timeout(ittwatch, (caddr_t) 0, ITTFASTT);
} else {
timeout(ittwatch, (caddr_t) 0, ITTSLOWT);
}
}
ittoutq()
{
int i, cc = 0;
for(i = 0; i < NITTL; i++){
if(ittl[i].itt_tp){
cc += ittl[i].itt_tp->t_outq.c_cc;
}
}
return(cc);
}
mbad(m)
struct mbuf *m;
{
while(m){
switch(m->m_type){
case MT_DATA:
case MT_HEADER:
case MT_FTABLE:
break;
default:
printf("mbad %d\n", m->m_type);
return(1);
}
if((m->m_off + m->m_len) > MMAXOFF || (m->m_off < MMINOFF)){
printf("mbad off/size: off %d len %d\n", m->m_off, m->m_len);
return(1);
}
m = m->m_next;
}
return(0);
}
struct tty *
ittdesttotp(dest)
{
int i, mincc = 9999;
struct tty *tp = NULL;
for(i = 0; i < NITTL; i++){
if(ittl[i].itt_tp && ittl[i].itt_host == dest){
if(ittl[i].itt_tp->t_outq.c_cc < mincc){
tp = ittl[i].itt_tp;
mincc = tp->t_outq.c_cc;
}
}
}
return(tp);
}
ittsendesc(tp, c, a)
struct tty *tp;
u_char c, a;
{
ittsend(tp, ESC);
ittsend(tp, c);
ittsend(tp, a);
}
****************************
ittconf.c
****************************
/* ittconf.c */
#include <sgtty.h>
main(argc, argv)
char *argv[];
{
int ldisc, host = 1;
int speed = B9600;
struct sgttyb vec;
int fd;
if(argc < 2){
printf("Usage: %s dev\n", argv[0]);
exit(1);
}
if(argc > 2) host = atoi(argv[2]);
if(argc > 3){
if(strcmp(argv[3], "300") == 0) speed = B300;
if(strcmp(argv[3], "1200") == 0) speed = B1200;
if(strcmp(argv[3], "2400") == 0) speed = B2400;
}
if((fd = open(argv[1], 2)) < 0){
perror(argv[1]);
exit(1);
}
ldisc = 5;
vec.sg_ispeed = vec.sg_ospeed = speed;
vec.sg_flags = RAW | EVENP | ODDP;
stty(fd, &vec);
if(ioctl(fd, TIOCSETD, &ldisc) < 0){
perror("SETD");
exit(1);
}
if(ioctl(fd, host, 0) < 0){
perror("host");
}
printf("yup\n");
pause();
}
****************************
tty_conf.c
****************************
/* tty_conf.c 6.2 83/09/25 */
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/buf.h"
#include "../h/tty.h"
#include "../h/conf.h"
int nodev();
int nulldev();
int ttyopen(),ttyclose(),ttread(),ttwrite(),nullioctl(),ttstart();
int ttyinput();
#include "bk.h"
#include "itt.h"
#if NBK > 0
int bkopen(),bkclose(),bkread(),bkinput(),bkioctl();
#endif
#include "tb.h"
#if NTB > 0
int tbopen(),tbclose(),tbread(),tbinput(),tbioctl();
#endif
#if NITT > 0
int ittopen(), ittclose(), itttioctl(), ittrint();
#endif
struct linesw linesw[] =
{
ttyopen, nulldev, ttread, ttwrite, nullioctl,
ttyinput, nodev, nulldev, ttstart, nulldev,
#if NBK > 0
bkopen, bkclose, bkread, ttwrite, bkioctl,
bkinput, nodev, nulldev, ttstart, nulldev,
#else
nodev, nodev, nodev, nodev, nodev,
nodev, nodev, nodev, nodev, nodev,
#endif
ttyopen, ttyclose, ttread, ttwrite, nullioctl,
ttyinput, nodev, nulldev, ttstart, nulldev,
#if NTB > 0
tbopen, tbclose, tbread, nodev, tbioctl,
tbinput, nodev, nulldev, ttstart, nulldev, /* 3 */
#else
nodev, nodev, nodev, nodev, nodev,
nodev, nodev, nodev, nodev, nodev,
#endif
#if NTB > 0
tbopen, tbclose, tbread, nodev, tbioctl,
tbinput, nodev, nulldev, ttstart, nulldev, /* 4 */
#else
nodev, nodev, nodev, nodev, nodev,
nodev, nodev, nodev, nodev, nodev,
#endif
#if NITT > 0
ittopen, ittclose, nodev, nodev, itttioctl,
ittrint, nodev, nodev, ttstart, nulldev, /* 5 */
#else
nodev, nodev, nodev, nodev, nodev,
nodev, nodev, nodev, nodev, nodev,
#endif
};
int nldisp = sizeof (linesw) / sizeof (linesw[0]);
/*
* Do nothing specific version of line
* discipline specific ioctl command.
*/
/*ARGSUSED*/
nullioctl(tp, cmd, data, flags)
struct tty *tp;
char *data;
int flags;
{
#ifdef lint
tp = tp; data = data; flags = flags;
#endif
return (-1);
}
More information about the Comp.sources.unix
mailing list