|
|
1.1 root 1: /*
2: * gpkt.c
3: *
4: * author: Peter S. Housel
5: *
6: * The |cksum()| routine is taken from UUPC, Copyright 1985, 1986, 1987 by
7: * Richard H. Lamb, with (possible) changes Copyright 1987 by Stuart Lynne
8: *
9: * All other code is Copyright 1989 by Peter S. Housel.
10: * Redistribution for any purpose is permitted provided this message
11: * is included intact. No warranty of any sort is provided.
12: *
13: * gpkt version 1.1 7/21/89
14: */
15:
16: /* This program was written based on the original UUPC 'g' driver,
17: * John Gilmore's version of uuslave, and Greg Chesson's protocol
18: * description article. The last was especially helpful.
19: *
20: * This code was written around a severely hacked version of UUPC.
21: * The call interface is almost identical to the original, but
22: * the internal implementation is quite different. Also, many
23: * functions are called that were not available in the original
24: * UUPC support functions. It should serve as an adequate framework.
25: *
26: * The framing strategy requires that a |read()| be interruptable
27: * by a |SIGALRM|. No "busy wait" or nonblocking read is required.
28: */
29: extern int bad_count;
30: extern int total_errors;
31:
32: #include "dcp.h"
33: #include "alarm.h"
34:
35: #define MAXPKT 64 /* incredibly conservative... actually 4096 */
36: #define SWINDOW 3 /* initial send window size */
37: #define RWINDOW 7 /* window size we want to recieve */
38: #define SPKTSIZE 64 /* initial send packet size */
39: #define RPKTSIZE 64 /* window size we want to receive */
40:
41: #define MAXLOST 5 /* max lost packets (closes or such) */
42: #define TIMEOUT 5 /* max seconds of before timeout */
43:
44: #define LOSTPKT -1 /* packet lost, got timeout */
45: #define BADPKT -2 /* bad checksum, or data read timed out */
46:
47: #define ENV_DLE 0 /* framing char at start of envelope */
48: #define ENV_K 1 /* packet length specifier */
49: #define ENV_C0 2 /* low-order checksum */
50: #define ENV_C1 3 /* high-order checksum */
51: #define ENV_C 4 /* control byte */
52: #define ENV_X 5 /* xor check byte */
53: #define ENV_LEN 6 /* overall envelope length */
54:
55: #define TT_CTRL 0 /* control packet */
56: #define TT_DATA 2 /* data packet */
57: #define TT_SDATA 3 /* short data packet */
58: #define TT_ALTCHAN 1 /* 'alternate channel' - invalid */
59:
60: #define X_CLOSE 1 /* close down protocol */
61: #define X_RJ 2 /* reject recieved packet */
62: #define X_SRJ 3 /* selectively reject packet - invalid */
63: #define X_RR 4 /* reciever ready */
64: #define X_INITC 5 /* third init packet */
65: #define X_INITB 6 /* second init packet */
66: #define X_INITA 7 /* first init packet */
67:
68: #define OP_OPEN 1 /* request to open/init protocol */
69: #define OP_CLOSE 2 /* request to close protocol */
70: #define OP_WRITE 3 /* request to send packet */
71: #define OP_READ 4 /* request to read packet */
72:
73: #define MAGIC (unsigned) 0xAAAA /* checksum magic value */
74:
75: /* from original dcp - determinie if a <= b < c, for mod 8 seq numbers */
76: #define between(a,b,c) (((a)<=(b) && (b)<(c)) \
77: || ((c)<(a) && (a)<=(b)) \
78: || ((b)<(c) && (c)<(a)))
79:
80: unsigned cksum(/* unsigned char *data, int len */);
81:
82: struct {
83: short sdata; /* 'is this a short data packet' flag */
84: unsigned length; /* length of this packet */
85: unsigned char *bufloc; /* location of this data pkt's buffer */
86: }
87: inpbufs[8], outbufs[8]; /* input/output queues */
88:
89: static int needack; /* do we need to acknowledge a rcv'd pkt? */
90: static int neednack; /* do we need to reject a recieved pkt? */
91: static int recv; /* seq. number of last correctly rcv'd pkt */
92: static int lastread; /* seq. number of last pkt ret. to caller */
93: static int send; /* first packet in output window */
94: static int next; /* next pkt to send send <= next < nstuff */
95: static int nstuff; /* next loc. to stuff a pkt in output queue */
96: /* (last pkt in output window) + 1 */
97: static int initpk; /* current init sequence send packet */
98: static int skipping; /* skipping out-of-seq packets after RJ */
99: static int spktsize; /* send data size (requested by other end) */
100: static int swindow; /* send output window size (ditto) */
101: static int nlost; /* number of consecutive timeouts */
102: static int chanopen = 0; /* 'channel open' flag */
103:
104: static unsigned char buf[ENV_LEN + RPKTSIZE+ BUFSIZ]; /* packet framing buffer */
105: static unsigned char *low, *high; /* framing buffer limits */
106:
107: static unsigned char *ibufset, *obufset; /* i/o packet buffer sets */
108:
109: /* |gopenpk()| opens the 'g' packet protocol on the serial line. It
110: * initializes its state variables, allocates buffers, etc., then calls
111: * |gmachine()| to do the actual protocol negotiation/initialization.
112: */
113: gopenpk()
114: {
115: int i; /* index */
116: unsigned char *p, *q; /* pointers into buffer set */
117:
118: high = low = buf; /* empty input buffer */
119: needack = neednack = 0; /* don't need to accept or reject anything */
120: initpk = X_INITA; /* send INITA during initialization */
121: recv = lastread = 0; /* initial empty read queue, seq=0 */
122: send = next = nstuff = 1; /* first in output queue, seq=1 */
123: skipping = nlost = 0; /* nothing lost yet, not skipping */
124:
125: if (gmachine(OP_OPEN) < 0) /* do the open */
126: return -1;
127: /* allocate send and recieve buffers */
128: if (NULL == (p = ibufset = (unsigned char *)malloc(8 * RPKTSIZE)))
129: return -1;
130: if (NULL == (q = obufset = (unsigned char *)malloc(8 * spktsize)))
131: return -1;
132: for(i = 0; i < 8; ++i) {
133: inpbufs[i].bufloc = p;
134: p += RPKTSIZE;
135: outbufs[i].bufloc = q;
136: q += spktsize;
137: }
138:
139: pktsize = spktsize; /* for dcp compatibility */
140: return 0;
141: }
142:
143: /* |gclosepk()| closes down the packet protocol using the |OP_CLOSE| operation
144: * of |gmachine()|.
145: */
146: gclosepk()
147: {
148: return gmachine(OP_CLOSE);
149: }
150:
151: /* |ggetpkt()| reads one packet and returns it. The data is stored in
152: * the buffer pointed to by |cdata|, and the length is stored in |*lenp|.
153: * It calls |gmachine()| to get the data, and copies it from the proper input
154: * buffer to the user's buffer area. "Short data" packets are handled here,
155: * as opposed to within |gmachine()|.
156: */
157: int
158: ggetpkt(cdata, lenp)
159: unsigned char *cdata;
160: int *lenp;
161: {
162: int i;
163: int nextread;
164: unsigned char *bufp;
165:
166: if (!chanopen)
167: return -1;
168: nextread = (lastread + 1) & 7;
169: printmsg(M_HIGHPROTO, "waiting for input pkt seq=%d", nextread);
170: if (gmachine(OP_READ) < 0)
171: return -1;
172: *lenp = inpbufs[nextread].length;
173: bufp = inpbufs[nextread].bufloc;
174: printmsg(M_HIGHPROTO, " lenp starts out at %d, and *bufp is %d",
175: *lenp, *bufp);
176: if (inpbufs[nextread].sdata) {
177: if (*bufp < 128) {
178: /* less than 128 bytes shorter than packet length */
179: printmsg(M_HIGHPROTO, "decrementing lenp once");
180: *lenp -= *bufp++;
181: } else { /* more than 128 bytes shorter */
182: printmsg(M_HIGHPROTO, "decrementing lenp twice");
183: *lenp -= (*bufp++ & 127) * 256;
184: *lenp -= *bufp++;
185: }
186: }
187: printmsg(M_HIGHPROTO,
188: "about to call memcpy, cdata %04x, bufp %04x, lenp %04x, *lenp %d",
189: cdata, bufp, lenp, *lenp);
190: i = *lenp;
191: memcpy(cdata, bufp, i);
192: lastread = nextread;
193: return 0;
194: }
195:
196: /* |gsendpkt()| queues the packet pointed to by |cdata| of length |len|
197: * into the packet driver output buffer, and calls |gmachine()| to send
198: * it. (|gmachine()| will return when the packet has been transmitted but
199: * not necessarily acknowledged, with window size greater than 1.) If
200: * |flag| is nonzero, |cdata| is considered a null-terminated string
201: * which will be null-padded to the packet size and transmitted.
202: */
203: int
204: gsendpkt(cdata, len, flag)
205: unsigned char *cdata;
206: int len, flag;
207: {
208: unsigned char *destp;
209: unsigned diff;
210:
211: if (!chanopen)
212: return -1;
213:
214: outbufs[nstuff].sdata = 0;
215: destp = outbufs[nstuff].bufloc;
216: if (flag && len < spktsize) {
217: printmsg(M_HIGHPROTO, "Padded message packet |%s|", cdata);
218: strncpy(destp, cdata, spktsize);
219: } else {
220: if ((diff = spktsize - len) > 127) { /* really short packet? */
221: *destp++ = (diff >> 8) | 128;
222: *destp++ = diff & 255;
223: outbufs[nstuff].sdata = 1;
224: } else if (diff > 0) { /* short packet */
225: *destp++ = diff;
226: outbufs[nstuff].sdata = 1;
227: } else
228: outbufs[nstuff].sdata = 0;
229: memcpy(destp, cdata, len); /* copy into buffer */
230: }
231: printmsg(M_HIGHPROTO, "queued data packet seq=%d len=%d", nstuff, len);
232: outbufs[nstuff].length = spktsize;
233: nstuff = (nstuff + 1) & 7;
234:
235: return gmachine(OP_WRITE);
236: }
237:
238: /* |gmachine()| is the heart of the 'g' packet driver. Its basic strategy
239: * is:
240: * - transmit a packet if necessary
241: * - return if possible,
242: * - else read a packet and act on it
243: * - repeat
244: *
245: * |OP_OPEN| requests that the channel be opened, and |OP_CLOSE| requests that
246: * it be closed. If |why| is |OP_WRITE|, |gmachine()| will return when the
247: * last packet in the output queue has been transmitted (but not necessarily
248: * acknowledged). |OP_READ| requests will return as soon as a new packet
249: * arrives.
250: */
251: int gmachine(why)
252: int why;
253: {
254: int xxx, yyy, len;
255: unsigned char *bufp;
256: int shortdat;
257: int i;
258: while(1) {
259: if (OP_CLOSE == why) {
260: gspack(TT_CTRL, X_CLOSE, 0, (unsigned char *)NULL, 0);
261: chanopen = 0;
262: printmsg(M_MEDPROTO, "Sending CLOSE request...");
263: } else if (neednack) {
264: gspack(TT_CTRL, X_RJ, recv, (unsigned char *)NULL, 0);
265: neednack = 0;
266: printmsg(M_MEDPROTO, "Sending RJ... recv=%d", recv);
267: } else if (send != nstuff /* nonzero output queue? */
268: && between(send, next, nstuff) /* 'next' in queue? */
269: && between(send, next, (send + swindow) & 7)) {
270: /* in out win. */
271: printmsg(M_MEDPROTO, "Sending data packet %d", next);
272: gspack(outbufs[next].sdata ? TT_SDATA : TT_DATA,
273: next, recv, outbufs[next].bufloc, outbufs[next].length);
274: needack = 0;
275: next = (next + 1) & 7;
276: if (OP_WRITE == why && next == nstuff)
277: /* go back for more */
278: return 0;
279: } else if (needack) {
280: gspack(TT_CTRL, X_RR, recv, (unsigned char *)NULL, 0);
281: needack = 0;
282: printmsg(M_MEDPROTO, "Sending RR... recv=%d", recv);
283: } else if (OP_OPEN == why) {
284: if (X_INITB == initpk)
285: i = ilog2(RPKTSIZE) - 5;
286: /* INITB contains packet size, */
287: else
288: i = RWINDOW;
289: /* INITA, INITC contain window size */
290: printmsg(M_MEDPROTO, "inita/b pkt/wndow i = %d", i);
291: /* ??? examine for INITC */
292: gspack(TT_CTRL, initpk, i, (unsigned char *)NULL, 0);
293: }
294: if (OP_READ == why && recv != lastread) {
295: return 0;
296: }
297: shortdat = 0;
298: bufp = buf + ENV_LEN;
299: switch(grpack(&xxx, &yyy, &len)) {
300: case LOSTPKT:
301: printmsg(M_MEDPROTO, "Lost packet...");
302: if (nlost > MAXLOST) {
303: plog(M_DATA, "Lost packets from other side");
304: return -1;
305: }
306: next = send; /* retransmit last un-ack'ed pkt, */
307: if (OP_READ == why) /* request retransmit */
308: neednack = 1;
309: skipping = 0;
310: break;
311: case BADPKT:
312: printmsg(M_MEDPROTO, "Bad packet...");
313: bad_count++;
314: total_errors ++;
315: neednack = 1; /* reject! */
316: skipping = 1; /* ignore the rest of the 'window' */
317: if (bad_count > 20) {
318: plog(M_DATA, "Too many errors");
319: return -1;
320: }
321: break;
322: case TT_SDATA:
323: shortdat = 1;
324: /* fall through */
325: case TT_DATA:
326: bad_count = 0;
327: send = (yyy + 1) & 7; /* recieve acknowledgement */
328: if (xxx != ((recv + 1) & 7)) {
329: printmsg(M_MEDPROTO,
330: "Recieved data out of sequence (%d != %d)",
331: xxx, (recv + 1) & 7);
332: if (!skipping) {
333: neednack = 1; /* we must have missed one */
334: skipping = 1;
335: } else
336: printmsg(M_MEDPROTO, "(Ignoring)");
337: } else {
338: recv = xxx;
339: /* this is most recent correct pkt */
340: needack = 1; /* we will ack it */
341: skipping = 0;
342: inpbufs[xxx].length = len;
343: inpbufs[xxx].sdata = shortdat;
344: memcpy(inpbufs[xxx].bufloc, bufp, len);
345: }
346: break;
347: case TT_CTRL:
348: bad_count = 0;
349: skipping = 0;
350: switch(xxx) {
351: case X_CLOSE:
352: printmsg(M_MEDPROTO, "* CLOSE *");
353: gspack(TT_CTRL, X_CLOSE, 0, NULL, 0);
354: chanopen = 0;
355: if (OP_CLOSE == why) /* expected? */
356: return 0;
357: else
358: return -1; /* nope */
359: case X_RJ:
360: printmsg(M_MEDPROTO, "got RJ yyy=%d", yyy);
361: next = send = (yyy + 1) & 7;
362: break;
363: case X_RR:
364: printmsg(M_MEDPROTO, "got RR yyy=%d", yyy);
365: send = (yyy + 1) & 7;
366: break;
367: case X_INITC:
368: printmsg(M_MEDPROTO, "* INITC *");
369: swindow = yyy;
370: if (X_INITC == initpk) {
371: chanopen = 1;
372: return 0;
373: }
374: break;
375: case X_INITB:
376: spktsize = 32 << yyy;
377: printmsg(M_MEDPROTO, "* INITB *, spktsz %d",
378: spktsize);
379: if (X_INITB == initpk)
380: initpk = X_INITC;
381: break;
382: case X_INITA:
383: printmsg(M_MEDPROTO, "* INITA *");
384: swindow = yyy;
385: initpk = X_INITB;
386: break;
387: default:
388: printmsg(M_MEDPROTO,
389: "bad control packet: xxx=%d", xxx);
390: }
391: break;
392: default:
393: break;
394: }
395: }
396: }
397:
398: /*
399: * |grpack()| is responsible for reading one 'g'-protocol packet from the
400: * input communications channel. This includes framing, detecting timeouts,
401: * and checksum validation.
402: * The basic strategy is to keep a count of how many bytes have currently
403: * been read and how many are needed. When enough bytes for a packet header
404: * ("envelope") have been read, it is validated. If it is valid, and it
405: * has a nonzero data segment, the data portion is read. When it has
406: * everything, it does a checksum test and returns, with the control
407: * information stored in |*xxxp|, |*yyyp|, and the data segment length stored
408: * in |*lenp|.
409: */
410: int grpack(xxxp, yyyp, lenp)
411: int *xxxp, *yyyp;
412: int *lenp;
413: {
414: int need; /* need how many bytes? */
415: int env; /* 'have pkt envelope' flag */
416: int gotdle; /* do we have envelope hdr? */
417: int tt; /* packet type */
418: unsigned sum; /* checksum */
419: int remain; /* bytes which we stil need */
420: int i;
421:
422: env = gotdle = 0;
423: need = ENV_LEN; /* initially, need a header */
424:
425: SETALRM(TIMEOUT); /* time out if we don't have a packet */
426:
427: while(1) {
428: if (low == high) /* prevent framebuffer overruns */
429: low = high = buf;
430: printmsg(M_LOWPROTO, "=> l=%d h=%d g=%d", (int)(low - buf),
431: (int)(high - buf), gotdle);
432: while((remain = need - (high - low)) > 0) {
433: if (timedout || (i = sread2(high, remain)) < 0) {
434: CLRALRM();
435: ++nlost;
436: low = high = buf;
437: /* empty out partial packet, if any */
438: return env ? BADPKT : LOSTPKT;
439: }
440: high += i; /* got some data - move upper limit up */
441: }
442: if (!gotdle) {
443: while(low < high) { /* look for header 'DLE' prefix */
444: if (DLE == *low) {
445: gotdle = 1;
446: break;
447: } else
448: ++low;
449: }
450: continue;
451: } else if (!env) {
452: /* found DLE, but haven't found header yet */
453: if (low > buf) { /* move envelope to buf beginning */
454: register unsigned char *dst = buf;
455: while(low < high)
456: *dst++ = *low++;
457: low = buf;
458: high = dst;
459: }
460: if (buf[ENV_X] != (buf[ENV_K]^buf[ENV_C0]^
461: buf[ENV_C1]^buf[ENV_C]) ||
462: buf[ENV_K] < 1 || buf[ENV_K] > 9) {
463: /* valid? */
464: ++low;
465: gotdle = 0;
466: printmsg(M_LOWPROTO,
467: "grpack: rejecting an envelope");
468: continue;
469: }
470: env = 1; /* we have an envelope */
471: /* store away control info */
472: tt = (buf[ENV_C] >> 6) & 3;
473: *xxxp = (buf[ENV_C] >> 3) & 7;
474: *yyyp = (buf[ENV_C] ) & 7;
475: if (buf[ENV_K] == 9) /* does it have data? */
476: *lenp = 0;
477: else
478: *lenp = 16 << buf[ENV_K];
479: /* size = 32 * 2^(k-1) */
480: need += *lenp; /* now need that many more */
481: printmsg(M_LOWPROTO,
482: "grpack: tt=%d, xxx=%d, yyy=%d, need=%d",
483: tt, *xxxp, *yyyp, need);
484: continue;
485: } else { /* have everything we need */
486: if (*lenp)
487: sum = MAGIC - (cksum(buf + ENV_LEN, *lenp) ^
488: buf[ENV_C]);
489: else {
490: sum = MAGIC - buf[ENV_C];
491: printmsg(M_LOWPROTO,
492: "0 length data, data %x, %x, %x", buf[ENV_C],
493: buf[ENV_C0], buf[ENV_C1]);
494: }
495: if (((sum >> 8) & 0xFF) != buf[ENV_C1]
496: || (sum & 0xFF) != buf[ENV_C0]) {
497: CLRALRM();
498: nlost = 0;
499: printmsg(M_LOWPROTO,
500: "grpack: bad check, sum is %x", sum);
501: low += ENV_LEN;
502: /* we will search bad data seg for a header */
503: return BADPKT;
504: } else {
505: CLRALRM(); /* got it all... return */
506: nlost = 0;
507: low += need;
508: if (*lenp)
509: printmsg(M_DATA, "|%s|",
510: visbuf(buf + ENV_LEN, *lenp));
511: return tt;
512: }
513: }
514: }
515: }
516:
517: /*
518: * gspack simply sends out a packet, by assembling an envelope and writing
519: * it, and the data segment, to the communications channel.
520: */
521: gspack(tt, xxx, yyy, data, size)
522: int tt, xxx, yyy;
523: unsigned char *data;
524: int size;
525: {
526: unsigned char envelope[ENV_LEN]; /* packet envelope */
527: unsigned sum; /* checksum */
528: unsigned char ctrl; /* header control byte */
529: int k; /* size = 32 * 2^(k-1) */
530:
531: ctrl = ((tt & 3) << 6) | ((xxx & 7) << 3) | (yyy & 7);
532: if (0 == size) {
533: k = 9; /* no data seg */
534: sum = MAGIC - ctrl;
535: } else {
536: int tmp = size;
537: for(k = -5; tmp != 0; ++k) /* find k */
538: tmp >>= 1;
539: sum = MAGIC - (cksum(data, size) ^ ctrl);
540: }
541: envelope[ENV_DLE] = DLE;
542: envelope[ENV_K] = k;
543: envelope[ENV_C1] = (sum >> 8) & 0xFF;
544: envelope[ENV_C0] = sum & 0xFF;
545: envelope[ENV_C] = ctrl;
546: envelope[ENV_X] = k ^ envelope[ENV_C0] ^ envelope[ENV_C1] ^ ctrl;
547: swrite(envelope, ENV_LEN); /* send envelope */
548: if (0 != size)
549: swrite(data, size); /* send data segment */
550: printmsg(M_LOWPROTO, "gspack: tt=%d xxx=%d yyy=%d k=%d sum=%x",
551: tt, xxx, yyy, k, sum);
552: if (0 != size)
553: printmsg(M_DATA, "|%s|", visbuf(data, size));
554: }
555:
556: /*
557: * chksum(data, len) came directly from dcp. It checksums the given data
558: * area using the g protocol algorithm.
559: */
560: unsigned cksum(data, len)
561: int len;
562: unsigned char *data;
563: {
564: unsigned int i, j, tmp, chk1, chk2;
565: chk1 = 0xffff;
566: chk2 = 0;
567: j = len;
568: for (i = 0; i < len; i++) {
569: if (chk1 & 0x8000) {
570: chk1 <<= 1;
571: chk1++;
572: } else
573: chk1 <<= 1;
574: tmp = chk1;
575: chk1 += (data[i] & 0xff);
576: chk2 += chk1 ^ j;
577: if ((chk1 & 0xffff) <= (tmp & 0xffff))
578: chk1 ^= chk2;
579: j--;
580: }
581: return (chk1 & 0xffff);
582: }
583:
584: /* |ilog2(value)| returns (int)floor(log2(value)).
585: */
586: int ilog2(value)
587: unsigned value;
588: {
589: int i;
590:
591: if (value == 0)
592: return -1;
593: for(i = 0; value > 1; ++i)
594: value >>= 1;
595: return i;
596: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.