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)
/*
* 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;
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;
/* 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));