xref: /original-bsd/old/berknet/prot.c (revision 2301fdfb)
1 static char sccsid[] = "@(#)prot.c	4.1	(Berkeley)	09/12/82";
2 
3 /* Protocol driver, user level, Berkeley network */
4 /*
5 	This code is a little complicated because of a number of different
6 	protocols used.  Here is an explanation:
7 
8 Level	Description
9 
10 0	Normal Case (6 bit with no kernel driver support)
11 
12 1	Line Discipline -- uses NETLDISP in sgtty.h and ioctl to set the
13 	line discipline.  At Berkeley this means avoiding interrupting on
14 	every character by using a Silo on a DH or DZ board, and (optionally)
15 	bypassing the canonicalization in the tty code by putting the charactars
16 	directly in a buffer.
17 	condition (netd.dp_bnetldis != 0)
18 
19 2	8-bit TTY protocol -- implies Level 1 and inserts record separators(012)
20 	and escapes other occurrences of 012.  Since the driver on the other
21 	end must interpolate the escapes, this is an asymmetric protocol where
22 	the sender puts in the escapes but the receiver at the user level knows
23 	they have already been removed.
24 	condition (netd.dp_bnetldis != 0 && netd.dp_use8bit != 0)
25 
26 3	8-bit Block Device protocol -- this is for a DMC-11, it writes fixed
27 	length blocks in both directions with no quoting.
28 	condition (netd.dp_bnetldis != 0 && netd.dp_usehighspeed != 0)
29 
30 4	RAND 8-bit protocol -- included for completeness, is not
31 	correctly specified here.
32 	Specified by an IFDEF.
33 
34 If the daemons are being simulated by pipes, then netd.dp_pipesim != 0
35 and each of the 4 levels (except RAND) are simulated.
36 In this case at level 2 (use8bit) on the receiver end it does the quoting.
37 
38 Timing statistics: We estimate 300 micros for queue/dequeue and then
39 	20 micros per interrupt for 30 cps => 2.5% of system for 9600 Baud line
40 
41 Max packet lengths=> to CSVAX with 1k buffers and 6-bit prot = 758 chars
42 	to Ing70 with 512 byte buffers and no NETLDISC, only 182 chars
43 
44 */
45 # include "defs.h"
46 
47 /* global */
48 struct dumpstruc dump;
49 struct daemonparms netd;
50 
51 /* local */
52 static int bufleft;
53 static char retransmit;
54 static jmp_buf env;
55 static short masterseqno, lastseqno;
56 /* writing packet */
57 static char wpack[MAXNBUF];
58 
59 /*
60    one problem has been character loss on
61    overloaded systems due to the daemon
62    taking too long to swap in
63    and losing characters.
64    A high priority process of small size
65    with a pipe would do the job.
66 */
67 alarmint(){
68 	errno = 100;
69 	signal(SIGALRM,SIG_IGN);		/* alarm off */
70 	longjmp(env,0);			/* ugh */
71 	}
72 /* returns number of bytes written, error returns WRITEFAIL (-3) */
73 /* inbuf is buffer of amt chars to be written */
74 xwrite(inbuf,amt)
75   char *inbuf;
76 {
77 	register char *p, *b;
78 	register int i;
79 	int cnt, num, savetime;
80 	struct packet *rpp, *xptr;
81 
82 	xptr = (struct packet *)wpack;
83 	cnt = 0;
84 	retransmit = 0;
85 	savetime = netd.dp_atime;
86 	while(amt > 0){
87 		if(retransmit > netd.dp_maxbread){
88 			debug("xwrite fail");
89 			return(WRITEFAIL);
90 			}
91 		/* format the packet to send */
92 		num = min(netd.dp_datasize,amt);
93 		/* set the length down if escapes are being used */
94 		if(netd.dp_use8bit)num = min(num,MAXNBUF/2);
95 		xptr->pcode = REQUEST;
96 		xptr->seqno = masterseqno;
97 		xptr->len = num;
98 		p = xptr->data;
99 		i = num;
100 		b = inbuf+cnt;
101 		while(i--)*p++ = *b++;
102 		/* send it */
103 		sendpacket(xptr);
104 		rpp = getpacket();
105 		if(rpp == NULL){
106 			netd.dp_atime += 3;	/* wait three more secs */
107 			retransmit++;
108 			dump.nretrans++;
109 			continue;
110 			}
111 		/* various errors */
112 		if(rpp->chksum != 0 || rpp->pcode != ACK
113 			|| rpp->seqno != xptr->seqno ){
114 			if(rpp->seqno == 1 && rpp->pcode == REQUEST){
115 				error("collision");
116 				return(WRITEFAIL);
117 				}
118 			if(rpp->chksum != 0)
119 				error("chksum %d",rpp->seqno);
120 			else if(rpp->pcode != ACK)
121 				error("not ack %d %d",rpp->pcode,rpp->seqno);
122 			else if(rpp->seqno != xptr ->seqno)
123 				error("WRSQNO got %d request %d",rpp->seqno,
124 					xptr->seqno);
125 			netd.dp_atime += 3;
126 			retransmit++;
127 			dump.nretrans++;
128 			continue;
129 			}
130 		masterseqno++;
131 		retransmit = 0;
132 		amt -= num;
133 		cnt += num;
134 		}
135 	netd.dp_atime = savetime;
136 	return(cnt);
137 	}
138 /* return the number of bytes read, or error = BROKENREAD (-2) */
139 nread(bptr,num)
140 	register char *bptr;
141 {
142 	register char *p;
143 	register struct packet *pp;
144     	register char *q;
145 	int bcnt = 0;
146 	int n,j,cnt;
147 	static char savebuf[MAXNBUF];
148 
149 	/* first see if theres any left from the last packet */
150 	cnt = 0;
151 	if(bufleft > 0){
152 		p = savebuf;
153 		cnt = n = min(bufleft,num);
154 		while(n--)*bptr++ = *p++;
155 		num -= cnt;
156 		bufleft -= cnt;
157 		if(bufleft > 0){
158 			q = savebuf;
159 			n = bufleft;
160 			while(n--)*q++ = *p++;
161 			}
162 		}
163 	if(num <= 0)
164 		return(cnt);
165 	/* now read a packet */
166 	retransmit = 0;
167 	for(;;){
168 		pp = getpacket();
169 		if(pp == NULL){
170 			if(++bcnt >= netd.dp_maxbread){
171 				debug("read timeout");
172 				return(BROKENREAD);
173 				}
174 			continue;
175 			}
176 		/* various errors */
177 		if(pp->chksum != 0){
178 			error("chksum %d",pp->seqno);
179 			retransmit++;
180 			continue;
181 			}
182 		if(pp->pcode & ~REQUEST){
183 			error("pcode %d %d",pp->pcode,pp->seqno);
184 			retransmit++;
185 			continue;
186 			}
187 		/* this is the normal case, so we ack it */
188 		else {		/* else was a REQUEST packet, no chksum errs */
189 			/*
190 			if(pp->seqno == 1)debug("^R ");
191 			*/
192 			pp->pcode = ACK;
193 			n = pp->len;
194 			pp->len = 0;
195 			sendpacket(pp);		/* send ACK */
196 			pp->len = n;
197 			break;
198 			}
199 		}
200 	/* now process this packet, bptr points to where we left off */
201 	retransmit = 0;
202 	j = n = min(num,pp->len);
203 	cnt += j;
204 	p = pp->data;
205 	while(n--)*bptr++ = *p++;
206 	if(pp->len > num){
207 		n = bufleft = pp->len - num;
208 		bptr = savebuf;
209 		while(n--)*bptr++ = *p++;
210 		}
211 	return(cnt);
212 	}
213 printpacket(pp,dest)
214   char *dest;
215   struct packet *pp; {
216 	char *s;
217 	int i;
218 	char c;
219 	dest[0] = 0;
220 	if(pp == NULL)return;
221 	if(pp->pcode == REQUEST)c='r';
222 	else if(pp->pcode == ACK)c = 'a';
223 	else if(pp->pcode == PURGE)c = 'p';
224 	else c = 'u';
225 	sprintf(dest,"p:%d len:%d c:%c d:", pp->seqno, pp->len, c);
226 	s = dest + strlen(dest);
227 	for(i=0; i<pp->len && pp->data[i]; i++)*s++ = pp->data[i];
228 	*s = 0;
229 	}
230 /*
231  * A purge can always be sent -
232  * the receiver totally ignores it.
233  * It is used to push the packet terminator
234  * down the wire in case of a crash
235  * leaving the receiver half reading.
236  */
237 sendpurge()
238   {
239 	struct packet *xptr;
240 	xptr = (struct packet *)wpack;
241 	xptr->pcode = PURGE;
242 	xptr->seqno = 0;
243 	xptr->len = 0;
244 	debug("send purge");
245 	sendpacket(xptr);
246 	}
247 /* init sequence numbers */
248 initseqno(){
249 	masterseqno = 1;
250 	lastseqno = 0;
251 	bufleft = 0;		/* if any chars are left in buffer, flush them*/
252 	netd.dp_atime = netd.dp_oatime + ((rand()>>8)%15);
253 	}
254 /*
255  *	Just sends packet pp
256  *	Calculates the chksum
257  */
258 sendpacket(pp)
259   struct packet *pp; {
260 	register char *q, *p;
261 	register int j;
262 	char *finalp;
263 	static char raw[MAXNBUF];
264 	int len, n, i;
265 
266 	/* writes the data to be sent in array raw */
267 	/* finalp will point to either pp or raw */
268 	dump.nbytesent += pp->len;
269 	dump.npacksent++;
270 	pp->chksum = 0;
271 	n = 0;
272 	p = (char *)pp;
273 	len = ACKLENGTH + pp->len;
274 	for(j = 0; j < len; j++)n ^= *p++;
275 	pp->chksum = n;
276 # ifdef SWAB
277 	switchem(pp);
278 # endif
279 # ifndef RAND
280 	if(netd.dp_usehispeed)finalp = (char *)pp;
281 	else if(netd.dp_use8bit){
282 		if(len >= MAXNBUF){
283 			fprintf(stderr,"Packet size too big- error\n");
284 			exit(1);
285 		}
286 		/* add escapes */
287 		p = (char *)pp;
288 		q = raw;
289 		i = len;
290 		len = 0;
291 		for(j = 0; j < i; j++){
292 			if(*p == '\n' || *p == '\\'){
293 				*q++ = '\\';
294 				*q++ = *p++;
295 				len++;
296 				len++;
297 			}
298 			else {
299 				*q++ = *p++;
300 				len++;
301 			}
302 		}
303 		*q = '\n';
304 		len++;
305 		finalp = raw;
306 	}
307 	else {
308 		/* now change 8-bit data to 6-bit data */
309 		if(((len+2)*4)/3 >= MAXNBUF){
310 			fprintf(stderr,"Packet size too big- error\n");
311 			exit(1);
312 			}
313 		p = raw;
314 		q = (char *)pp;
315 		len = n = (len+2)/3;
316 		while(n--){
317 			*p++ = (*q & 077) + INCR;
318 			j =    (*q++ >> 6) &03;
319 			*p++ = (((*q << 2) | j) & 077) + INCR;
320 			j =    (*q++ >> 4) & 017;
321 			*p++ = (((*q << 4) | j) & 077) + INCR;
322 			*p++ = ((*q++ >> 2) & 077) + INCR;
323 			}
324 		*p++ = '\n';
325 		*p = 0;
326 	/*	because of bugs in processing around erase and kill in v6 */
327 		for(p=raw; *p; p++)
328 			if(*p == '\\')*p = '}';
329 		len = len * 4 + 1;
330 		finalp = raw;
331 	}
332 	/*
333 	debug("send %d <<%s>>",len,raw);
334 	*/
335 	if(netd.dp_usehispeed){
336 		if(len > SENDLEN)error("send length too long");
337 		len = SENDLEN;
338 		}
339 	if(netd.dp_pipesim) i = write(netd.dp_pwritefd,finalp,len);
340 	else i = write(netd.dp_linefd,finalp,len);
341 	dump.braw += i;
342 	dump.brawtot += i;
343 # ifdef SWAB
344 	switchem(pp);
345 # endif
346 # else
347 	/* for RAND */
348 	i = write(netd.dp_linefd, (char *)pp,len);
349 # endif
350 	/*
351 	debug("count %d",i);
352 	*/
353 	}
354 
355 static int tooshort;
356 /*
357  *	returns NULL if couldn't get a packet with correct seqno
358  *	chksum not checked here
359  * 	because other programs may want to interrogate checksum
360  */
361 struct packet *getpacket() {
362 	register struct packet *gptr;
363 	register char *p;
364 	register int i;
365 	int n, bcnt, len;
366 	struct packet *decpacket();
367 
368 	bcnt = 0;
369 	errno = 0;
370 	setjmp(env);
371 	alarm(0);
372 	signal(SIGALRM,alarmint);
373 	for(;;){
374 		if(bcnt++ > netd.dp_maxbread)errno = 100;	/* give up */
375 		if(errno == 100){
376 			if(debugflg)putchar('^');
377 			return(NULL);
378 			}
379 		/* decode the buffer, including 6-8 bit conv, etc. */
380 		gptr = decpacket();
381 		if(gptr == NULL){
382 			error("getpacket fails");
383 			return(NULL);
384 		}
385 		if(tooshort || gptr->len < 0 || gptr->len > MAXNBUF){
386 			error("too short p:%d l:%d",gptr->seqno,gptr->len);
387 			continue;
388 		}
389 		if(gptr->seqno == 1 && gptr->pcode != ACK){
390 			debug("got reset");
391 			addtolog(remote,"^R ");
392 			}
393 		if(gptr->pcode == PURGE){
394 			debug("got purge");
395 			continue;		/* never seen */
396 			}
397 		if(gptr->seqno == lastseqno){
398 			if(retransmit)break;
399 			/* send ACK - it was lost first time thru */
400 			len = gptr->len;
401 			n = gptr->pcode;
402 			gptr->len = 0;
403 			gptr->pcode = ACK;
404 			sendpacket(gptr);
405 			gptr->len = len;
406 			gptr->pcode = n;
407 			error("sendlostack %d",lastseqno);
408 			break;
409 			}
410 		/* this is the correct case */
411 		if(gptr->seqno == lastseqno + 1)break;
412 		error("Wrong seq no g: %d last: %d",gptr->seqno,
413 			lastseqno);
414 		}
415 	lastseqno = gptr->seqno;
416 	n = 0;
417 	len = gptr->len + ACKLENGTH;
418 	p = (char *)gptr;
419 	for(i=0; i < len; i++)n ^= *p++;
420 	gptr->chksum = n;
421 	if(n != 0)dump.ncksum++;
422 	dump.nbytercv += gptr->len;
423 	dump.npackrcv++;
424 	return(gptr);
425 }
426 /* read in and decode packet */
427 /* as a side effect sets "tooshort" */
428 static struct packet *decpacket()
429 {
430 # ifndef RAND
431 	register char *p, *q;
432 	register int i,j;
433 	int n, len, ch;
434 	struct packet *pp;
435 	static char cooked[MAXNBUF], raw[MAXNBUF];
436 
437 	/* read in chars to raw, if processed then return in cooked, otherwise
438 	return in raw */
439 	alarm(netd.dp_atime);
440 	tooshort = 0;
441 	if(netd.dp_pipesim){
442 		if(netd.dp_usehispeed)
443 			len = read(fileno(netd.dp_rdfile),raw,SENDLEN);
444 		else {
445 			q = raw;
446 			len = 0;
447 			for(;;){
448 				ch = getc(netd.dp_rdfile);
449 				len++;
450 				if(ch == '\n'){
451 					*q++ = '\n';
452 					break;
453 				}
454 				/* eat up the backslashes */
455 				if(ch == '\\' && netd.dp_use8bit)
456 					ch = getc(netd.dp_rdfile);
457 				*q++ = ch;
458 			}
459 			if(netd.dp_use8bit)len--;
460 		}
461 	}
462 	else if(netd.dp_usehispeed)
463 		len = read(fileno(netd.dp_rdfile),raw,SENDLEN);
464 	else len = read(netd.dp_linefd,raw,MAXNBUF);
465 	alarm(0);
466 	if(len == 0)fprintf(stderr,"eof pip %d\n",fileno(netd.dp_rdfile));
467 	if(len <= 0)return(NULL);
468 	raw[len] = 0;
469 	dump.braw += len;
470 	dump.brawtot += len;
471 	/*
472 	debug("receive %d <<%s>>",len,raw);
473 	*/
474 	/* if 8 bit the all we need to do is return */
475 	if(netd.dp_usehispeed)return((struct packet *)raw);
476 	if(netd.dp_use8bit){
477 		pp = (struct packet *)raw;
478 		if(len != ACKLENGTH + pp->len)tooshort = 1;
479 		return(pp);
480 	}
481 	/* remove this loop later */
482 	for(p=raw; *p; p++)
483 		if(*p == '}')*p = '\\';
484 	p = raw;
485 	q = cooked;
486 	n = (len+3) /4;
487 	while(n--){
488 		if(*p == '\n')break;
489 		if(*p < INCR || *p & 0200)error("bad char %o\n",*p);
490 		i =  *p++ - INCR;
491 		j =  *p++ - INCR;
492 		*q++ = ((j & 03) << 6) | (i & 077);
493 		i =  *p++ -INCR;
494 		*q++ = ((i & 017) << 4) | ((j >> 2) & 017);
495 		j =  *p++ - INCR;
496 		*q++ = ((j & 077) << 2) | ((i >> 4) & 03);
497 		}
498 	*q = 0;
499 	pp = (struct packet *)cooked;
500 # ifdef SWAB
501 	switchem(pp);
502 # endif
503 	if(len != ((ACKLENGTH + pp->len + 2)/3)*4 + 1) tooshort = 1;
504 # else
505 	/* for RAND */
506 	/* not sure of the length computation */
507 	if(len != ACKLENGTH + gptr->len) tooshort = 1;
508 # endif
509 	return((struct packet *)cooked);
510 }
511 
512 # ifdef SWAB
513 switchem(pp)
514 register struct packet *pp; {
515 	register short *p;
516 	p = &(pp->seqno);
517 	swab(p, p, 2);
518 	p = &(pp->len);
519 	swab(p, p, 2);
520 }
521 # endif
522