Commit | Line | Data |
---|---|---|
f22be67b KM |
1 | static char sccsid[] = "@(#)prot.c 4.1 (Berkeley) %G%"; |
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 |