Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#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;
}