/* * Packet protocol processor */ #include "../h/param.h" #include "../h/stream.h" #include "../h/conf.h" #include "../h/ioctl.h" #include "../h/ttyld.h" #include "pk.h" struct pack { u_char istate; u_char ostate; u_char xlogseg; /* code for xmit segment size */ u_char S; /* current output seqno */ u_char SN; /* next output seqno */ u_char XW; /* size of output window */ u_char R; /* current input seqno */ u_char timer; short xsegment; /* output segment size */ short iwant; /* # chars needed before i q enable */ struct block *xpacks[8]; /* waiting output blocks */ struct { u_char k; /* packet size */ u_char c0; /* low checksum byte */ u_char c1; /* high checksum byte */ u_char C; /* control byte */ u_char x; /* header check */ } hdr; /* copy of header of received segment */ struct queue *rdq; /* associated read queue */ }; #define PK_TYPE 0xC0 #define PKCNTL 0x00 #define PKDATA 0x80 #define PKSDATA 0xC0 #define PK_CLOSE 010 #define PK_RJ 020 #define PK_RR 040 #define PK_INITC 050 #define PK_INITB 060 #define PK_INITA 070 #define CHECK 0125252 #define WDATA 01 #define PKDELIM 02 #define PKREJ 04 #define SYN 020 /* sic */ #define SINITA 1 #define SINITB 2 #define SINITC 3 #define OPEN 4 #define LCLOSE 5 #define RCLOSE 6 #define PKPRI 28 #define DKDATA 0400 #define RWINSIZE 3 #define RSEGSIZE 1 /* 64 bytes */ #define PKTIME 5 struct pack packs[NPK]; short pksizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 }; u_short pkc_sum; u_short pkc_x; int pkc_size; int pkiput(), pkisrv(), pkoput(), pkosrv(), pkopen(), pkclose(); static struct qinit pkrinit = { pkiput, pkisrv, pkopen, pkclose, 512, 64 }; static struct qinit pkwinit = { pkoput, pkosrv, pkopen, pkclose, 128, 65 }; struct streamtab pkinfo = { &pkrinit, &pkwinit }; pkopen(q) register struct queue *q; { register struct pack *pkp; register s; static timer = 0; int pktimer(); if (q->ptr) { pkp = (struct pack *)q->ptr; if (pkp->ostate != OPEN) return(0); /* don't reopen half-open chans */ return(1); } else { for (pkp = packs; pkp->ostate!=0; pkp++) if (pkp >= &packs[NPK]) return(0); pkp->rdq = q; q->ptr = (caddr_t)pkp; WR(q)->ptr = (caddr_t)pkp; pkp->S = 1; pkp->SN = 1; pkp->R = 0; putctl(q->next, M_FLUSH); pkp->ostate = SINITA; pkp->istate = 0; pkp->timer = 2; pkscntl(pkp, PK_INITA+RWINSIZE); } if (timer == 0) { timer = 1; timeout(pktimer, (caddr_t)NULL, 60); } s = spl5(); while (pkp->ostate != OPEN) { switch (tsleep((caddr_t)pkp, PKPRI, 15)) { case TS_OK: continue; case TS_SIG: case TS_TIME: pkp->ostate = 0; splx(s); return(0); } } q->flag |= QDELIM; splx(s); return(1); } /* * Shut it down. * The problem is to dispose of unacked stuff in the window. * -- no real solution; the receiver might hang on for hours. * Give it 15 seconds. */ pkclose(q) register struct queue *q; { register struct pack *pkp; register s = spl5(); register i; pkp = (struct pack *)q->ptr; pkp->ostate = LCLOSE; flushq(q, 1); for (i=0; pkp->S < pkp->SN && i<15; i++) tsleep((caddr_t)pkp, PKPRI, 1); splx(s); flushq(WR(q), 1); pkrack(pkp, pkp->SN-1, 0); pkscntl(pkp, PK_CLOSE); pkp->ostate = 0; } /* * Process a bunch of input */ pkisrv(q) register struct queue *q; { register struct pack *pkp = (struct pack *)q->ptr; register struct block *bp; register segsize, msgsize; int R, OK; u_char c; more: while ((pkp->istate & WDATA) == 0) { if (pkwant(pkp, 6)==0) return; pkgetb(q, &c, 1); if (c != SYN) continue; pkgetb(q, (u_char *)&pkp->hdr, 5); if (pkp->hdr.x != (pkp->hdr.k ^ pkp->hdr.c0 ^ pkp->hdr.c1 ^ pkp->hdr.C)) { /* bad header? */ continue; } switch (pkp->hdr.C & PK_TYPE) { case PKCNTL: pkp->istate &= ~PKREJ; pkcntl(pkp); continue; case PKDATA: case PKSDATA: if (pkp->ostate==SINITC) { pkp->ostate = OPEN; wakeup((caddr_t)pkp); } if (pkp->S != pkp->SN) /* if need an ack */ pkrack(pkp, pkp->hdr.C&07, 0); if (pkp->hdr.k<1 || pkp->hdr.k>8) { continue; } pkp->istate |= WDATA; break; default: continue; } break; } if (q->next->flag&QFULL) return; msgsize = segsize = pksizes[pkp->hdr.k]; if (pkwant(pkp, segsize) == 0) return; /* * Now have a segment's worth of data; if in window, compute checksum * and accept if all is OK */ R = (pkp->hdr.C>>3) & 07; if (R <= pkp->R) R += 8; /* compute checksum */ OK = 0; pkc_sum = -1; pkc_x = 0; pkc_size = segsize; for (bp = q->first; segsize>0; bp = bp->next) { register sz = bp->wptr - bp->rptr; pkcksum(bp->rptr, sz); segsize -= sz; } segsize = msgsize; pkc_sum ^= pkp->hdr.C; pkc_sum += (pkp->hdr.c1<<8) + pkp->hdr.c0; if (pkc_sum != CHECK || R != pkp->R+1) { /* Bad? */ if (R < pkp->R+RWINSIZE) { flushq(q, 0); if ((pkp->istate & PKREJ) == 0) pkscntl(pkp, PK_RJ+pkp->R); pkp->istate &= ~WDATA; pkp->istate |= PKREJ; goto more; } msgsize = -1; /* duplicate; just ignore */ } else { /* It's good */ pkp->R = (pkp->R+1)&07; OK = 1; if ((pkp->hdr.C&PK_TYPE) == PKSDATA) { pkgetb(q, &c, 1); msgsize -= c & 0x7F; segsize -= 1; if (c&0x80) { pkgetb(q, &c, 1); msgsize -= c << 7; segsize -= 1; } } } pkp->istate &= ~PKREJ; pkscntl(pkp, PK_RR+pkp->R); while (segsize > 0) { register i; bp = getq(q); i = bp->wptr - bp->rptr; if (i <= msgsize) { (*q->next->qinfo->putp)(q->next, bp); msgsize -= i; segsize -= i; continue; } if (msgsize>0) { register struct block *bp1 = allocb(msgsize); if (bp1) { bcopy(bp->rptr, bp1->wptr, msgsize); bp->rptr += msgsize; bp1->wptr += msgsize; (*q->next->qinfo->putp)(q->next, bp1); segsize -= msgsize; msgsize = 0; putbq(q, bp); continue; } else panic("pk alloc\n"); } if (i <= segsize) { freeb(bp); segsize -= i; continue; } bp->rptr += segsize; putbq(q, bp); break; } if (OK) putctl(q->next, M_DELIM); pkp->istate &= ~WDATA; goto more; } /* * Packet arrives. */ pkiput(q, bp) struct queue *q; register struct block *bp; { register struct pack *pkp; register c; if ((pkp = (struct pack *)q->ptr)==NULL || pkp->ostate==LCLOSE) { freeb(bp); return; } switch (bp->type) { case M_DATA: if (q->flag&QFULL) { freeb(bp); return; } if ((pkp->iwant -= bp->wptr - bp->rptr) <= 0) { pkp->iwant = 0; qenable(q); } putq(q, bp); return; case M_HANGUP: pkp->ostate = RCLOSE; flushq(WR(q), 0); pkrack(pkp, pkp->SN-1, 0); qenable(q); freeb(bp); return; case M_IOCACK: case M_IOCNAK: (*q->next->qinfo->putp)(q->next, bp); return; default: freeb(bp); return; } } /* * --- Output processor */ /* * accept data from writer * -- handle all non-data messages */ pkoput(q, bp) register struct queue *q; register struct block *bp; { register struct pack *pkp = (struct pack *)q->ptr; register union stmsg *sp; if (pkp->ostate == RCLOSE) { freeb(bp); return; } switch (bp->type) { case M_DELIM: if (pkp->istate & PKDELIM) goto isdata; pkp->istate |= PKDELIM; /* flow through */ case M_FLUSH: freeb(bp); return; case M_IOCTL: sp = (union stmsg *)bp->rptr; switch (sp->ioc0.com) { case TIOCGETP: sp->ioc1.sb.sg_erase = 0; sp->ioc1.sb.sg_kill = 0; sp->ioc1.sb.sg_flags = RAW; bp->wptr = bp->rptr+sizeof(struct ioc1); bp->type = M_IOCACK; qreply(q, bp); break; default: (*q->next->qinfo->putp)(q->next, bp); break; } return; default: freeb(bp); return; case M_DATA: pkp->istate &= ~PKDELIM; isdata: putq(q, bp); if (pkp->SN < pkp->S + pkp->XW) qenable(q); return; } } /* * PK out server: * if space in window, process queue * take care of splitting packets bigger than segment size * if timer has expired, retransmit */ pkosrv(q) register struct queue *q; { register struct pack *pkp = (struct pack *)q->ptr; register struct block *bp, *xbp; if (pkp->timer==0 && pkp->S != pkp->SN) pkxmit(q, pkp->xpacks[pkp->S], pkp->S); while (pkp->SN < pkp->S + pkp->XW) { if ((bp = getq(q)) == NULL) break; if (bp->wptr-bp->rptr > pkp->xsegment) { if ((xbp = allocb(pkp->xsegment)) == NULL) { putbq(q, bp); return; } bcopy(bp->rptr, xbp->wptr, pkp->xsegment); bp->rptr += pkp->xsegment; xbp->wptr += pkp->xsegment; putbq(q, bp); bp = xbp; } bp->type = M_DATA; /* M_DELIM -> 0-length write */ pkp->xpacks[pkp->SN & 07] = bp; pkxmit(q, bp, pkp->SN); pkp->SN++; } } /* * Send out a packet, with header. * -- only a "pointer" to the data is sent; the original * is kept. */ pkxmit(q, bp, pkno) struct queue *q; register struct block *bp; unsigned pkno; { register struct block *hbp; register size; register seg; register struct pack *pkp = (struct pack *)q->ptr; register struct block *sbp = NULL; register sizeadjust = 0; static u_char zero[64]; if (bp == (struct block *)NULL) { printf("0 bp in pkxmit\n"); return; } pkp->timer = PKTIME; if ((hbp = allocb(8)) == NULL) return; /* hope timeouts will recover */ pkno &= 07; size = bp->wptr - bp->rptr; /* compute header */ hbp->wptr += 6; /* header size */ hbp->rptr[0] = SYN; hbp->rptr[1] = pkp->xlogseg; seg = pkp->xsegment; pkc_sum = -1; pkc_x = 0; pkc_size = seg; if (size < seg) { hbp->rptr[4] = PKSDATA | (pkno << 3) | pkp->R; if ((sbp = allocb(2)) == NULL) { freeb(hbp); return; } /* hack in the size bytes */ if ((seg - size) <= 127) { *sbp->wptr++ = seg - size; sizeadjust = 1; } else { *sbp->wptr++ = 0x80 + ((seg - size) & 0x7F); *sbp->wptr++ = (seg - size) >> 7; sizeadjust = 2; } pkcksum(sbp->rptr, sizeadjust); pkcksum(bp->rptr, size); seg -= size; while (seg>0) { pkcksum(zero, 64); seg -= 64; } } else { if (size > seg) { printf("pk size botch\n"); return; } hbp->rptr[4] = PKDATA | (pkno<<3) | pkp->R; pkcksum(bp->rptr, size); } pkc_sum = CHECK - (pkc_sum ^ hbp->rptr[4]); hbp->rptr[2] = pkc_sum; hbp->rptr[3] = pkc_sum>>8; hbp->rptr[5] = hbp->rptr[1]^hbp->rptr[2]^hbp->rptr[3]^hbp->rptr[4]; /* send header */ (*q->next->qinfo->putp)(q->next, hbp); /* send size */ if (sbp) (*q->next->qinfo->putp)(q->next, sbp); /* send data ptr */ if (hbp = allocb(0)) { /* if fail, pray for timeout */ hbp->rptr = bp->rptr; /* copy ptrs */ hbp->wptr = bp->wptr; (*q->next->qinfo->putp)(q->next, hbp); /* send data */ } /* send padding */ seg = pkp->xsegment - sizeadjust; while (size < seg) { register deficit = seg - size; if (deficit > 64) deficit = 64; if (hbp = allocb(deficit)) { hbp->rptr = zero; hbp->wptr = zero + deficit; (*q->next->qinfo->putp)(q->next, hbp); } size += deficit; } } /* * Receive an ack for a transmitted packet. * Advance the window. Retransmit stacked up stuff if required. */ pkrack(pkp, packno, rxmit) register unsigned packno; register struct pack *pkp; { register struct block **bpp; register s = spl5(); packno &= 07; if (packno < pkp->S) packno += 8; pkp->timer = PKTIME; if (packno < pkp->SN) while (pkp->S <= packno) { bpp = &pkp->xpacks[pkp->S & 07]; if (*bpp) { freeb(*bpp); *bpp = NULL; } pkp->S++; } if (WR(pkp->rdq)->count) qenable(WR(pkp->rdq)); if (rxmit) { for (packno = pkp->S; packno < pkp->SN; packno++) pkxmit(WR(pkp->rdq), pkp->xpacks[packno&07], packno); } if (pkp->S >= 8) { pkp->S -= 8; pkp->SN -= 8; } splx(s); } /* * If n bytes are available, return 1, else 0. */ pkwant(pkp, n) register n; register struct pack *pkp; { register int navail = 0; register struct block *bp; int s = spl5(); for (bp = pkp->rdq->first; bp; bp = bp->next) { navail += bp->wptr - bp->rptr; if (navail >= n) { splx(s); return(1); } } if (pkp->ostate==RCLOSE) putctl(pkp->rdq->next, M_HANGUP); pkp->iwant = n - navail; splx(s); return(0); } /* * Copy n bytes from the front of the queue. */ pkgetb(q, cp, n) register struct queue *q; register u_char *cp; register n; { register struct block *bp; while (n) { if ((bp = q->first) == NULL) { printf("pkgetb fail\n"); return; } if (bp->rptr >= bp->wptr) { freeb(getq(q)); continue; } *cp++ = *bp->rptr++; --n; } if (bp->rptr >= bp->wptr) freeb(getq(q)); } /* * The very strange checksum */ pkcksum(cp, nbytes) register u_char *cp; register nbytes; { register sum, t, x; if (nbytes > pkc_size) nbytes = pkc_size; sum = pkc_sum; x = pkc_x; while (--nbytes >= 0) { sum <<= 1; if (sum & 0x10000) sum++; t = sum & 0xFFFF; sum += *cp++; sum &= 0xFFFF; x += sum ^ pkc_size; pkc_size--; if (sum <= t) sum ^= x; } pkc_x = x; pkc_sum = sum; } /* * Send a control packet */ pkscntl(pkp, cmd) struct pack *pkp; { register struct block *bp; register u_char *cp; register struct queue *q; if ((bp = allocb(6)) == NULL) return; /* what else to do? */ bp->wptr += 6; cp = bp->rptr; *cp++ = SYN; *cp++ = 9; /* ctl pack marker */ *cp++ = cmd; *cp++ = 0; *cp++ = cmd; *cp++ = 9; /* xor of hdr[1..4] */ q = WR(pkp->rdq); (*q->next->qinfo->putp)(q->next, bp); } /* * Receive a control packet */ pkcntl(pkp) register struct pack *pkp; { register val; register reject = 0; if (pkp->hdr.k != 9) { return; } val = pkp->hdr.C & 07; switch (pkp->hdr.C & 0370) { case PK_INITA: pkp->timer = 2; pkp->XW = val; if (pkp->ostate==SINITA || pkp->ostate==SINITB) { pkscntl(pkp, PK_INITA+RWINSIZE); pkscntl(pkp, PK_INITB+RSEGSIZE); pkp->ostate = SINITB; return; } return; case PK_INITB: pkp->timer = 2; pkp->xsegment = pksizes[val+1]; pkp->xlogseg = val + 1; if (pkp->ostate==SINITB) { pkp->ostate = SINITC; pkscntl(pkp, PK_INITB+RSEGSIZE); pkscntl(pkp, PK_INITC+RWINSIZE); return; } return; case PK_INITC: if (pkp->ostate>=SINITB) { if (pkp->ostate==SINITB) pkscntl(pkp, PK_INITC+RWINSIZE); pkp->ostate = OPEN; wakeup((caddr_t)pkp); return; } return; case PK_RJ: reject = 1; case PK_RR: pkrack(pkp, val, reject); return; case PK_CLOSE: pkp->ostate = RCLOSE; flushq(WR(pkp->rdq), 0); pkrack(pkp, pkp->SN-1, 0); qenable(pkp->rdq); return; default: return; } } pktimer() { register struct pack *pkp; for (pkp = packs; pkp < &packs[NPK]; pkp++) { if (pkp->timer) { pkp->timer--; continue; } switch (pkp->ostate) { case 0: continue; case OPEN: case LCLOSE: qenable(WR(pkp->rdq)); continue; case SINITA: pkscntl(pkp, PK_INITA+RWINSIZE); pkp->timer = 2; continue; case SINITB: pkscntl(pkp, PK_INITA+RWINSIZE); pkscntl(pkp, PK_INITB+RSEGSIZE); pkp->timer = 2; continue; } } timeout(pktimer, (caddr_t)NULL, 60); }