123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- #include <u.h>
- #include <libc.h>
- #include <ip.h>
- #include <auth.h>
- #include "ppp.h"
- typedef struct Iphdr Iphdr;
- struct Iphdr
- {
- uchar vihl; /* Version and header length */
- uchar tos; /* Type of service */
- uchar length[2]; /* packet length */
- uchar id[2]; /* Identification */
- uchar frag[2]; /* Fragment information */
- uchar ttl; /* Time to live */
- uchar proto; /* Protocol */
- uchar cksum[2]; /* Header checksum */
- ulong src; /* Ip source (uchar ordering unimportant) */
- ulong dst; /* Ip destination (uchar ordering unimportant) */
- };
- typedef struct Tcphdr Tcphdr;
- struct Tcphdr
- {
- ulong ports; /* defined as a ulong to make comparisons easier */
- uchar seq[4];
- uchar ack[4];
- uchar flag[2];
- uchar win[2];
- uchar cksum[2];
- uchar urg[2];
- };
- typedef struct Ilhdr Ilhdr;
- struct Ilhdr
- {
- uchar sum[2]; /* Checksum including header */
- uchar len[2]; /* Packet length */
- uchar type; /* Packet type */
- uchar spec; /* Special */
- uchar src[2]; /* Src port */
- uchar dst[2]; /* Dst port */
- uchar id[4]; /* Sequence id */
- uchar ack[4]; /* Acked sequence */
- };
- enum
- {
- URG = 0x20, /* Data marked urgent */
- ACK = 0x10, /* Aknowledge is valid */
- PSH = 0x08, /* Whole data pipe is pushed */
- RST = 0x04, /* Reset connection */
- SYN = 0x02, /* Pkt. is synchronise */
- FIN = 0x01, /* Start close down */
- IP_DF = 0x4000, /* Don't fragment */
- IP_TCPPROTO = 6,
- IP_ILPROTO = 40,
- IL_IPHDR = 20,
- };
- typedef struct Hdr Hdr;
- struct Hdr
- {
- uchar buf[128];
- Iphdr *ip;
- Tcphdr *tcp;
- int len;
- };
- typedef struct Tcpc Tcpc;
- struct Tcpc
- {
- uchar lastrecv;
- uchar lastxmit;
- uchar basexmit;
- uchar err;
- uchar compressid;
- Hdr t[MAX_STATES];
- Hdr r[MAX_STATES];
- };
- enum
- { /* flag bits for what changed in a packet */
- NEW_U=(1<<0), /* tcp only */
- NEW_W=(1<<1), /* tcp only */
- NEW_A=(1<<2), /* il tcp */
- NEW_S=(1<<3), /* tcp only */
- NEW_P=(1<<4), /* tcp only */
- NEW_I=(1<<5), /* il tcp */
- NEW_C=(1<<6), /* il tcp */
- NEW_T=(1<<7), /* il only */
- TCP_PUSH_BIT = 0x10,
- };
- /* reserved, special-case values of above for tcp */
- #define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
- #define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
- #define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
- int
- encode(void *p, ulong n)
- {
- uchar *cp;
- cp = p;
- if(n >= 256 || n == 0) {
- *cp++ = 0;
- cp[0] = n >> 8;
- cp[1] = n;
- return 3;
- }
- *cp = n;
- return 1;
- }
- #define DECODEL(f) { \
- if (*cp == 0) {\
- hnputl(f, nhgetl(f) + ((cp[1] << 8) | cp[2])); \
- cp += 3; \
- } else { \
- hnputl(f, nhgetl(f) + (ulong)*cp++); \
- } \
- }
- #define DECODES(f) { \
- if (*cp == 0) {\
- hnputs(f, nhgets(f) + ((cp[1] << 8) | cp[2])); \
- cp += 3; \
- } else { \
- hnputs(f, nhgets(f) + (ulong)*cp++); \
- } \
- }
- Block*
- tcpcompress(Tcpc *comp, Block *b, int *protop)
- {
- Iphdr *ip; /* current packet */
- Tcphdr *tcp; /* current pkt */
- ulong iplen, tcplen, hlen; /* header length in uchars */
- ulong deltaS, deltaA; /* general purpose temporaries */
- ulong changes; /* change mask */
- uchar new_seq[16]; /* changes from last to current */
- uchar *cp;
- Hdr *h; /* last packet */
- int i, j;
- /*
- * Bail if this is not a compressible TCP/IP packet
- */
- ip = (Iphdr*)b->rptr;
- iplen = (ip->vihl & 0xf) << 2;
- tcp = (Tcphdr*)(b->rptr + iplen);
- tcplen = (tcp->flag[0] & 0xf0) >> 2;
- hlen = iplen + tcplen;
- if((tcp->flag[1] & (SYN|FIN|RST|ACK)) != ACK){
- *protop = Pip;
- return b; /* connection control */
- }
- /*
- * Packet is compressible, look for a connection
- */
- changes = 0;
- cp = new_seq;
- j = comp->lastxmit;
- h = &comp->t[j];
- if(ip->src != h->ip->src || ip->dst != h->ip->dst
- || tcp->ports != h->tcp->ports) {
- for(i = 0; i < MAX_STATES; ++i) {
- j = (comp->basexmit + i) % MAX_STATES;
- h = &comp->t[j];
- if(ip->src == h->ip->src && ip->dst == h->ip->dst
- && tcp->ports == h->tcp->ports)
- goto found;
- }
- /* no connection, reuse the oldest */
- if(i == MAX_STATES) {
- j = comp->basexmit;
- j = (j + MAX_STATES - 1) % MAX_STATES;
- comp->basexmit = j;
- h = &comp->t[j];
- goto rescue;
- }
- }
- found:
- /*
- * Make sure that only what we expect to change changed.
- */
- if(ip->vihl != h->ip->vihl || ip->tos != h->ip->tos ||
- ip->ttl != h->ip->ttl || ip->proto != h->ip->proto)
- goto rescue; /* headers changed */
- if(iplen != sizeof(Iphdr) && memcmp(ip+1, h->ip+1, iplen - sizeof(Iphdr)))
- goto rescue; /* ip options changed */
- if(tcplen != sizeof(Tcphdr) && memcmp(tcp+1, h->tcp+1, tcplen - sizeof(Tcphdr)))
- goto rescue; /* tcp options changed */
- if(tcp->flag[1] & URG) {
- cp += encode(cp, nhgets(tcp->urg));
- changes |= NEW_U;
- } else if(memcmp(tcp->urg, h->tcp->urg, sizeof(tcp->urg)) != 0)
- goto rescue;
- if(deltaS = nhgets(tcp->win) - nhgets(h->tcp->win)) {
- cp += encode(cp, deltaS);
- changes |= NEW_W;
- }
- if(deltaA = nhgetl(tcp->ack) - nhgetl(h->tcp->ack)) {
- if(deltaA > 0xffff)
- goto rescue;
- cp += encode(cp, deltaA);
- changes |= NEW_A;
- }
- if(deltaS = nhgetl(tcp->seq) - nhgetl(h->tcp->seq)) {
- if (deltaS > 0xffff)
- goto rescue;
- cp += encode(cp, deltaS);
- changes |= NEW_S;
- }
- /*
- * Look for the special-case encodings.
- */
- switch(changes) {
- case 0:
- /*
- * Nothing changed. If this packet contains data and the last
- * one didn't, this is probably a data packet following an
- * ack (normal on an interactive connection) and we send it
- * compressed. Otherwise it's probably a retransmit,
- * retransmitted ack or window probe. Send it uncompressed
- * in case the other side missed the compressed version.
- */
- if(nhgets(ip->length) == nhgets(h->ip->length) ||
- nhgets(h->ip->length) != hlen)
- goto rescue;
- break;
- case SPECIAL_I:
- case SPECIAL_D:
- /*
- * Actual changes match one of our special case encodings --
- * send packet uncompressed.
- */
- goto rescue;
- case NEW_S | NEW_A:
- if (deltaS == deltaA &&
- deltaS == nhgets(h->ip->length) - hlen) {
- /* special case for echoed terminal traffic */
- changes = SPECIAL_I;
- cp = new_seq;
- }
- break;
- case NEW_S:
- if (deltaS == nhgets(h->ip->length) - hlen) {
- /* special case for data xfer */
- changes = SPECIAL_D;
- cp = new_seq;
- }
- break;
- }
- deltaS = nhgets(ip->id) - nhgets(h->ip->id);
- if(deltaS != 1) {
- cp += encode(cp, deltaS);
- changes |= NEW_I;
- }
- if (tcp->flag[1] & PSH)
- changes |= TCP_PUSH_BIT;
- /*
- * Grab the cksum before we overwrite it below. Then update our
- * state with this packet's header.
- */
- deltaA = nhgets(tcp->cksum);
- memmove(h->buf, b->rptr, hlen);
- h->len = hlen;
- h->tcp = (Tcphdr*)(h->buf + iplen);
- /*
- * We want to use the original packet as our compressed packet. (cp -
- * new_seq) is the number of uchars we need for compressed sequence
- * numbers. In addition we need one uchar for the change mask, one
- * for the connection id and two for the tcp checksum. So, (cp -
- * new_seq) + 4 uchars of header are needed. hlen is how many uchars
- * of the original packet to toss so subtract the two to get the new
- * packet size. The temporaries are gross -egs.
- */
- deltaS = cp - new_seq;
- cp = b->rptr;
- if(comp->lastxmit != j || comp->compressid == 0) {
- comp->lastxmit = j;
- hlen -= deltaS + 4;
- cp += hlen;
- *cp++ = (changes | NEW_C);
- *cp++ = j;
- } else {
- hlen -= deltaS + 3;
- cp += hlen;
- *cp++ = changes;
- }
- b->rptr += hlen;
- hnputs(cp, deltaA);
- cp += 2;
- memmove(cp, new_seq, deltaS);
- *protop = Pvjctcp;
- return b;
- rescue:
- /*
- * Update connection state & send uncompressed packet
- */
- memmove(h->buf, b->rptr, hlen);
- h->tcp = (Tcphdr*)(h->buf + iplen);
- h->len = hlen;
- ip->proto = j;
- comp->lastxmit = j;
- *protop = Pvjutcp;
- return b;
- }
- Block*
- tcpuncompress(Tcpc *comp, Block *b, int type)
- {
- uchar *cp, changes;
- int i;
- int iplen, len;
- Iphdr *ip;
- Tcphdr *tcp;
- Hdr *h;
- if(type == Pvjutcp) {
- /*
- * Locate the saved state for this connection. If the state
- * index is legal, clear the 'discard' flag.
- */
- ip = (Iphdr*)b->rptr;
- if(ip->proto >= MAX_STATES)
- goto rescue;
- iplen = (ip->vihl & 0xf) << 2;
- tcp = (Tcphdr*)(b->rptr + iplen);
- comp->lastrecv = ip->proto;
- len = iplen + ((tcp->flag[0] & 0xf0) >> 2);
- comp->err = 0;
- /*
- * Restore the IP protocol field then save a copy of this
- * packet header. The checksum is zeroed in the copy so we
- * don't have to zero it each time we process a compressed
- * packet.
- */
- ip->proto = IP_TCPPROTO;
- h = &comp->r[comp->lastrecv];
- memmove(h->buf, b->rptr, len);
- h->tcp = (Tcphdr*)(h->buf + iplen);
- h->len = len;
- h->ip->cksum[0] = h->ip->cksum[1] = 0;
- return b;
- }
- cp = b->rptr;
- changes = *cp++;
- if(changes & NEW_C) {
- /*
- * Make sure the state index is in range, then grab the
- * state. If we have a good state index, clear the 'discard'
- * flag.
- */
- if(*cp >= MAX_STATES)
- goto rescue;
- comp->err = 0;
- comp->lastrecv = *cp++;
- } else {
- /*
- * This packet has no state index. If we've had a
- * line error since the last time we got an explicit state
- * index, we have to toss the packet.
- */
- if(comp->err != 0){
- freeb(b);
- return nil;
- }
- }
- /*
- * Find the state then fill in the TCP checksum and PUSH bit.
- */
- h = &comp->r[comp->lastrecv];
- ip = h->ip;
- tcp = h->tcp;
- len = h->len;
- memmove(tcp->cksum, cp, sizeof tcp->cksum);
- cp += 2;
- if(changes & TCP_PUSH_BIT)
- tcp->flag[1] |= PSH;
- else
- tcp->flag[1] &= ~PSH;
- /*
- * Fix up the state's ack, seq, urg and win fields based on the
- * changemask.
- */
- switch (changes & SPECIALS_MASK) {
- case SPECIAL_I:
- i = nhgets(ip->length) - len;
- hnputl(tcp->ack, nhgetl(tcp->ack) + i);
- hnputl(tcp->seq, nhgetl(tcp->seq) + i);
- break;
- case SPECIAL_D:
- hnputl(tcp->seq, nhgetl(tcp->seq) + nhgets(ip->length) - len);
- break;
- default:
- if(changes & NEW_U) {
- tcp->flag[1] |= URG;
- if(*cp == 0){
- hnputs(tcp->urg, nhgets(cp+1));
- cp += 3;
- }else
- hnputs(tcp->urg, *cp++);
- } else
- tcp->flag[1] &= ~URG;
- if(changes & NEW_W)
- DECODES(tcp->win)
- if(changes & NEW_A)
- DECODEL(tcp->ack)
- if(changes & NEW_S)
- DECODEL(tcp->seq)
- break;
- }
- /* Update the IP ID */
- if(changes & NEW_I)
- DECODES(ip->id)
- else
- hnputs(ip->id, nhgets(ip->id) + 1);
- /*
- * At this point, cp points to the first uchar of data in the packet.
- * Back up cp by the TCP/IP header length to make room for the
- * reconstructed header.
- * We assume the packet we were handed has enough space to prepend
- * up to 128 uchars of header.
- */
- b->rptr = cp;
- if(b->rptr - b->base < len){
- b = padb(b, len);
- b = pullup(b, blen(b));
- } else
- b->rptr -= len;
- hnputs(ip->length, BLEN(b));
- memmove(b->rptr, ip, len);
-
- /* recompute the ip header checksum */
- ip = (Iphdr*)b->rptr;
- ip->cksum[0] = ip->cksum[1] = 0;
- hnputs(ip->cksum, ipcsum(b->rptr));
- if(*b->rptr != 0x45) syslog(0, LOG, "bad tcpuncompress %2.2ux", *b->rptr);
- return b;
- rescue:
- netlog("ppp: vj: Bad Packet!\n");
- comp->err = 1;
- freeb(b);
- return nil;
- }
- Tcpc*
- compress_init(Tcpc *c)
- {
- int i;
- Hdr *h;
- if(c == nil)
- c = malloc(sizeof(Tcpc));
- memset(c, 0, sizeof(*c));
- for(i = 0; i < MAX_STATES; i++){
- h = &c->t[i];
- h->ip = (Iphdr*)h->buf;
- h->tcp = (Tcphdr*)(h->buf + 20);
- h->len = 40;
- h = &c->r[i];
- h->ip = (Iphdr*)h->buf;
- h->tcp = (Tcphdr*)(h->buf + 20);
- h->len = 40;
- }
- return c;
- }
- Block*
- compress(Tcpc *tcp, Block *b, int *protop)
- {
- Iphdr *ip;
- /*
- * Bail if this is not a compressible IP packet
- */
- ip = (Iphdr*)b->rptr;
- if((nhgets(ip->frag) & 0x3fff) != 0){
- *protop = Pip;
- return b;
- }
- switch(ip->proto) {
- case IP_TCPPROTO:
- return tcpcompress(tcp, b, protop);
- default:
- *protop = Pip;
- return b;
- }
- }
- int
- compress_negotiate(Tcpc *tcp, uchar *data)
- {
- if(data[0] != MAX_STATES - 1)
- return -1;
- tcp->compressid = data[1];
- return 0;
- }
|