/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <ip.h>#include <auth.h>#include "ppp.h"#include "thwack.h" typedef struct Cstate Cstate;struct Cstate{ ulong seq; Thwack th; ulong stats[ThwStats];}; typedef struct Uncstate Uncstate;struct Uncstate{ QLock ackl; /* lock for acks sent back to compressor */ int doack; /* send an ack? */ int badpacks; /* bad packets seen in a row */ ulong ackseq; /* packets to ack */ int ackmask; int active; /* 0 => waiting for resetack */ int resetid; /* id of most recent reset */ Unthwack ut;}; enum{ ThwAcked = 1UL << 23, ThwCompMask = 3UL << 21, ThwCompressed = 0UL << 21, ThwUncomp = 1UL << 21, ThwUncompAdd = 2UL << 21, /* uncompressed, but add to decompression buffer */ ThwSeqMask = 0x0fffff, ThwSmallPack = 96,}; static void *compinit(PPP*);static Block* comp(PPP*, ushort, Block*, int*);static Block *compresetreq(void*, Block*);static void compcompack(void*, Block*);static void compfini(void*); static void *uncinit(PPP*);static Block* uncomp(PPP*, Block*, int *protop, Block**);static void uncfini(void*);static void uncresetack(void*, Block*); Comptype cthwack = { compinit, comp, compresetreq, compfini}; Uncomptype uncthwack = { uncinit, uncomp, uncresetack, uncfini}; static void *compinit(PPP *){ Cstate *cs; cs = mallocz(sizeof(Cstate), 1); thwackinit(&cs->th); return cs;} static voidcompfini(void *as){ Cstate *cs; cs = as; thwackcleanup(&cs->th); free(cs);} static Block *compresetreq(void *as, Block *b){ Cstate *cs; Lcpmsg *m; int id; cs = as; m = (Lcpmsg*)b->rptr; id = m->id; thwackinit(&cs->th); freeb(b); netlog("thwack resetreq id=%d \n", id); b = alloclcp(Lresetack, id, 4, &m); hnputs(m->len, 4); return b;} static Block*comp(PPP *ppp, ushort proto, Block *b, int *protop){ Uncstate *uncs; Cstate *cs; Block *bb; ulong seq, acked; int n, nn, mustadd; cs = ppp->cstate; *protop = 0; /* put ack and protocol into b */ n = BLEN(b); if(b->rptr - (2+4) < b->base) sysfatal("thwack: not enough header in block"); acked = 0; if(ppp->unctype == &uncthwack){ uncs = ppp->uncstate; qlock(&uncs->ackl); if(uncs->doack){ uncs->doack = 0; b->rptr -= 4; b->rptr[0] = uncs->ackseq >> 16; b->rptr[1] = uncs->ackseq >> 8; b->rptr[2] = uncs->ackseq; b->rptr[3] = uncs->ackmask; acked = ThwAcked; } qunlock(&uncs->ackl); } if(proto > 0xff){ b->rptr -= 2; b->rptr[0] = proto >> 8; b->rptr[1] = proto; }else{ b->rptr--; b->rptr[0] = proto; } bb = allocb(BLEN(b) + 3); seq = cs->seq; if(n <= 3){ mustadd = 0; nn = -1; }else{ mustadd = n < ThwSmallPack; nn = thwack(&cs->th, mustadd, bb->wptr + 3, n - 3, b, seq, cs->stats); } if(nn < 0 && !mustadd){ if(!acked || BLEN(b) + 1 > ppp->mtu){ freeb(bb); if(acked) b->rptr += 4; if(proto > 0xff) b->rptr += 2; else b->rptr++; *protop = proto; return b; } bb->wptr[0] = (ThwUncomp | ThwAcked) >> 16; memmove(bb->wptr + 1, b->rptr, BLEN(b)); bb->wptr += BLEN(b) + 1; freeb(b); }else{ cs->seq = (seq + 1) & ThwSeqMask; if(nn < 0){ nn = BLEN(b); memmove(bb->wptr + 3, b->rptr, nn); seq |= ThwUncompAdd; }else seq |= ThwCompressed; seq |= acked; bb->wptr[0] = seq>>16; bb->wptr[1] = seq>>8; bb->wptr[2] = seq; bb->wptr += nn + 3; } *protop = Pcdata; return bb;} static void *uncinit(PPP *){ Uncstate *s; s = mallocz(sizeof(Uncstate), 1); s->active = 1; unthwackinit(&s->ut); return s;} static voiduncfini(void *as){ free(as);} static voiduncresetack(void *as, Block *b){ Uncstate *s; Lcpmsg *m; s = as; m = (Lcpmsg*)b->rptr; /* * rfc 1962 says we must reset every message * we don't since we may have acked some messages * which the compressor will use in the future. */ netlog("unthwack resetack id=%d resetid=%d active=%d\n", m->id, s->resetid, s->active); if(m->id == (uchar)s->resetid && !s->active){ s->active = 1; unthwackinit(&s->ut); }} static Block*uncomp(PPP *ppp, Block *bb, int *protop, Block **reply){ Lcpmsg *m; Cstate *cs; Uncstate *uncs; Block *b, *r; ulong seq, mseq; ushort proto; uchar mask; int n; *reply = nil; *protop = 0; uncs = ppp->uncstate; if(BLEN(bb) < 4){ syslog(0, "ppp", ": thwack: short packet\n"); freeb(bb); return nil; } if(!uncs->active){ netlog("unthwack: inactive, killing packet\n"); freeb(bb); r = alloclcp(Lresetreq, uncs->resetid, 4, &m); hnputs(m->len, 4); *reply = r; return nil; } seq = bb->rptr[0] << 16; if((seq & ThwCompMask) == ThwUncomp){ bb->rptr++; b = bb; }else{ seq |= (bb->rptr[1]<<8) | bb->rptr[2]; bb->rptr += 3; if((seq & ThwCompMask) == ThwCompressed){ b = allocb(ThwMaxBlock); n = unthwack(&uncs->ut, b->wptr, ThwMaxBlock, bb->rptr, BLEN(bb), seq & ThwSeqMask); freeb(bb); if(n < 2){ syslog(0, "ppp", ": unthwack: short or corrupted packet %d seq=%ld\n", n, seq); netlog("unthwack: short or corrupted packet n=%d seq=%ld: %s\n", n, seq, uncs->ut.err); freeb(b); r = alloclcp(Lresetreq, ++uncs->resetid, 4, &m); hnputs(m->len, 4); *reply = r; uncs->active = 0; return nil; } b->wptr += n; }else{ unthwackadd(&uncs->ut, bb->rptr, BLEN(bb), seq & ThwSeqMask); b = bb; } /* * update ack state */ mseq = unthwackstate(&uncs->ut, &mask); qlock(&uncs->ackl); uncs->ackseq = mseq; uncs->ackmask = mask; uncs->doack = 1; qunlock(&uncs->ackl); } /* * grab the compressed protocol field */ proto = *b->rptr++; if((proto & 1) == 0) proto = (proto << 8) | *b->rptr++; *protop = proto; /* * decode the ack, and forward to compressor */ if(seq & ThwAcked){ if(ppp->ctype == &cthwack){ cs = ppp->cstate; mseq = (b->rptr[0]<<16) | (b->rptr[1]<<8) | b->rptr[2]; mask = b->rptr[3]; thwackack(&cs->th, mseq, mask); } b->rptr += 4; } return b;}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <ip.h>
#include <auth.h>
#include "ppp.h"
#include "thwack.h"
typedef struct Cstate Cstate;
struct Cstate
{
ulong seq;
Thwack th;
ulong stats[ThwStats];
};
typedef struct Uncstate Uncstate;
struct Uncstate
{
QLock ackl; /* lock for acks sent back to compressor */
int doack; /* send an ack? */
int badpacks; /* bad packets seen in a row */
ulong ackseq; /* packets to ack */
int ackmask;
int active; /* 0 => waiting for resetack */
int resetid; /* id of most recent reset */
Unthwack ut;
};
enum
{
ThwAcked = 1UL << 23,
ThwCompMask = 3UL << 21,
ThwCompressed = 0UL << 21,
ThwUncomp = 1UL << 21,
ThwUncompAdd = 2UL << 21, /* uncompressed, but add to decompression buffer */
ThwSeqMask = 0x0fffff,
ThwSmallPack = 96,
};
static void *compinit(PPP*);
static Block* comp(PPP*, ushort, Block*, int*);
static Block *compresetreq(void*, Block*);
static void compcompack(void*, Block*);
static void compfini(void*);
static void *uncinit(PPP*);
static Block* uncomp(PPP*, Block*, int *protop, Block**);
static void uncfini(void*);
static void uncresetack(void*, Block*);
Comptype cthwack = {
compinit,
comp,
compresetreq,
compfini
};
Uncomptype uncthwack = {
uncinit,
uncomp,
uncresetack,
uncfini
};
static void *
compinit(PPP *)
{
Cstate *cs;
cs = mallocz(sizeof(Cstate), 1);
thwackinit(&cs->th);
return cs;
}
static void
compfini(void *as)
{
Cstate *cs;
cs = as;
thwackcleanup(&cs->th);
free(cs);
}
static Block *
compresetreq(void *as, Block *b)
{
Cstate *cs;
Lcpmsg *m;
int id;
cs = as;
m = (Lcpmsg*)b->rptr;
id = m->id;
thwackinit(&cs->th);
freeb(b);
netlog("thwack resetreq id=%d \n", id);
b = alloclcp(Lresetack, id, 4, &m);
hnputs(m->len, 4);
return b;
}
static Block*
comp(PPP *ppp, ushort proto, Block *b, int *protop)
{
Uncstate *uncs;
Cstate *cs;
Block *bb;
ulong seq, acked;
int n, nn, mustadd;
cs = ppp->cstate;
*protop = 0;
/* put ack and protocol into b */
n = BLEN(b);
if(b->rptr - (2+4) < b->base)
sysfatal("thwack: not enough header in block");
acked = 0;
if(ppp->unctype == &uncthwack){
uncs = ppp->uncstate;
qlock(&uncs->ackl);
if(uncs->doack){
uncs->doack = 0;
b->rptr -= 4;
b->rptr[0] = uncs->ackseq >> 16;
b->rptr[1] = uncs->ackseq >> 8;
b->rptr[2] = uncs->ackseq;
b->rptr[3] = uncs->ackmask;
acked = ThwAcked;
}
qunlock(&uncs->ackl);
}
if(proto > 0xff){
b->rptr -= 2;
b->rptr[0] = proto >> 8;
b->rptr[1] = proto;
}else{
b->rptr--;
b->rptr[0] = proto;
}
bb = allocb(BLEN(b) + 3);
seq = cs->seq;
if(n <= 3){
mustadd = 0;
nn = -1;
}else{
mustadd = n < ThwSmallPack;
nn = thwack(&cs->th, mustadd, bb->wptr + 3, n - 3, b, seq, cs->stats);
}
if(nn < 0 && !mustadd){
if(!acked || BLEN(b) + 1 > ppp->mtu){
freeb(bb);
if(acked)
b->rptr += 4;
if(proto > 0xff)
b->rptr += 2;
else
b->rptr++;
*protop = proto;
return b;
}
bb->wptr[0] = (ThwUncomp | ThwAcked) >> 16;
memmove(bb->wptr + 1, b->rptr, BLEN(b));
bb->wptr += BLEN(b) + 1;
freeb(b);
}else{
cs->seq = (seq + 1) & ThwSeqMask;
if(nn < 0){
nn = BLEN(b);
memmove(bb->wptr + 3, b->rptr, nn);
seq |= ThwUncompAdd;
}else
seq |= ThwCompressed;
seq |= acked;
bb->wptr[0] = seq>>16;
bb->wptr[1] = seq>>8;
bb->wptr[2] = seq;
bb->wptr += nn + 3;
}
*protop = Pcdata;
return bb;
}
static void *
uncinit(PPP *)
{
Uncstate *s;
s = mallocz(sizeof(Uncstate), 1);
s->active = 1;
unthwackinit(&s->ut);
return s;
}
static void
uncfini(void *as)
{
free(as);
}
static void
uncresetack(void *as, Block *b)
{
Uncstate *s;
Lcpmsg *m;
s = as;
m = (Lcpmsg*)b->rptr;
/*
* rfc 1962 says we must reset every message
* we don't since we may have acked some messages
* which the compressor will use in the future.
*/
netlog("unthwack resetack id=%d resetid=%d active=%d\n", m->id, s->resetid, s->active);
if(m->id == (uchar)s->resetid && !s->active){
s->active = 1;
unthwackinit(&s->ut);
}
}
static Block*
uncomp(PPP *ppp, Block *bb, int *protop, Block **reply)
{
Lcpmsg *m;
Cstate *cs;
Uncstate *uncs;
Block *b, *r;
ulong seq, mseq;
ushort proto;
uchar mask;
int n;
*reply = nil;
*protop = 0;
uncs = ppp->uncstate;
if(BLEN(bb) < 4){
syslog(0, "ppp", ": thwack: short packet\n");
freeb(bb);
return nil;
}
if(!uncs->active){
netlog("unthwack: inactive, killing packet\n");
freeb(bb);
r = alloclcp(Lresetreq, uncs->resetid, 4, &m);
hnputs(m->len, 4);
*reply = r;
return nil;
}
seq = bb->rptr[0] << 16;
if((seq & ThwCompMask) == ThwUncomp){
bb->rptr++;
b = bb;
}else{
seq |= (bb->rptr[1]<<8) | bb->rptr[2];
bb->rptr += 3;
if((seq & ThwCompMask) == ThwCompressed){
b = allocb(ThwMaxBlock);
n = unthwack(&uncs->ut, b->wptr, ThwMaxBlock, bb->rptr, BLEN(bb), seq & ThwSeqMask);
freeb(bb);
if(n < 2){
syslog(0, "ppp", ": unthwack: short or corrupted packet %d seq=%ld\n", n, seq);
netlog("unthwack: short or corrupted packet n=%d seq=%ld: %s\n", n, seq, uncs->ut.err);
freeb(b);
r = alloclcp(Lresetreq, ++uncs->resetid, 4, &m);
hnputs(m->len, 4);
*reply = r;
uncs->active = 0;
return nil;
}
b->wptr += n;
}else{
unthwackadd(&uncs->ut, bb->rptr, BLEN(bb), seq & ThwSeqMask);
b = bb;
}
/*
* update ack state
*/
mseq = unthwackstate(&uncs->ut, &mask);
qlock(&uncs->ackl);
uncs->ackseq = mseq;
uncs->ackmask = mask;
uncs->doack = 1;
qunlock(&uncs->ackl);
}
/*
* grab the compressed protocol field
*/
proto = *b->rptr++;
if((proto & 1) == 0)
proto = (proto << 8) | *b->rptr++;
*protop = proto;
/*
* decode the ack, and forward to compressor
*/
if(seq & ThwAcked){
if(ppp->ctype == &cthwack){
cs = ppp->cstate;
mseq = (b->rptr[0]<<16) | (b->rptr[1]<<8) | b->rptr[2];
mask = b->rptr[3];
thwackack(&cs->th, mseq, mask);
}
b->rptr += 4;
}
return b;
}