static char sccsid
[] = "@(#)prot.c 4.1 (Berkeley) %G%";
/* Protocol driver, user level, Berkeley network */
This code is a little complicated because of a number of different
protocols used. Here is an explanation:
0 Normal Case (6 bit with no kernel driver support)
1 Line Discipline -- uses NETLDISP in sgtty.h and ioctl to set the
line discipline. At Berkeley this means avoiding interrupting on
every character by using a Silo on a DH or DZ board, and (optionally)
bypassing the canonicalization in the tty code by putting the charactars
condition (netd.dp_bnetldis != 0)
2 8-bit TTY protocol -- implies Level 1 and inserts record separators(012)
and escapes other occurrences of 012. Since the driver on the other
end must interpolate the escapes, this is an asymmetric protocol where
the sender puts in the escapes but the receiver at the user level knows
they have already been removed.
condition (netd.dp_bnetldis != 0 && netd.dp_use8bit != 0)
3 8-bit Block Device protocol -- this is for a DMC-11, it writes fixed
length blocks in both directions with no quoting.
condition (netd.dp_bnetldis != 0 && netd.dp_usehighspeed != 0)
4 RAND 8-bit protocol -- included for completeness, is not
correctly specified here.
If the daemons are being simulated by pipes, then netd.dp_pipesim != 0
and each of the 4 levels (except RAND) are simulated.
In this case at level 2 (use8bit) on the receiver end it does the quoting.
Timing statistics: We estimate 300 micros for queue/dequeue and then
20 micros per interrupt for 30 cps => 2.5% of system for 9600 Baud line
Max packet lengths=> to CSVAX with 1k buffers and 6-bit prot = 758 chars
to Ing70 with 512 byte buffers and no NETLDISC, only 182 chars
static short masterseqno
, lastseqno
;
static char wpack
[MAXNBUF
];
one problem has been character loss on
overloaded systems due to the daemon
taking too long to swap in
A high priority process of small size
with a pipe would do the job.
signal(SIGALRM
,SIG_IGN
); /* alarm off */
longjmp(env
,0); /* ugh */
/* returns number of bytes written, error returns WRITEFAIL (-3) */
/* inbuf is buffer of amt chars to be written */
struct packet
*rpp
, *xptr
;
xptr
= (struct packet
*)wpack
;
savetime
= netd
.dp_atime
;
if(retransmit
> netd
.dp_maxbread
){
/* format the packet to send */
num
= min(netd
.dp_datasize
,amt
);
/* set the length down if escapes are being used */
if(netd
.dp_use8bit
)num
= min(num
,MAXNBUF
/2);
xptr
->seqno
= masterseqno
;
netd
.dp_atime
+= 3; /* wait three more secs */
if(rpp
->chksum
!= 0 || rpp
->pcode
!= ACK
|| rpp
->seqno
!= xptr
->seqno
){
if(rpp
->seqno
== 1 && rpp
->pcode
== REQUEST
){
error("chksum %d",rpp
->seqno
);
else if(rpp
->pcode
!= ACK
)
error("not ack %d %d",rpp
->pcode
,rpp
->seqno
);
else if(rpp
->seqno
!= xptr
->seqno
)
error("WRSQNO got %d request %d",rpp
->seqno
,
netd
.dp_atime
= savetime
;
/* return the number of bytes read, or error = BROKENREAD (-2) */
register struct packet
*pp
;
static char savebuf
[MAXNBUF
];
/* first see if theres any left from the last packet */
cnt
= n
= min(bufleft
,num
);
while(n
--)*bptr
++ = *p
++;
if(++bcnt
>= netd
.dp_maxbread
){
error("chksum %d",pp
->seqno
);
if(pp
->pcode
& ~REQUEST
){
error("pcode %d %d",pp
->pcode
,pp
->seqno
);
/* this is the normal case, so we ack it */
else { /* else was a REQUEST packet, no chksum errs */
if(pp->seqno == 1)debug("^R ");
sendpacket(pp
); /* send ACK */
/* now process this packet, bptr points to where we left off */
j
= n
= min(num
,pp
->len
);
while(n
--)*bptr
++ = *p
++;
n
= bufleft
= pp
->len
- num
;
while(n
--)*bptr
++ = *p
++;
if(pp
->pcode
== REQUEST
)c
='r';
else if(pp
->pcode
== ACK
)c
= 'a';
else if(pp
->pcode
== PURGE
)c
= 'p';
sprintf(dest
,"p:%d len:%d c:%c d:", pp
->seqno
, pp
->len
, c
);
for(i
=0; i
<pp
->len
&& pp
->data
[i
]; i
++)*s
++ = pp
->data
[i
];
* A purge can always be sent -
* the receiver totally ignores it.
* It is used to push the packet terminator
* down the wire in case of a crash
* leaving the receiver half reading.
xptr
= (struct packet
*)wpack
;
/* init sequence numbers */
bufleft
= 0; /* if any chars are left in buffer, flush them*/
netd
.dp_atime
= netd
.dp_oatime
+ ((rand()>>8)%15);
static char raw
[MAXNBUF
];
/* writes the data to be sent in array raw */
/* finalp will point to either pp or raw */
dump
.nbytesent
+= pp
->len
;
len
= ACKLENGTH
+ pp
->len
;
for(j
= 0; j
< len
; j
++)n
^= *p
++;
if(netd
.dp_usehispeed
)finalp
= (char *)pp
;
else if(netd
.dp_use8bit
){
fprintf(stderr
,"Packet size too big- error\n");
if(*p
== '\n' || *p
== '\\'){
/* now change 8-bit data to 6-bit data */
if(((len
+2)*4)/3 >= MAXNBUF
){
fprintf(stderr
,"Packet size too big- error\n");
*p
++ = (*q
& 077) + INCR
;
*p
++ = (((*q
<< 2) | j
) & 077) + INCR
;
*p
++ = (((*q
<< 4) | j
) & 077) + INCR
;
*p
++ = ((*q
++ >> 2) & 077) + INCR
;
/* because of bugs in processing around erase and kill in v6 */
debug("send %d <<%s>>",len,raw);
if(len
> SENDLEN
)error("send length too long");
if(netd
.dp_pipesim
) i
= write(netd
.dp_pwritefd
,finalp
,len
);
else i
= write(netd
.dp_linefd
,finalp
,len
);
i
= write(netd
.dp_linefd
, (char *)pp
,len
);
* returns NULL if couldn't get a packet with correct seqno
* chksum not checked here
* because other programs may want to interrogate checksum
struct packet
*getpacket() {
register struct packet
*gptr
;
struct packet
*decpacket();
signal(SIGALRM
,alarmint
);
if(bcnt
++ > netd
.dp_maxbread
)errno
= 100; /* give up */
if(debugflg
)putchar('^');
/* decode the buffer, including 6-8 bit conv, etc. */
error("getpacket fails");
if(tooshort
|| gptr
->len
< 0 || gptr
->len
> MAXNBUF
){
error("too short p:%d l:%d",gptr
->seqno
,gptr
->len
);
if(gptr
->seqno
== 1 && gptr
->pcode
!= ACK
){
if(gptr
->pcode
== PURGE
){
continue; /* never seen */
if(gptr
->seqno
== lastseqno
){
/* send ACK - it was lost first time thru */
error("sendlostack %d",lastseqno
);
/* this is the correct case */
if(gptr
->seqno
== lastseqno
+ 1)break;
error("Wrong seq no g: %d last: %d",gptr
->seqno
,
len
= gptr
->len
+ ACKLENGTH
;
for(i
=0; i
< len
; i
++)n
^= *p
++;
dump
.nbytercv
+= gptr
->len
;
/* read in and decode packet */
/* as a side effect sets "tooshort" */
static struct packet
*decpacket()
static char cooked
[MAXNBUF
], raw
[MAXNBUF
];
/* read in chars to raw, if processed then return in cooked, otherwise
len
= read(fileno(netd
.dp_rdfile
),raw
,SENDLEN
);
ch
= getc(netd
.dp_rdfile
);
/* eat up the backslashes */
if(ch
== '\\' && netd
.dp_use8bit
)
ch
= getc(netd
.dp_rdfile
);
if(netd
.dp_use8bit
)len
--;
else if(netd
.dp_usehispeed
)
len
= read(fileno(netd
.dp_rdfile
),raw
,SENDLEN
);
else len
= read(netd
.dp_linefd
,raw
,MAXNBUF
);
if(len
== 0)fprintf(stderr
,"eof pip %d\n",fileno(netd
.dp_rdfile
));
if(len
<= 0)return(NULL
);
debug("receive %d <<%s>>",len,raw);
/* if 8 bit the all we need to do is return */
if(netd
.dp_usehispeed
)return((struct packet
*)raw
);
pp
= (struct packet
*)raw
;
if(len
!= ACKLENGTH
+ pp
->len
)tooshort
= 1;
/* remove this loop later */
if(*p
< INCR
|| *p
& 0200)error("bad char %o\n",*p
);
*q
++ = ((j
& 03) << 6) | (i
& 077);
*q
++ = ((i
& 017) << 4) | ((j
>> 2) & 017);
*q
++ = ((j
& 077) << 2) | ((i
>> 4) & 03);
pp
= (struct packet
*)cooked
;
if(len
!= ((ACKLENGTH
+ pp
->len
+ 2)/3)*4 + 1) tooshort
= 1;
/* not sure of the length computation */
if(len
!= ACKLENGTH
+ gptr
->len
) tooshort
= 1;
return((struct packet
*)cooked
);
register struct packet
*pp
; {