xref: /plan9/sys/src/cmd/ip/ppp/compress.c (revision fe853e2326f51910bb38886e9bfc22ecdef993d7)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <ip.h>
49a747e4fSDavid du Colombier #include <auth.h>
57dd7cddfSDavid du Colombier #include "ppp.h"
67dd7cddfSDavid du Colombier 
77dd7cddfSDavid du Colombier typedef struct Iphdr Iphdr;
87dd7cddfSDavid du Colombier struct Iphdr
97dd7cddfSDavid du Colombier {
107dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
117dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
127dd7cddfSDavid du Colombier 	uchar	length[2];	/* packet length */
137dd7cddfSDavid du Colombier 	uchar	id[2];		/* Identification */
147dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
157dd7cddfSDavid du Colombier 	uchar	ttl;		/* Time to live */
167dd7cddfSDavid du Colombier 	uchar	proto;		/* Protocol */
177dd7cddfSDavid du Colombier 	uchar	cksum[2];	/* Header checksum */
187dd7cddfSDavid du Colombier 	ulong	src;		/* Ip source (uchar ordering unimportant) */
197dd7cddfSDavid du Colombier 	ulong	dst;		/* Ip destination (uchar ordering unimportant) */
207dd7cddfSDavid du Colombier };
217dd7cddfSDavid du Colombier 
227dd7cddfSDavid du Colombier typedef struct Tcphdr Tcphdr;
237dd7cddfSDavid du Colombier struct Tcphdr
247dd7cddfSDavid du Colombier {
257dd7cddfSDavid du Colombier 	ulong	ports;		/* defined as a ulong to make comparisons easier */
267dd7cddfSDavid du Colombier 	uchar	seq[4];
277dd7cddfSDavid du Colombier 	uchar	ack[4];
287dd7cddfSDavid du Colombier 	uchar	flag[2];
297dd7cddfSDavid du Colombier 	uchar	win[2];
307dd7cddfSDavid du Colombier 	uchar	cksum[2];
317dd7cddfSDavid du Colombier 	uchar	urg[2];
327dd7cddfSDavid du Colombier };
337dd7cddfSDavid du Colombier 
347dd7cddfSDavid du Colombier typedef struct Ilhdr Ilhdr;
357dd7cddfSDavid du Colombier struct Ilhdr
367dd7cddfSDavid du Colombier {
377dd7cddfSDavid du Colombier 	uchar	sum[2];	/* Checksum including header */
387dd7cddfSDavid du Colombier 	uchar	len[2];	/* Packet length */
397dd7cddfSDavid du Colombier 	uchar	type;		/* Packet type */
407dd7cddfSDavid du Colombier 	uchar	spec;		/* Special */
417dd7cddfSDavid du Colombier 	uchar	src[2];	/* Src port */
427dd7cddfSDavid du Colombier 	uchar	dst[2];	/* Dst port */
437dd7cddfSDavid du Colombier 	uchar	id[4];	/* Sequence id */
447dd7cddfSDavid du Colombier 	uchar	ack[4];	/* Acked sequence */
457dd7cddfSDavid du Colombier };
467dd7cddfSDavid du Colombier 
477dd7cddfSDavid du Colombier enum
487dd7cddfSDavid du Colombier {
497dd7cddfSDavid du Colombier 	URG		= 0x20,		/* Data marked urgent */
507dd7cddfSDavid du Colombier 	ACK		= 0x10,		/* Aknowledge is valid */
517dd7cddfSDavid du Colombier 	PSH		= 0x08,		/* Whole data pipe is pushed */
527dd7cddfSDavid du Colombier 	RST		= 0x04,		/* Reset connection */
537dd7cddfSDavid du Colombier 	SYN		= 0x02,		/* Pkt. is synchronise */
547dd7cddfSDavid du Colombier 	FIN		= 0x01,		/* Start close down */
557dd7cddfSDavid du Colombier 
567dd7cddfSDavid du Colombier 	IP_DF		= 0x4000,	/* Don't fragment */
577dd7cddfSDavid du Colombier 
587dd7cddfSDavid du Colombier 	IP_TCPPROTO	= 6,
597dd7cddfSDavid du Colombier 	IP_ILPROTO	= 40,
607dd7cddfSDavid du Colombier 	IL_IPHDR	= 20,
617dd7cddfSDavid du Colombier };
627dd7cddfSDavid du Colombier 
637dd7cddfSDavid du Colombier typedef struct Hdr Hdr;
647dd7cddfSDavid du Colombier struct Hdr
657dd7cddfSDavid du Colombier {
667dd7cddfSDavid du Colombier 	uchar	buf[128];
677dd7cddfSDavid du Colombier 	Iphdr	*ip;
687dd7cddfSDavid du Colombier 	Tcphdr	*tcp;
697dd7cddfSDavid du Colombier 	int	len;
707dd7cddfSDavid du Colombier };
717dd7cddfSDavid du Colombier 
727dd7cddfSDavid du Colombier typedef struct Tcpc Tcpc;
737dd7cddfSDavid du Colombier struct Tcpc
747dd7cddfSDavid du Colombier {
757dd7cddfSDavid du Colombier 	uchar	lastrecv;
767dd7cddfSDavid du Colombier 	uchar	lastxmit;
777dd7cddfSDavid du Colombier 	uchar	basexmit;
787dd7cddfSDavid du Colombier 	uchar	err;
797dd7cddfSDavid du Colombier 	uchar	compressid;
807dd7cddfSDavid du Colombier 	Hdr	t[MAX_STATES];
817dd7cddfSDavid du Colombier 	Hdr	r[MAX_STATES];
827dd7cddfSDavid du Colombier };
837dd7cddfSDavid du Colombier 
847dd7cddfSDavid du Colombier enum
857dd7cddfSDavid du Colombier {	/* flag bits for what changed in a packet */
867dd7cddfSDavid du Colombier 	NEW_U=(1<<0),	/* tcp only */
877dd7cddfSDavid du Colombier 	NEW_W=(1<<1),	/* tcp only */
887dd7cddfSDavid du Colombier 	NEW_A=(1<<2),	/* il tcp */
897dd7cddfSDavid du Colombier 	NEW_S=(1<<3),	/* tcp only */
907dd7cddfSDavid du Colombier 	NEW_P=(1<<4),	/* tcp only */
917dd7cddfSDavid du Colombier 	NEW_I=(1<<5),	/* il tcp */
927dd7cddfSDavid du Colombier 	NEW_C=(1<<6),	/* il tcp */
937dd7cddfSDavid du Colombier 	NEW_T=(1<<7),	/* il only */
947dd7cddfSDavid du Colombier 	TCP_PUSH_BIT	= 0x10,
957dd7cddfSDavid du Colombier };
967dd7cddfSDavid du Colombier 
977dd7cddfSDavid du Colombier /* reserved, special-case values of above for tcp */
987dd7cddfSDavid du Colombier #define SPECIAL_I (NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
997dd7cddfSDavid du Colombier #define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
1007dd7cddfSDavid du Colombier #define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
1017dd7cddfSDavid du Colombier 
1027dd7cddfSDavid du Colombier int
encode(void * p,ulong n)1037dd7cddfSDavid du Colombier encode(void *p, ulong n)
1047dd7cddfSDavid du Colombier {
1057dd7cddfSDavid du Colombier 	uchar	*cp;
1067dd7cddfSDavid du Colombier 
1077dd7cddfSDavid du Colombier 	cp = p;
1087dd7cddfSDavid du Colombier 	if(n >= 256 || n == 0) {
1097dd7cddfSDavid du Colombier 		*cp++ = 0;
1107dd7cddfSDavid du Colombier 		cp[0] = n >> 8;
1117dd7cddfSDavid du Colombier 		cp[1] = n;
1127dd7cddfSDavid du Colombier 		return 3;
1137dd7cddfSDavid du Colombier 	}
1147dd7cddfSDavid du Colombier 	*cp = n;
1157dd7cddfSDavid du Colombier 	return 1;
1167dd7cddfSDavid du Colombier }
1177dd7cddfSDavid du Colombier 
1187dd7cddfSDavid du Colombier #define DECODEL(f) { \
1197dd7cddfSDavid du Colombier 	if (*cp == 0) {\
1207dd7cddfSDavid du Colombier 		hnputl(f, nhgetl(f) + ((cp[1] << 8) | cp[2])); \
1217dd7cddfSDavid du Colombier 		cp += 3; \
1227dd7cddfSDavid du Colombier 	} else { \
1237dd7cddfSDavid du Colombier 		hnputl(f, nhgetl(f) + (ulong)*cp++); \
1247dd7cddfSDavid du Colombier 	} \
1257dd7cddfSDavid du Colombier }
1267dd7cddfSDavid du Colombier #define DECODES(f) { \
1277dd7cddfSDavid du Colombier 	if (*cp == 0) {\
1287dd7cddfSDavid du Colombier 		hnputs(f, nhgets(f) + ((cp[1] << 8) | cp[2])); \
1297dd7cddfSDavid du Colombier 		cp += 3; \
1307dd7cddfSDavid du Colombier 	} else { \
1317dd7cddfSDavid du Colombier 		hnputs(f, nhgets(f) + (ulong)*cp++); \
1327dd7cddfSDavid du Colombier 	} \
1337dd7cddfSDavid du Colombier }
1347dd7cddfSDavid du Colombier 
1357dd7cddfSDavid du Colombier Block*
tcpcompress(Tcpc * comp,Block * b,int * protop)1367dd7cddfSDavid du Colombier tcpcompress(Tcpc *comp, Block *b, int *protop)
1377dd7cddfSDavid du Colombier {
1387dd7cddfSDavid du Colombier 	Iphdr	*ip;		/* current packet */
1397dd7cddfSDavid du Colombier 	Tcphdr	*tcp;		/* current pkt */
1407dd7cddfSDavid du Colombier 	ulong 	iplen, tcplen, hlen;	/* header length in uchars */
1417dd7cddfSDavid du Colombier 	ulong 	deltaS, deltaA;	/* general purpose temporaries */
1427dd7cddfSDavid du Colombier 	ulong 	changes;	/* change mask */
1437dd7cddfSDavid du Colombier 	uchar 	new_seq[16];	/* changes from last to current */
1447dd7cddfSDavid du Colombier 	uchar 	*cp;
1457dd7cddfSDavid du Colombier 	Hdr	*h;		/* last packet */
1467dd7cddfSDavid du Colombier 	int 	i, j;
1477dd7cddfSDavid du Colombier 
1487dd7cddfSDavid du Colombier 	/*
1497dd7cddfSDavid du Colombier 	 * Bail if this is not a compressible TCP/IP packet
1507dd7cddfSDavid du Colombier 	 */
1517dd7cddfSDavid du Colombier 	ip = (Iphdr*)b->rptr;
1527dd7cddfSDavid du Colombier 	iplen = (ip->vihl & 0xf) << 2;
1537dd7cddfSDavid du Colombier 	tcp = (Tcphdr*)(b->rptr + iplen);
1547dd7cddfSDavid du Colombier 	tcplen = (tcp->flag[0] & 0xf0) >> 2;
1557dd7cddfSDavid du Colombier 	hlen = iplen + tcplen;
1567dd7cddfSDavid du Colombier 	if((tcp->flag[1] & (SYN|FIN|RST|ACK)) != ACK){
1577dd7cddfSDavid du Colombier 		*protop = Pip;
1587dd7cddfSDavid du Colombier 		return b;		/* connection control */
1597dd7cddfSDavid du Colombier 	}
1607dd7cddfSDavid du Colombier 
1617dd7cddfSDavid du Colombier 	/*
1627dd7cddfSDavid du Colombier 	 * Packet is compressible, look for a connection
1637dd7cddfSDavid du Colombier 	 */
1647dd7cddfSDavid du Colombier 	changes = 0;
1657dd7cddfSDavid du Colombier 	cp = new_seq;
1667dd7cddfSDavid du Colombier 	j = comp->lastxmit;
1677dd7cddfSDavid du Colombier 	h = &comp->t[j];
1687dd7cddfSDavid du Colombier 	if(ip->src != h->ip->src || ip->dst != h->ip->dst
1697dd7cddfSDavid du Colombier 	|| tcp->ports != h->tcp->ports) {
1707dd7cddfSDavid du Colombier 		for(i = 0; i < MAX_STATES; ++i) {
1717dd7cddfSDavid du Colombier 			j = (comp->basexmit + i) % MAX_STATES;
1727dd7cddfSDavid du Colombier 			h = &comp->t[j];
1737dd7cddfSDavid du Colombier 			if(ip->src == h->ip->src && ip->dst == h->ip->dst
1747dd7cddfSDavid du Colombier 			&& tcp->ports == h->tcp->ports)
1757dd7cddfSDavid du Colombier 				goto found;
1767dd7cddfSDavid du Colombier 		}
1777dd7cddfSDavid du Colombier 
1787dd7cddfSDavid du Colombier 		/* no connection, reuse the oldest */
1797dd7cddfSDavid du Colombier 		if(i == MAX_STATES) {
1807dd7cddfSDavid du Colombier 			j = comp->basexmit;
1817dd7cddfSDavid du Colombier 			j = (j + MAX_STATES - 1) % MAX_STATES;
1827dd7cddfSDavid du Colombier 			comp->basexmit = j;
1837dd7cddfSDavid du Colombier 			h = &comp->t[j];
1847dd7cddfSDavid du Colombier 			goto rescue;
1857dd7cddfSDavid du Colombier 		}
1867dd7cddfSDavid du Colombier 	}
1877dd7cddfSDavid du Colombier found:
1887dd7cddfSDavid du Colombier 
1897dd7cddfSDavid du Colombier 	/*
1907dd7cddfSDavid du Colombier 	 * Make sure that only what we expect to change changed.
1917dd7cddfSDavid du Colombier 	 */
1927dd7cddfSDavid du Colombier 	if(ip->vihl  != h->ip->vihl || ip->tos   != h->ip->tos ||
1937dd7cddfSDavid du Colombier 	   ip->ttl   != h->ip->ttl  || ip->proto != h->ip->proto)
1947dd7cddfSDavid du Colombier 		goto rescue;	/* headers changed */
1957dd7cddfSDavid du Colombier 	if(iplen != sizeof(Iphdr) && memcmp(ip+1, h->ip+1, iplen - sizeof(Iphdr)))
1967dd7cddfSDavid du Colombier 		goto rescue;	/* ip options changed */
1977dd7cddfSDavid du Colombier 	if(tcplen != sizeof(Tcphdr) && memcmp(tcp+1, h->tcp+1, tcplen - sizeof(Tcphdr)))
1987dd7cddfSDavid du Colombier 		goto rescue;	/* tcp options changed */
1997dd7cddfSDavid du Colombier 
2007dd7cddfSDavid du Colombier 	if(tcp->flag[1] & URG) {
2017dd7cddfSDavid du Colombier 		cp += encode(cp, nhgets(tcp->urg));
2027dd7cddfSDavid du Colombier 		changes |= NEW_U;
2037dd7cddfSDavid du Colombier 	} else if(memcmp(tcp->urg, h->tcp->urg, sizeof(tcp->urg)) != 0)
2047dd7cddfSDavid du Colombier 		goto rescue;
2057dd7cddfSDavid du Colombier 	if(deltaS = nhgets(tcp->win) - nhgets(h->tcp->win)) {
2067dd7cddfSDavid du Colombier 		cp += encode(cp, deltaS);
2077dd7cddfSDavid du Colombier 		changes |= NEW_W;
2087dd7cddfSDavid du Colombier 	}
2097dd7cddfSDavid du Colombier 	if(deltaA = nhgetl(tcp->ack) - nhgetl(h->tcp->ack)) {
2107dd7cddfSDavid du Colombier 		if(deltaA > 0xffff)
2117dd7cddfSDavid du Colombier 			goto rescue;
2127dd7cddfSDavid du Colombier 		cp += encode(cp, deltaA);
2137dd7cddfSDavid du Colombier 		changes |= NEW_A;
2147dd7cddfSDavid du Colombier 	}
2157dd7cddfSDavid du Colombier 	if(deltaS = nhgetl(tcp->seq) - nhgetl(h->tcp->seq)) {
2167dd7cddfSDavid du Colombier 		if (deltaS > 0xffff)
2177dd7cddfSDavid du Colombier 			goto rescue;
2187dd7cddfSDavid du Colombier 		cp += encode(cp, deltaS);
2197dd7cddfSDavid du Colombier 		changes |= NEW_S;
2207dd7cddfSDavid du Colombier 	}
2217dd7cddfSDavid du Colombier 
2227dd7cddfSDavid du Colombier 	/*
2237dd7cddfSDavid du Colombier 	 * Look for the special-case encodings.
2247dd7cddfSDavid du Colombier 	 */
2257dd7cddfSDavid du Colombier 	switch(changes) {
2267dd7cddfSDavid du Colombier 	case 0:
2277dd7cddfSDavid du Colombier 		/*
2287dd7cddfSDavid du Colombier 		 * Nothing changed. If this packet contains data and the last
2297dd7cddfSDavid du Colombier 		 * one didn't, this is probably a data packet following an
2307dd7cddfSDavid du Colombier 		 * ack (normal on an interactive connection) and we send it
2317dd7cddfSDavid du Colombier 		 * compressed. Otherwise it's probably a retransmit,
2327dd7cddfSDavid du Colombier 		 * retransmitted ack or window probe.  Send it uncompressed
2337dd7cddfSDavid du Colombier 		 * in case the other side missed the compressed version.
2347dd7cddfSDavid du Colombier 		 */
2357dd7cddfSDavid du Colombier 		if(nhgets(ip->length) == nhgets(h->ip->length) ||
2367dd7cddfSDavid du Colombier 		   nhgets(h->ip->length) != hlen)
2377dd7cddfSDavid du Colombier 			goto rescue;
2387dd7cddfSDavid du Colombier 		break;
2397dd7cddfSDavid du Colombier 	case SPECIAL_I:
2407dd7cddfSDavid du Colombier 	case SPECIAL_D:
2417dd7cddfSDavid du Colombier 		/*
2427dd7cddfSDavid du Colombier 		 * Actual changes match one of our special case encodings --
2437dd7cddfSDavid du Colombier 		 * send packet uncompressed.
2447dd7cddfSDavid du Colombier 		 */
2457dd7cddfSDavid du Colombier 		goto rescue;
2467dd7cddfSDavid du Colombier 	case NEW_S | NEW_A:
2477dd7cddfSDavid du Colombier 		if (deltaS == deltaA &&
2487dd7cddfSDavid du Colombier 			deltaS == nhgets(h->ip->length) - hlen) {
2497dd7cddfSDavid du Colombier 			/* special case for echoed terminal traffic */
2507dd7cddfSDavid du Colombier 			changes = SPECIAL_I;
2517dd7cddfSDavid du Colombier 			cp = new_seq;
2527dd7cddfSDavid du Colombier 		}
2537dd7cddfSDavid du Colombier 		break;
2547dd7cddfSDavid du Colombier 	case NEW_S:
2557dd7cddfSDavid du Colombier 		if (deltaS == nhgets(h->ip->length) - hlen) {
2567dd7cddfSDavid du Colombier 			/* special case for data xfer */
2577dd7cddfSDavid du Colombier 			changes = SPECIAL_D;
2587dd7cddfSDavid du Colombier 			cp = new_seq;
2597dd7cddfSDavid du Colombier 		}
2607dd7cddfSDavid du Colombier 		break;
2617dd7cddfSDavid du Colombier 	}
2627dd7cddfSDavid du Colombier 	deltaS = nhgets(ip->id) - nhgets(h->ip->id);
2637dd7cddfSDavid du Colombier 	if(deltaS != 1) {
2647dd7cddfSDavid du Colombier 		cp += encode(cp, deltaS);
2657dd7cddfSDavid du Colombier 		changes |= NEW_I;
2667dd7cddfSDavid du Colombier 	}
2677dd7cddfSDavid du Colombier 	if (tcp->flag[1] & PSH)
2687dd7cddfSDavid du Colombier 		changes |= TCP_PUSH_BIT;
2697dd7cddfSDavid du Colombier 	/*
2707dd7cddfSDavid du Colombier 	 * Grab the cksum before we overwrite it below. Then update our
2717dd7cddfSDavid du Colombier 	 * state with this packet's header.
2727dd7cddfSDavid du Colombier 	 */
2737dd7cddfSDavid du Colombier 	deltaA = nhgets(tcp->cksum);
2747dd7cddfSDavid du Colombier 	memmove(h->buf, b->rptr, hlen);
2757dd7cddfSDavid du Colombier 	h->len = hlen;
2767dd7cddfSDavid du Colombier 	h->tcp = (Tcphdr*)(h->buf + iplen);
2777dd7cddfSDavid du Colombier 
2787dd7cddfSDavid du Colombier 	/*
2797dd7cddfSDavid du Colombier 	 * We want to use the original packet as our compressed packet. (cp -
2807dd7cddfSDavid du Colombier 	 * new_seq) is the number of uchars we need for compressed sequence
2817dd7cddfSDavid du Colombier 	 * numbers. In addition we need one uchar for the change mask, one
2827dd7cddfSDavid du Colombier 	 * for the connection id and two for the tcp checksum. So, (cp -
2837dd7cddfSDavid du Colombier 	 * new_seq) + 4 uchars of header are needed. hlen is how many uchars
2847dd7cddfSDavid du Colombier 	 * of the original packet to toss so subtract the two to get the new
2857dd7cddfSDavid du Colombier 	 * packet size. The temporaries are gross -egs.
2867dd7cddfSDavid du Colombier 	 */
2877dd7cddfSDavid du Colombier 	deltaS = cp - new_seq;
2887dd7cddfSDavid du Colombier 	cp = b->rptr;
2897dd7cddfSDavid du Colombier 	if(comp->lastxmit != j || comp->compressid == 0) {
2907dd7cddfSDavid du Colombier 		comp->lastxmit = j;
2917dd7cddfSDavid du Colombier 		hlen -= deltaS + 4;
2927dd7cddfSDavid du Colombier 		cp += hlen;
2937dd7cddfSDavid du Colombier 		*cp++ = (changes | NEW_C);
2947dd7cddfSDavid du Colombier 		*cp++ = j;
2957dd7cddfSDavid du Colombier 	} else {
2967dd7cddfSDavid du Colombier 		hlen -= deltaS + 3;
2977dd7cddfSDavid du Colombier 		cp += hlen;
2987dd7cddfSDavid du Colombier 		*cp++ = changes;
2997dd7cddfSDavid du Colombier 	}
3007dd7cddfSDavid du Colombier 	b->rptr += hlen;
3017dd7cddfSDavid du Colombier 	hnputs(cp, deltaA);
3027dd7cddfSDavid du Colombier 	cp += 2;
3037dd7cddfSDavid du Colombier 	memmove(cp, new_seq, deltaS);
3047dd7cddfSDavid du Colombier 	*protop = Pvjctcp;
3057dd7cddfSDavid du Colombier 	return b;
3067dd7cddfSDavid du Colombier 
3077dd7cddfSDavid du Colombier rescue:
3087dd7cddfSDavid du Colombier 	/*
3097dd7cddfSDavid du Colombier 	 * Update connection state & send uncompressed packet
3107dd7cddfSDavid du Colombier 	 */
3117dd7cddfSDavid du Colombier 	memmove(h->buf, b->rptr, hlen);
3127dd7cddfSDavid du Colombier 	h->tcp = (Tcphdr*)(h->buf + iplen);
3137dd7cddfSDavid du Colombier 	h->len = hlen;
3147dd7cddfSDavid du Colombier 	ip->proto = j;
3157dd7cddfSDavid du Colombier 	comp->lastxmit = j;
3167dd7cddfSDavid du Colombier 	*protop = Pvjutcp;
3177dd7cddfSDavid du Colombier 	return b;
3187dd7cddfSDavid du Colombier }
3197dd7cddfSDavid du Colombier 
3207dd7cddfSDavid du Colombier Block*
tcpuncompress(Tcpc * comp,Block * b,int type)3217dd7cddfSDavid du Colombier tcpuncompress(Tcpc *comp, Block *b, int type)
3227dd7cddfSDavid du Colombier {
3237dd7cddfSDavid du Colombier 	uchar	*cp, changes;
3247dd7cddfSDavid du Colombier 	int	i;
3257dd7cddfSDavid du Colombier 	int	iplen, len;
3267dd7cddfSDavid du Colombier 	Iphdr	*ip;
3277dd7cddfSDavid du Colombier 	Tcphdr	*tcp;
3287dd7cddfSDavid du Colombier 	Hdr	*h;
3297dd7cddfSDavid du Colombier 
3307dd7cddfSDavid du Colombier 	if(type == Pvjutcp) {
3317dd7cddfSDavid du Colombier 		/*
3327dd7cddfSDavid du Colombier 		 *  Locate the saved state for this connection. If the state
3337dd7cddfSDavid du Colombier 		 *  index is legal, clear the 'discard' flag.
3347dd7cddfSDavid du Colombier 		 */
3357dd7cddfSDavid du Colombier 		ip = (Iphdr*)b->rptr;
3367dd7cddfSDavid du Colombier 		if(ip->proto >= MAX_STATES)
3377dd7cddfSDavid du Colombier 			goto rescue;
3387dd7cddfSDavid du Colombier 		iplen = (ip->vihl & 0xf) << 2;
3397dd7cddfSDavid du Colombier 		tcp = (Tcphdr*)(b->rptr + iplen);
3407dd7cddfSDavid du Colombier 		comp->lastrecv = ip->proto;
3417dd7cddfSDavid du Colombier 		len = iplen + ((tcp->flag[0] & 0xf0) >> 2);
3427dd7cddfSDavid du Colombier 		comp->err = 0;
3437dd7cddfSDavid du Colombier 		/*
3447dd7cddfSDavid du Colombier 		 * Restore the IP protocol field then save a copy of this
3457dd7cddfSDavid du Colombier 		 * packet header. The checksum is zeroed in the copy so we
3467dd7cddfSDavid du Colombier 		 * don't have to zero it each time we process a compressed
3477dd7cddfSDavid du Colombier 		 * packet.
3487dd7cddfSDavid du Colombier 		 */
3497dd7cddfSDavid du Colombier 		ip->proto = IP_TCPPROTO;
3507dd7cddfSDavid du Colombier 		h = &comp->r[comp->lastrecv];
3517dd7cddfSDavid du Colombier 		memmove(h->buf, b->rptr, len);
3527dd7cddfSDavid du Colombier 		h->tcp = (Tcphdr*)(h->buf + iplen);
3537dd7cddfSDavid du Colombier 		h->len = len;
3547dd7cddfSDavid du Colombier 		h->ip->cksum[0] = h->ip->cksum[1] = 0;
3557dd7cddfSDavid du Colombier 		return b;
3567dd7cddfSDavid du Colombier 	}
3577dd7cddfSDavid du Colombier 
3587dd7cddfSDavid du Colombier 	cp = b->rptr;
3597dd7cddfSDavid du Colombier 	changes = *cp++;
3607dd7cddfSDavid du Colombier 	if(changes & NEW_C) {
3617dd7cddfSDavid du Colombier 		/*
3627dd7cddfSDavid du Colombier 		 * Make sure the state index is in range, then grab the
3637dd7cddfSDavid du Colombier 		 * state. If we have a good state index, clear the 'discard'
3647dd7cddfSDavid du Colombier 		 * flag.
3657dd7cddfSDavid du Colombier 		 */
3667dd7cddfSDavid du Colombier 		if(*cp >= MAX_STATES)
3677dd7cddfSDavid du Colombier 			goto rescue;
3687dd7cddfSDavid du Colombier 		comp->err = 0;
3697dd7cddfSDavid du Colombier 		comp->lastrecv = *cp++;
3707dd7cddfSDavid du Colombier 	} else {
3717dd7cddfSDavid du Colombier 		/*
3727dd7cddfSDavid du Colombier 		 * This packet has no state index. If we've had a
3737dd7cddfSDavid du Colombier 		 * line error since the last time we got an explicit state
3747dd7cddfSDavid du Colombier 		 * index, we have to toss the packet.
3757dd7cddfSDavid du Colombier 		 */
3767dd7cddfSDavid du Colombier 		if(comp->err != 0){
3777dd7cddfSDavid du Colombier 			freeb(b);
3787dd7cddfSDavid du Colombier 			return nil;
3797dd7cddfSDavid du Colombier 		}
3807dd7cddfSDavid du Colombier 	}
3817dd7cddfSDavid du Colombier 
3827dd7cddfSDavid du Colombier 	/*
3837dd7cddfSDavid du Colombier 	 * Find the state then fill in the TCP checksum and PUSH bit.
3847dd7cddfSDavid du Colombier 	 */
3857dd7cddfSDavid du Colombier 	h = &comp->r[comp->lastrecv];
3867dd7cddfSDavid du Colombier 	ip = h->ip;
3877dd7cddfSDavid du Colombier 	tcp = h->tcp;
3887dd7cddfSDavid du Colombier 	len = h->len;
3897dd7cddfSDavid du Colombier 	memmove(tcp->cksum, cp, sizeof tcp->cksum);
3907dd7cddfSDavid du Colombier 	cp += 2;
3917dd7cddfSDavid du Colombier 	if(changes & TCP_PUSH_BIT)
3927dd7cddfSDavid du Colombier 		tcp->flag[1] |= PSH;
3937dd7cddfSDavid du Colombier 	else
3947dd7cddfSDavid du Colombier 		tcp->flag[1] &= ~PSH;
3957dd7cddfSDavid du Colombier 	/*
3967dd7cddfSDavid du Colombier 	 * Fix up the state's ack, seq, urg and win fields based on the
3977dd7cddfSDavid du Colombier 	 * changemask.
3987dd7cddfSDavid du Colombier 	 */
3997dd7cddfSDavid du Colombier 	switch (changes & SPECIALS_MASK) {
4007dd7cddfSDavid du Colombier 	case SPECIAL_I:
4017dd7cddfSDavid du Colombier 		i = nhgets(ip->length) - len;
4027dd7cddfSDavid du Colombier 		hnputl(tcp->ack, nhgetl(tcp->ack) + i);
4037dd7cddfSDavid du Colombier 		hnputl(tcp->seq, nhgetl(tcp->seq) + i);
4047dd7cddfSDavid du Colombier 		break;
4057dd7cddfSDavid du Colombier 
4067dd7cddfSDavid du Colombier 	case SPECIAL_D:
4077dd7cddfSDavid du Colombier 		hnputl(tcp->seq, nhgetl(tcp->seq) + nhgets(ip->length) - len);
4087dd7cddfSDavid du Colombier 		break;
4097dd7cddfSDavid du Colombier 
4107dd7cddfSDavid du Colombier 	default:
4117dd7cddfSDavid du Colombier 		if(changes & NEW_U) {
4127dd7cddfSDavid du Colombier 			tcp->flag[1] |= URG;
4137dd7cddfSDavid du Colombier 			if(*cp == 0){
4147dd7cddfSDavid du Colombier 				hnputs(tcp->urg, nhgets(cp+1));
4157dd7cddfSDavid du Colombier 				cp += 3;
4167dd7cddfSDavid du Colombier 			}else
4177dd7cddfSDavid du Colombier 				hnputs(tcp->urg, *cp++);
4187dd7cddfSDavid du Colombier 		} else
4197dd7cddfSDavid du Colombier 			tcp->flag[1] &= ~URG;
4207dd7cddfSDavid du Colombier 		if(changes & NEW_W)
4217dd7cddfSDavid du Colombier 			DECODES(tcp->win)
4227dd7cddfSDavid du Colombier 		if(changes & NEW_A)
4237dd7cddfSDavid du Colombier 			DECODEL(tcp->ack)
4247dd7cddfSDavid du Colombier 		if(changes & NEW_S)
4257dd7cddfSDavid du Colombier 			DECODEL(tcp->seq)
4267dd7cddfSDavid du Colombier 		break;
4277dd7cddfSDavid du Colombier 	}
4287dd7cddfSDavid du Colombier 
4297dd7cddfSDavid du Colombier 	/* Update the IP ID */
4307dd7cddfSDavid du Colombier 	if(changes & NEW_I)
4317dd7cddfSDavid du Colombier 		DECODES(ip->id)
4327dd7cddfSDavid du Colombier 	else
4337dd7cddfSDavid du Colombier 		hnputs(ip->id, nhgets(ip->id) + 1);
4347dd7cddfSDavid du Colombier 
4357dd7cddfSDavid du Colombier 	/*
4367dd7cddfSDavid du Colombier 	 *  At this point, cp points to the first uchar of data in the packet.
4377dd7cddfSDavid du Colombier 	 *  Back up cp by the TCP/IP header length to make room for the
4387dd7cddfSDavid du Colombier 	 *  reconstructed header.
4397dd7cddfSDavid du Colombier 	 *  We assume the packet we were handed has enough space to prepend
4407dd7cddfSDavid du Colombier 	 *  up to 128 uchars of header.
4417dd7cddfSDavid du Colombier 	 */
4427dd7cddfSDavid du Colombier 	b->rptr = cp;
4437dd7cddfSDavid du Colombier 	if(b->rptr - b->base < len){
4447dd7cddfSDavid du Colombier 		b = padb(b, len);
4457dd7cddfSDavid du Colombier 		b = pullup(b, blen(b));
4467dd7cddfSDavid du Colombier 	} else
4477dd7cddfSDavid du Colombier 		b->rptr -= len;
4487dd7cddfSDavid du Colombier 	hnputs(ip->length, BLEN(b));
4497dd7cddfSDavid du Colombier 	memmove(b->rptr, ip, len);
4507dd7cddfSDavid du Colombier 
4517dd7cddfSDavid du Colombier 	/* recompute the ip header checksum */
4527dd7cddfSDavid du Colombier 	ip = (Iphdr*)b->rptr;
4537dd7cddfSDavid du Colombier 	ip->cksum[0] = ip->cksum[1] = 0;
4547dd7cddfSDavid du Colombier 	hnputs(ip->cksum, ipcsum(b->rptr));
4557dd7cddfSDavid du Colombier 
4567dd7cddfSDavid du Colombier 	return b;
4577dd7cddfSDavid du Colombier 
4587dd7cddfSDavid du Colombier rescue:
4597dd7cddfSDavid du Colombier 	netlog("ppp: vj: Bad Packet!\n");
4607dd7cddfSDavid du Colombier 	comp->err = 1;
4617dd7cddfSDavid du Colombier 	freeb(b);
4627dd7cddfSDavid du Colombier 	return nil;
4637dd7cddfSDavid du Colombier }
4647dd7cddfSDavid du Colombier 
4657dd7cddfSDavid du Colombier Tcpc*
compress_init(Tcpc * c)4667dd7cddfSDavid du Colombier compress_init(Tcpc *c)
4677dd7cddfSDavid du Colombier {
4687dd7cddfSDavid du Colombier 	int i;
4697dd7cddfSDavid du Colombier 	Hdr *h;
4707dd7cddfSDavid du Colombier 
4717dd7cddfSDavid du Colombier 	if(c == nil)
4727dd7cddfSDavid du Colombier 		c = malloc(sizeof(Tcpc));
4737dd7cddfSDavid du Colombier 
4747dd7cddfSDavid du Colombier 	memset(c, 0, sizeof(*c));
4757dd7cddfSDavid du Colombier 	for(i = 0; i < MAX_STATES; i++){
4767dd7cddfSDavid du Colombier 		h = &c->t[i];
4777dd7cddfSDavid du Colombier 		h->ip = (Iphdr*)h->buf;
4787dd7cddfSDavid du Colombier 		h->tcp = (Tcphdr*)(h->buf + 20);
4797dd7cddfSDavid du Colombier 		h->len = 40;
4807dd7cddfSDavid du Colombier 		h = &c->r[i];
4817dd7cddfSDavid du Colombier 		h->ip = (Iphdr*)h->buf;
4827dd7cddfSDavid du Colombier 		h->tcp = (Tcphdr*)(h->buf + 20);
4837dd7cddfSDavid du Colombier 		h->len = 40;
4847dd7cddfSDavid du Colombier 	}
4857dd7cddfSDavid du Colombier 
4867dd7cddfSDavid du Colombier 	return c;
4877dd7cddfSDavid du Colombier }
4887dd7cddfSDavid du Colombier 
4897dd7cddfSDavid du Colombier Block*
compress(Tcpc * tcp,Block * b,int * protop)4907dd7cddfSDavid du Colombier compress(Tcpc *tcp, Block *b, int *protop)
4917dd7cddfSDavid du Colombier {
4927dd7cddfSDavid du Colombier 	Iphdr		*ip;
4937dd7cddfSDavid du Colombier 
4947dd7cddfSDavid du Colombier 	/*
4957dd7cddfSDavid du Colombier 	 * Bail if this is not a compressible IP packet
4967dd7cddfSDavid du Colombier 	 */
4977dd7cddfSDavid du Colombier 	ip = (Iphdr*)b->rptr;
4987dd7cddfSDavid du Colombier 	if((nhgets(ip->frag) & 0x3fff) != 0){
4997dd7cddfSDavid du Colombier 		*protop = Pip;
5007dd7cddfSDavid du Colombier 		return b;
5017dd7cddfSDavid du Colombier 	}
5027dd7cddfSDavid du Colombier 
5037dd7cddfSDavid du Colombier 	switch(ip->proto) {
5047dd7cddfSDavid du Colombier 	case IP_TCPPROTO:
5057dd7cddfSDavid du Colombier 		return tcpcompress(tcp, b, protop);
5067dd7cddfSDavid du Colombier 	default:
5077dd7cddfSDavid du Colombier 		*protop = Pip;
5087dd7cddfSDavid du Colombier 		return b;
5097dd7cddfSDavid du Colombier 	}
5107dd7cddfSDavid du Colombier }
5117dd7cddfSDavid du Colombier 
5127dd7cddfSDavid du Colombier int
compress_negotiate(Tcpc * tcp,uchar * data)5137dd7cddfSDavid du Colombier compress_negotiate(Tcpc *tcp, uchar *data)
5147dd7cddfSDavid du Colombier {
5157dd7cddfSDavid du Colombier 	if(data[0] != MAX_STATES - 1)
5167dd7cddfSDavid du Colombier 		return -1;
5177dd7cddfSDavid du Colombier 	tcp->compressid = data[1];
5187dd7cddfSDavid du Colombier 	return 0;
5197dd7cddfSDavid du Colombier }
520*fe853e23SDavid du Colombier 
521*fe853e23SDavid du Colombier /* called by ppp when there was a bad frame received */
522*fe853e23SDavid du Colombier void
compress_error(Tcpc * tcp)523*fe853e23SDavid du Colombier compress_error(Tcpc *tcp)
524*fe853e23SDavid du Colombier {
525*fe853e23SDavid du Colombier 	tcp->err = 1;
526*fe853e23SDavid du Colombier }
527