xref: /inferno-os/os/ip/compress.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1*74a4d8c2SCharles.Forsyth #include	"u.h"
2*74a4d8c2SCharles.Forsyth #include	"../port/lib.h"
3*74a4d8c2SCharles.Forsyth #include	"mem.h"
4*74a4d8c2SCharles.Forsyth #include	"dat.h"
5*74a4d8c2SCharles.Forsyth #include	"fns.h"
6*74a4d8c2SCharles.Forsyth #include	"../port/error.h"
7*74a4d8c2SCharles.Forsyth 
8*74a4d8c2SCharles.Forsyth #include	"ip.h"
9*74a4d8c2SCharles.Forsyth #include	"ppp.h"
10*74a4d8c2SCharles.Forsyth 
11*74a4d8c2SCharles.Forsyth typedef struct Iphdr	Iphdr;
12*74a4d8c2SCharles.Forsyth typedef struct Tcphdr	Tcphdr;
13*74a4d8c2SCharles.Forsyth typedef struct Ilhdr	Ilhdr;
14*74a4d8c2SCharles.Forsyth typedef struct Hdr	Hdr;
15*74a4d8c2SCharles.Forsyth typedef struct Tcpc	Tcpc;
16*74a4d8c2SCharles.Forsyth 
17*74a4d8c2SCharles.Forsyth struct Iphdr
18*74a4d8c2SCharles.Forsyth {
19*74a4d8c2SCharles.Forsyth 	uchar	vihl;		/* Version and header length */
20*74a4d8c2SCharles.Forsyth 	uchar	tos;		/* Type of service */
21*74a4d8c2SCharles.Forsyth 	uchar	length[2];	/* packet length */
22*74a4d8c2SCharles.Forsyth 	uchar	id[2];		/* Identification */
23*74a4d8c2SCharles.Forsyth 	uchar	frag[2];	/* Fragment information */
24*74a4d8c2SCharles.Forsyth 	uchar	ttl;		/* Time to live */
25*74a4d8c2SCharles.Forsyth 	uchar	proto;		/* Protocol */
26*74a4d8c2SCharles.Forsyth 	uchar	cksum[2];	/* Header checksum */
27*74a4d8c2SCharles.Forsyth 	ulong	src;		/* Ip source (byte ordering unimportant) */
28*74a4d8c2SCharles.Forsyth 	ulong	dst;		/* Ip destination (byte ordering unimportant) */
29*74a4d8c2SCharles.Forsyth };
30*74a4d8c2SCharles.Forsyth 
31*74a4d8c2SCharles.Forsyth struct Tcphdr
32*74a4d8c2SCharles.Forsyth {
33*74a4d8c2SCharles.Forsyth 	ulong	ports;		/* defined as a ulong to make comparisons easier */
34*74a4d8c2SCharles.Forsyth 	uchar	seq[4];
35*74a4d8c2SCharles.Forsyth 	uchar	ack[4];
36*74a4d8c2SCharles.Forsyth 	uchar	flag[2];
37*74a4d8c2SCharles.Forsyth 	uchar	win[2];
38*74a4d8c2SCharles.Forsyth 	uchar	cksum[2];
39*74a4d8c2SCharles.Forsyth 	uchar	urg[2];
40*74a4d8c2SCharles.Forsyth };
41*74a4d8c2SCharles.Forsyth 
42*74a4d8c2SCharles.Forsyth struct Ilhdr
43*74a4d8c2SCharles.Forsyth {
44*74a4d8c2SCharles.Forsyth 	uchar	sum[2];	/* Checksum including header */
45*74a4d8c2SCharles.Forsyth 	uchar	len[2];	/* Packet length */
46*74a4d8c2SCharles.Forsyth 	uchar	type;		/* Packet type */
47*74a4d8c2SCharles.Forsyth 	uchar	spec;		/* Special */
48*74a4d8c2SCharles.Forsyth 	uchar	src[2];	/* Src port */
49*74a4d8c2SCharles.Forsyth 	uchar	dst[2];	/* Dst port */
50*74a4d8c2SCharles.Forsyth 	uchar	id[4];	/* Sequence id */
51*74a4d8c2SCharles.Forsyth 	uchar	ack[4];	/* Acked sequence */
52*74a4d8c2SCharles.Forsyth };
53*74a4d8c2SCharles.Forsyth 
54*74a4d8c2SCharles.Forsyth enum
55*74a4d8c2SCharles.Forsyth {
56*74a4d8c2SCharles.Forsyth 	URG		= 0x20,		/* Data marked urgent */
57*74a4d8c2SCharles.Forsyth 	ACK		= 0x10,		/* Aknowledge is valid */
58*74a4d8c2SCharles.Forsyth 	PSH		= 0x08,		/* Whole data pipe is pushed */
59*74a4d8c2SCharles.Forsyth 	RST		= 0x04,		/* Reset connection */
60*74a4d8c2SCharles.Forsyth 	SYN		= 0x02,		/* Pkt. is synchronise */
61*74a4d8c2SCharles.Forsyth 	FIN		= 0x01,		/* Start close down */
62*74a4d8c2SCharles.Forsyth 
63*74a4d8c2SCharles.Forsyth 	IP_DF		= 0x4000,	/* Don't fragment */
64*74a4d8c2SCharles.Forsyth 
65*74a4d8c2SCharles.Forsyth 	IP_TCPPROTO	= 6,
66*74a4d8c2SCharles.Forsyth 	IP_ILPROTO	= 40,
67*74a4d8c2SCharles.Forsyth 	IL_IPHDR	= 20,
68*74a4d8c2SCharles.Forsyth };
69*74a4d8c2SCharles.Forsyth 
70*74a4d8c2SCharles.Forsyth struct Hdr
71*74a4d8c2SCharles.Forsyth {
72*74a4d8c2SCharles.Forsyth 	uchar	buf[128];
73*74a4d8c2SCharles.Forsyth 	Iphdr	*ip;
74*74a4d8c2SCharles.Forsyth 	Tcphdr	*tcp;
75*74a4d8c2SCharles.Forsyth 	int	len;
76*74a4d8c2SCharles.Forsyth };
77*74a4d8c2SCharles.Forsyth 
78*74a4d8c2SCharles.Forsyth struct Tcpc
79*74a4d8c2SCharles.Forsyth {
80*74a4d8c2SCharles.Forsyth 	uchar	lastrecv;
81*74a4d8c2SCharles.Forsyth 	uchar	lastxmit;
82*74a4d8c2SCharles.Forsyth 	uchar	basexmit;
83*74a4d8c2SCharles.Forsyth 	uchar	err;
84*74a4d8c2SCharles.Forsyth 	uchar	compressid;
85*74a4d8c2SCharles.Forsyth 	Hdr	t[MAX_STATES];
86*74a4d8c2SCharles.Forsyth 	Hdr	r[MAX_STATES];
87*74a4d8c2SCharles.Forsyth };
88*74a4d8c2SCharles.Forsyth 
89*74a4d8c2SCharles.Forsyth enum
90*74a4d8c2SCharles.Forsyth {	/* flag bits for what changed in a packet */
91*74a4d8c2SCharles.Forsyth 	NEW_U=(1<<0),	/* tcp only */
92*74a4d8c2SCharles.Forsyth 	NEW_W=(1<<1),	/* tcp only */
93*74a4d8c2SCharles.Forsyth 	NEW_A=(1<<2),	/* il tcp */
94*74a4d8c2SCharles.Forsyth 	NEW_S=(1<<3),	/* tcp only */
95*74a4d8c2SCharles.Forsyth 	NEW_P=(1<<4),	/* tcp only */
96*74a4d8c2SCharles.Forsyth 	NEW_I=(1<<5),	/* il tcp */
97*74a4d8c2SCharles.Forsyth 	NEW_C=(1<<6),	/* il tcp */
98*74a4d8c2SCharles.Forsyth 	NEW_T=(1<<7),	/* il only */
99*74a4d8c2SCharles.Forsyth 	TCP_PUSH_BIT	= 0x10,
100*74a4d8c2SCharles.Forsyth };
101*74a4d8c2SCharles.Forsyth 
102*74a4d8c2SCharles.Forsyth /* reserved, special-case values of above for tcp */
103*74a4d8c2SCharles.Forsyth #define SPECIAL_I (NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
104*74a4d8c2SCharles.Forsyth #define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
105*74a4d8c2SCharles.Forsyth #define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
106*74a4d8c2SCharles.Forsyth 
107*74a4d8c2SCharles.Forsyth int
encode(void * p,ulong n)108*74a4d8c2SCharles.Forsyth encode(void *p, ulong n)
109*74a4d8c2SCharles.Forsyth {
110*74a4d8c2SCharles.Forsyth 	uchar	*cp;
111*74a4d8c2SCharles.Forsyth 
112*74a4d8c2SCharles.Forsyth 	cp = p;
113*74a4d8c2SCharles.Forsyth 	if(n >= 256 || n == 0) {
114*74a4d8c2SCharles.Forsyth 		*cp++ = 0;
115*74a4d8c2SCharles.Forsyth 		cp[0] = n >> 8;
116*74a4d8c2SCharles.Forsyth 		cp[1] = n;
117*74a4d8c2SCharles.Forsyth 		return 3;
118*74a4d8c2SCharles.Forsyth 	} else
119*74a4d8c2SCharles.Forsyth 		*cp = n;
120*74a4d8c2SCharles.Forsyth 	return 1;
121*74a4d8c2SCharles.Forsyth }
122*74a4d8c2SCharles.Forsyth 
123*74a4d8c2SCharles.Forsyth #define DECODEL(f) { \
124*74a4d8c2SCharles.Forsyth 	if (*cp == 0) {\
125*74a4d8c2SCharles.Forsyth 		hnputl(f, nhgetl(f) + ((cp[1] << 8) | cp[2])); \
126*74a4d8c2SCharles.Forsyth 		cp += 3; \
127*74a4d8c2SCharles.Forsyth 	} else { \
128*74a4d8c2SCharles.Forsyth 		hnputl(f, nhgetl(f) + (ulong)*cp++); \
129*74a4d8c2SCharles.Forsyth 	} \
130*74a4d8c2SCharles.Forsyth }
131*74a4d8c2SCharles.Forsyth #define DECODES(f) { \
132*74a4d8c2SCharles.Forsyth 	if (*cp == 0) {\
133*74a4d8c2SCharles.Forsyth 		hnputs(f, nhgets(f) + ((cp[1] << 8) | cp[2])); \
134*74a4d8c2SCharles.Forsyth 		cp += 3; \
135*74a4d8c2SCharles.Forsyth 	} else { \
136*74a4d8c2SCharles.Forsyth 		hnputs(f, nhgets(f) + (ulong)*cp++); \
137*74a4d8c2SCharles.Forsyth 	} \
138*74a4d8c2SCharles.Forsyth }
139*74a4d8c2SCharles.Forsyth 
140*74a4d8c2SCharles.Forsyth ushort
tcpcompress(Tcpc * comp,Block * b,Fs *)141*74a4d8c2SCharles.Forsyth tcpcompress(Tcpc *comp, Block *b, Fs *)
142*74a4d8c2SCharles.Forsyth {
143*74a4d8c2SCharles.Forsyth 	Iphdr	*ip;		/* current packet */
144*74a4d8c2SCharles.Forsyth 	Tcphdr	*tcp;		/* current pkt */
145*74a4d8c2SCharles.Forsyth 	ulong 	iplen, tcplen, hlen;	/* header length in bytes */
146*74a4d8c2SCharles.Forsyth 	ulong 	deltaS, deltaA;	/* general purpose temporaries */
147*74a4d8c2SCharles.Forsyth 	ulong 	changes;	/* change mask */
148*74a4d8c2SCharles.Forsyth 	uchar	new_seq[16];	/* changes from last to current */
149*74a4d8c2SCharles.Forsyth 	uchar	*cp;
150*74a4d8c2SCharles.Forsyth 	Hdr	*h;		/* last packet */
151*74a4d8c2SCharles.Forsyth 	int 	i, j;
152*74a4d8c2SCharles.Forsyth 
153*74a4d8c2SCharles.Forsyth 	/*
154*74a4d8c2SCharles.Forsyth 	 * Bail if this is not a compressible TCP/IP packet
155*74a4d8c2SCharles.Forsyth 	 */
156*74a4d8c2SCharles.Forsyth 	ip = (Iphdr*)b->rp;
157*74a4d8c2SCharles.Forsyth 	iplen = (ip->vihl & 0xf) << 2;
158*74a4d8c2SCharles.Forsyth 	tcp = (Tcphdr*)(b->rp + iplen);
159*74a4d8c2SCharles.Forsyth 	tcplen = (tcp->flag[0] & 0xf0) >> 2;
160*74a4d8c2SCharles.Forsyth 	hlen = iplen + tcplen;
161*74a4d8c2SCharles.Forsyth 	if((tcp->flag[1] & (SYN|FIN|RST|ACK)) != ACK)
162*74a4d8c2SCharles.Forsyth 		return Pip;	/* connection control */
163*74a4d8c2SCharles.Forsyth 
164*74a4d8c2SCharles.Forsyth 	/*
165*74a4d8c2SCharles.Forsyth 	 * Packet is compressible, look for a connection
166*74a4d8c2SCharles.Forsyth 	 */
167*74a4d8c2SCharles.Forsyth 	changes = 0;
168*74a4d8c2SCharles.Forsyth 	cp = new_seq;
169*74a4d8c2SCharles.Forsyth 	j = comp->lastxmit;
170*74a4d8c2SCharles.Forsyth 	h = &comp->t[j];
171*74a4d8c2SCharles.Forsyth 	if(ip->src != h->ip->src || ip->dst != h->ip->dst
172*74a4d8c2SCharles.Forsyth 	|| tcp->ports != h->tcp->ports) {
173*74a4d8c2SCharles.Forsyth 		for(i = 0; i < MAX_STATES; ++i) {
174*74a4d8c2SCharles.Forsyth 			j = (comp->basexmit + i) % MAX_STATES;
175*74a4d8c2SCharles.Forsyth 			h = &comp->t[j];
176*74a4d8c2SCharles.Forsyth 			if(ip->src == h->ip->src && ip->dst == h->ip->dst
177*74a4d8c2SCharles.Forsyth 			&& tcp->ports == h->tcp->ports)
178*74a4d8c2SCharles.Forsyth 				goto found;
179*74a4d8c2SCharles.Forsyth 		}
180*74a4d8c2SCharles.Forsyth 
181*74a4d8c2SCharles.Forsyth 		/* no connection, reuse the oldest */
182*74a4d8c2SCharles.Forsyth 		if(i == MAX_STATES) {
183*74a4d8c2SCharles.Forsyth 			j = comp->basexmit;
184*74a4d8c2SCharles.Forsyth 			j = (j + MAX_STATES - 1) % MAX_STATES;
185*74a4d8c2SCharles.Forsyth 			comp->basexmit = j;
186*74a4d8c2SCharles.Forsyth 			h = &comp->t[j];
187*74a4d8c2SCharles.Forsyth 			goto raise;
188*74a4d8c2SCharles.Forsyth 		}
189*74a4d8c2SCharles.Forsyth 	}
190*74a4d8c2SCharles.Forsyth found:
191*74a4d8c2SCharles.Forsyth 
192*74a4d8c2SCharles.Forsyth 	/*
193*74a4d8c2SCharles.Forsyth 	 * Make sure that only what we expect to change changed.
194*74a4d8c2SCharles.Forsyth 	 */
195*74a4d8c2SCharles.Forsyth 	if(ip->vihl  != h->ip->vihl || ip->tos   != h->ip->tos ||
196*74a4d8c2SCharles.Forsyth 	   ip->ttl   != h->ip->ttl  || ip->proto != h->ip->proto)
197*74a4d8c2SCharles.Forsyth 		goto raise;	/* headers changed */
198*74a4d8c2SCharles.Forsyth 	if(iplen != sizeof(Iphdr) && memcmp(ip+1, h->ip+1, iplen - sizeof(Iphdr)))
199*74a4d8c2SCharles.Forsyth 		goto raise;	/* ip options changed */
200*74a4d8c2SCharles.Forsyth 	if(tcplen != sizeof(Tcphdr) && memcmp(tcp+1, h->tcp+1, tcplen - sizeof(Tcphdr)))
201*74a4d8c2SCharles.Forsyth 		goto raise;	/* tcp options changed */
202*74a4d8c2SCharles.Forsyth 
203*74a4d8c2SCharles.Forsyth 	if(tcp->flag[1] & URG) {
204*74a4d8c2SCharles.Forsyth 		cp += encode(cp, nhgets(tcp->urg));
205*74a4d8c2SCharles.Forsyth 		changes |= NEW_U;
206*74a4d8c2SCharles.Forsyth 	} else if(memcmp(tcp->urg, h->tcp->urg, sizeof(tcp->urg)) != 0)
207*74a4d8c2SCharles.Forsyth 		goto raise;
208*74a4d8c2SCharles.Forsyth 	if(deltaS = nhgets(tcp->win) - nhgets(h->tcp->win)) {
209*74a4d8c2SCharles.Forsyth 		cp += encode(cp, deltaS);
210*74a4d8c2SCharles.Forsyth 		changes |= NEW_W;
211*74a4d8c2SCharles.Forsyth 	}
212*74a4d8c2SCharles.Forsyth 	if(deltaA = nhgetl(tcp->ack) - nhgetl(h->tcp->ack)) {
213*74a4d8c2SCharles.Forsyth 		if(deltaA > 0xffff)
214*74a4d8c2SCharles.Forsyth 			goto raise;
215*74a4d8c2SCharles.Forsyth 		cp += encode(cp, deltaA);
216*74a4d8c2SCharles.Forsyth 		changes |= NEW_A;
217*74a4d8c2SCharles.Forsyth 	}
218*74a4d8c2SCharles.Forsyth 	if(deltaS = nhgetl(tcp->seq) - nhgetl(h->tcp->seq)) {
219*74a4d8c2SCharles.Forsyth 		if (deltaS > 0xffff)
220*74a4d8c2SCharles.Forsyth 			goto raise;
221*74a4d8c2SCharles.Forsyth 		cp += encode(cp, deltaS);
222*74a4d8c2SCharles.Forsyth 		changes |= NEW_S;
223*74a4d8c2SCharles.Forsyth 	}
224*74a4d8c2SCharles.Forsyth 
225*74a4d8c2SCharles.Forsyth 	/*
226*74a4d8c2SCharles.Forsyth 	 * Look for the special-case encodings.
227*74a4d8c2SCharles.Forsyth 	 */
228*74a4d8c2SCharles.Forsyth 	switch(changes) {
229*74a4d8c2SCharles.Forsyth 	case 0:
230*74a4d8c2SCharles.Forsyth 		/*
231*74a4d8c2SCharles.Forsyth 		 * Nothing changed. If this packet contains data and the last
232*74a4d8c2SCharles.Forsyth 		 * one didn't, this is probably a data packet following an
233*74a4d8c2SCharles.Forsyth 		 * ack (normal on an interactive connection) and we send it
234*74a4d8c2SCharles.Forsyth 		 * compressed. Otherwise it's probably a retransmit,
235*74a4d8c2SCharles.Forsyth 		 * retransmitted ack or window probe.  Send it uncompressed
236*74a4d8c2SCharles.Forsyth 		 * in case the other side missed the compressed version.
237*74a4d8c2SCharles.Forsyth 		 */
238*74a4d8c2SCharles.Forsyth 		if(nhgets(ip->length) == nhgets(h->ip->length) ||
239*74a4d8c2SCharles.Forsyth 		   nhgets(h->ip->length) != hlen)
240*74a4d8c2SCharles.Forsyth 			goto raise;
241*74a4d8c2SCharles.Forsyth 		break;
242*74a4d8c2SCharles.Forsyth 	case SPECIAL_I:
243*74a4d8c2SCharles.Forsyth 	case SPECIAL_D:
244*74a4d8c2SCharles.Forsyth 		/*
245*74a4d8c2SCharles.Forsyth 		 * Actual changes match one of our special case encodings --
246*74a4d8c2SCharles.Forsyth 		 * send packet uncompressed.
247*74a4d8c2SCharles.Forsyth 		 */
248*74a4d8c2SCharles.Forsyth 		goto raise;
249*74a4d8c2SCharles.Forsyth 	case NEW_S | NEW_A:
250*74a4d8c2SCharles.Forsyth 		if (deltaS == deltaA &&
251*74a4d8c2SCharles.Forsyth 			deltaS == nhgets(h->ip->length) - hlen) {
252*74a4d8c2SCharles.Forsyth 			/* special case for echoed terminal traffic */
253*74a4d8c2SCharles.Forsyth 			changes = SPECIAL_I;
254*74a4d8c2SCharles.Forsyth 			cp = new_seq;
255*74a4d8c2SCharles.Forsyth 		}
256*74a4d8c2SCharles.Forsyth 		break;
257*74a4d8c2SCharles.Forsyth 	case NEW_S:
258*74a4d8c2SCharles.Forsyth 		if (deltaS == nhgets(h->ip->length) - hlen) {
259*74a4d8c2SCharles.Forsyth 			/* special case for data xfer */
260*74a4d8c2SCharles.Forsyth 			changes = SPECIAL_D;
261*74a4d8c2SCharles.Forsyth 			cp = new_seq;
262*74a4d8c2SCharles.Forsyth 		}
263*74a4d8c2SCharles.Forsyth 		break;
264*74a4d8c2SCharles.Forsyth 	}
265*74a4d8c2SCharles.Forsyth 	deltaS = nhgets(ip->id) - nhgets(h->ip->id);
266*74a4d8c2SCharles.Forsyth 	if(deltaS != 1) {
267*74a4d8c2SCharles.Forsyth 		cp += encode(cp, deltaS);
268*74a4d8c2SCharles.Forsyth 		changes |= NEW_I;
269*74a4d8c2SCharles.Forsyth 	}
270*74a4d8c2SCharles.Forsyth 	if (tcp->flag[1] & PSH)
271*74a4d8c2SCharles.Forsyth 		changes |= TCP_PUSH_BIT;
272*74a4d8c2SCharles.Forsyth 	/*
273*74a4d8c2SCharles.Forsyth 	 * Grab the cksum before we overwrite it below. Then update our
274*74a4d8c2SCharles.Forsyth 	 * state with this packet's header.
275*74a4d8c2SCharles.Forsyth 	 */
276*74a4d8c2SCharles.Forsyth 	deltaA = nhgets(tcp->cksum);
277*74a4d8c2SCharles.Forsyth 	memmove(h->buf, b->rp, hlen);
278*74a4d8c2SCharles.Forsyth 	h->len = hlen;
279*74a4d8c2SCharles.Forsyth 	h->tcp = (Tcphdr*)(h->buf + iplen);
280*74a4d8c2SCharles.Forsyth 
281*74a4d8c2SCharles.Forsyth 	/*
282*74a4d8c2SCharles.Forsyth 	 * We want to use the original packet as our compressed packet. (cp -
283*74a4d8c2SCharles.Forsyth 	 * new_seq) is the number of bytes we need for compressed sequence
284*74a4d8c2SCharles.Forsyth 	 * numbers. In addition we need one byte for the change mask, one
285*74a4d8c2SCharles.Forsyth 	 * for the connection id and two for the tcp checksum. So, (cp -
286*74a4d8c2SCharles.Forsyth 	 * new_seq) + 4 bytes of header are needed. hlen is how many bytes
287*74a4d8c2SCharles.Forsyth 	 * of the original packet to toss so subtract the two to get the new
288*74a4d8c2SCharles.Forsyth 	 * packet size. The temporaries are gross -egs.
289*74a4d8c2SCharles.Forsyth 	 */
290*74a4d8c2SCharles.Forsyth 	deltaS = cp - new_seq;
291*74a4d8c2SCharles.Forsyth 	cp = b->rp;
292*74a4d8c2SCharles.Forsyth 	if(comp->lastxmit != j || comp->compressid == 0) {
293*74a4d8c2SCharles.Forsyth 		comp->lastxmit = j;
294*74a4d8c2SCharles.Forsyth 		hlen -= deltaS + 4;
295*74a4d8c2SCharles.Forsyth 		cp += hlen;
296*74a4d8c2SCharles.Forsyth 		*cp++ = (changes | NEW_C);
297*74a4d8c2SCharles.Forsyth 		*cp++ = j;
298*74a4d8c2SCharles.Forsyth 	} else {
299*74a4d8c2SCharles.Forsyth 		hlen -= deltaS + 3;
300*74a4d8c2SCharles.Forsyth 		cp += hlen;
301*74a4d8c2SCharles.Forsyth 		*cp++ = changes;
302*74a4d8c2SCharles.Forsyth 	}
303*74a4d8c2SCharles.Forsyth 	b->rp += hlen;
304*74a4d8c2SCharles.Forsyth 	hnputs(cp, deltaA);
305*74a4d8c2SCharles.Forsyth 	cp += 2;
306*74a4d8c2SCharles.Forsyth 	memmove(cp, new_seq, deltaS);
307*74a4d8c2SCharles.Forsyth 	return Pvjctcp;
308*74a4d8c2SCharles.Forsyth 
309*74a4d8c2SCharles.Forsyth raise:
310*74a4d8c2SCharles.Forsyth 	/*
311*74a4d8c2SCharles.Forsyth 	 * Update connection state & send uncompressed packet
312*74a4d8c2SCharles.Forsyth 	 */
313*74a4d8c2SCharles.Forsyth 	memmove(h->buf, b->rp, hlen);
314*74a4d8c2SCharles.Forsyth 	h->tcp = (Tcphdr*)(h->buf + iplen);
315*74a4d8c2SCharles.Forsyth 	h->len = hlen;
316*74a4d8c2SCharles.Forsyth 	h->ip->proto = j;
317*74a4d8c2SCharles.Forsyth 	comp->lastxmit = j;
318*74a4d8c2SCharles.Forsyth 	return Pvjutcp;
319*74a4d8c2SCharles.Forsyth }
320*74a4d8c2SCharles.Forsyth 
321*74a4d8c2SCharles.Forsyth Block*
tcpuncompress(Tcpc * comp,Block * b,ushort type,Fs * f)322*74a4d8c2SCharles.Forsyth tcpuncompress(Tcpc *comp, Block *b, ushort type, Fs *f)
323*74a4d8c2SCharles.Forsyth {
324*74a4d8c2SCharles.Forsyth 	uchar	*cp, changes;
325*74a4d8c2SCharles.Forsyth 	int	i;
326*74a4d8c2SCharles.Forsyth 	int	iplen, len;
327*74a4d8c2SCharles.Forsyth 	Iphdr	*ip;
328*74a4d8c2SCharles.Forsyth 	Tcphdr	*tcp;
329*74a4d8c2SCharles.Forsyth 	Hdr	*h;
330*74a4d8c2SCharles.Forsyth 
331*74a4d8c2SCharles.Forsyth 	if(type == Pvjutcp) {
332*74a4d8c2SCharles.Forsyth 		/*
333*74a4d8c2SCharles.Forsyth 		 *  Locate the saved state for this connection. If the state
334*74a4d8c2SCharles.Forsyth 		 *  index is legal, clear the 'discard' flag.
335*74a4d8c2SCharles.Forsyth 		 */
336*74a4d8c2SCharles.Forsyth 		ip = (Iphdr*)b->rp;
337*74a4d8c2SCharles.Forsyth 		if(ip->proto >= MAX_STATES)
338*74a4d8c2SCharles.Forsyth 			goto raise;
339*74a4d8c2SCharles.Forsyth 		iplen = (ip->vihl & 0xf) << 2;
340*74a4d8c2SCharles.Forsyth 		tcp = (Tcphdr*)(b->rp + iplen);
341*74a4d8c2SCharles.Forsyth 		comp->lastrecv = ip->proto;
342*74a4d8c2SCharles.Forsyth 		len = iplen + ((tcp->flag[0] & 0xf0) >> 2);
343*74a4d8c2SCharles.Forsyth 		comp->err = 0;
344*74a4d8c2SCharles.Forsyth netlog(f, Logcompress, "uncompressed %d\n", comp->lastrecv);
345*74a4d8c2SCharles.Forsyth 		/*
346*74a4d8c2SCharles.Forsyth 		 * Restore the IP protocol field then save a copy of this
347*74a4d8c2SCharles.Forsyth 		 * packet header. The checksum is zeroed in the copy so we
348*74a4d8c2SCharles.Forsyth 		 * don't have to zero it each time we process a compressed
349*74a4d8c2SCharles.Forsyth 		 * packet.
350*74a4d8c2SCharles.Forsyth 		 */
351*74a4d8c2SCharles.Forsyth 		ip->proto = IP_TCPPROTO;
352*74a4d8c2SCharles.Forsyth 		h = &comp->r[comp->lastrecv];
353*74a4d8c2SCharles.Forsyth 		memmove(h->buf, b->rp, len);
354*74a4d8c2SCharles.Forsyth 		h->tcp = (Tcphdr*)(h->buf + iplen);
355*74a4d8c2SCharles.Forsyth 		h->len = len;
356*74a4d8c2SCharles.Forsyth 		h->ip->cksum[0] = h->ip->cksum[1] = 0;
357*74a4d8c2SCharles.Forsyth 		return b;
358*74a4d8c2SCharles.Forsyth 	}
359*74a4d8c2SCharles.Forsyth 
360*74a4d8c2SCharles.Forsyth 	cp = b->rp;
361*74a4d8c2SCharles.Forsyth 	changes = *cp++;
362*74a4d8c2SCharles.Forsyth 	if(changes & NEW_C) {
363*74a4d8c2SCharles.Forsyth 		/*
364*74a4d8c2SCharles.Forsyth 		 * Make sure the state index is in range, then grab the
365*74a4d8c2SCharles.Forsyth 		 * state. If we have a good state index, clear the 'discard'
366*74a4d8c2SCharles.Forsyth 		 * flag.
367*74a4d8c2SCharles.Forsyth 		 */
368*74a4d8c2SCharles.Forsyth 		if(*cp >= MAX_STATES)
369*74a4d8c2SCharles.Forsyth 			goto raise;
370*74a4d8c2SCharles.Forsyth 		comp->err = 0;
371*74a4d8c2SCharles.Forsyth 		comp->lastrecv = *cp++;
372*74a4d8c2SCharles.Forsyth netlog(f, Logcompress, "newc %d\n", comp->lastrecv);
373*74a4d8c2SCharles.Forsyth 	} else {
374*74a4d8c2SCharles.Forsyth 		/*
375*74a4d8c2SCharles.Forsyth 		 * This packet has no state index. If we've had a
376*74a4d8c2SCharles.Forsyth 		 * line error since the last time we got an explicit state
377*74a4d8c2SCharles.Forsyth 		 * index, we have to toss the packet.
378*74a4d8c2SCharles.Forsyth 		 */
379*74a4d8c2SCharles.Forsyth 		if(comp->err != 0){
380*74a4d8c2SCharles.Forsyth 			freeblist(b);
381*74a4d8c2SCharles.Forsyth 			return nil;
382*74a4d8c2SCharles.Forsyth 		}
383*74a4d8c2SCharles.Forsyth netlog(f, Logcompress, "oldc %d\n", comp->lastrecv);
384*74a4d8c2SCharles.Forsyth 	}
385*74a4d8c2SCharles.Forsyth 
386*74a4d8c2SCharles.Forsyth 	/*
387*74a4d8c2SCharles.Forsyth 	 * Find the state then fill in the TCP checksum and PUSH bit.
388*74a4d8c2SCharles.Forsyth 	 */
389*74a4d8c2SCharles.Forsyth 	h = &comp->r[comp->lastrecv];
390*74a4d8c2SCharles.Forsyth 	ip = h->ip;
391*74a4d8c2SCharles.Forsyth 	tcp = h->tcp;
392*74a4d8c2SCharles.Forsyth 	len = h->len;
393*74a4d8c2SCharles.Forsyth 	memmove(tcp->cksum, cp, sizeof tcp->cksum);
394*74a4d8c2SCharles.Forsyth 	cp += 2;
395*74a4d8c2SCharles.Forsyth 	if(changes & TCP_PUSH_BIT)
396*74a4d8c2SCharles.Forsyth 		tcp->flag[1] |= PSH;
397*74a4d8c2SCharles.Forsyth 	else
398*74a4d8c2SCharles.Forsyth 		tcp->flag[1] &= ~PSH;
399*74a4d8c2SCharles.Forsyth 	/*
400*74a4d8c2SCharles.Forsyth 	 * Fix up the state's ack, seq, urg and win fields based on the
401*74a4d8c2SCharles.Forsyth 	 * changemask.
402*74a4d8c2SCharles.Forsyth 	 */
403*74a4d8c2SCharles.Forsyth 	switch (changes & SPECIALS_MASK) {
404*74a4d8c2SCharles.Forsyth 	case SPECIAL_I:
405*74a4d8c2SCharles.Forsyth 		i = nhgets(ip->length) - len;
406*74a4d8c2SCharles.Forsyth 		hnputl(tcp->ack, nhgetl(tcp->ack) + i);
407*74a4d8c2SCharles.Forsyth 		hnputl(tcp->seq, nhgetl(tcp->seq) + i);
408*74a4d8c2SCharles.Forsyth 		break;
409*74a4d8c2SCharles.Forsyth 
410*74a4d8c2SCharles.Forsyth 	case SPECIAL_D:
411*74a4d8c2SCharles.Forsyth 		hnputl(tcp->seq, nhgetl(tcp->seq) + nhgets(ip->length) - len);
412*74a4d8c2SCharles.Forsyth 		break;
413*74a4d8c2SCharles.Forsyth 
414*74a4d8c2SCharles.Forsyth 	default:
415*74a4d8c2SCharles.Forsyth 		if(changes & NEW_U) {
416*74a4d8c2SCharles.Forsyth 			tcp->flag[1] |= URG;
417*74a4d8c2SCharles.Forsyth 			if(*cp == 0){
418*74a4d8c2SCharles.Forsyth 				hnputs(tcp->urg, nhgets(cp+1));
419*74a4d8c2SCharles.Forsyth 				cp += 3;
420*74a4d8c2SCharles.Forsyth 			}else
421*74a4d8c2SCharles.Forsyth 				hnputs(tcp->urg, *cp++);
422*74a4d8c2SCharles.Forsyth 		} else
423*74a4d8c2SCharles.Forsyth 			tcp->flag[1] &= ~URG;
424*74a4d8c2SCharles.Forsyth 		if(changes & NEW_W)
425*74a4d8c2SCharles.Forsyth 			DECODES(tcp->win)
426*74a4d8c2SCharles.Forsyth 		if(changes & NEW_A)
427*74a4d8c2SCharles.Forsyth 			DECODEL(tcp->ack)
428*74a4d8c2SCharles.Forsyth 		if(changes & NEW_S)
429*74a4d8c2SCharles.Forsyth 			DECODEL(tcp->seq)
430*74a4d8c2SCharles.Forsyth 		break;
431*74a4d8c2SCharles.Forsyth 	}
432*74a4d8c2SCharles.Forsyth 
433*74a4d8c2SCharles.Forsyth 	/* Update the IP ID */
434*74a4d8c2SCharles.Forsyth 	if(changes & NEW_I)
435*74a4d8c2SCharles.Forsyth 		DECODES(ip->id)
436*74a4d8c2SCharles.Forsyth 	else
437*74a4d8c2SCharles.Forsyth 		hnputs(ip->id, nhgets(ip->id) + 1);
438*74a4d8c2SCharles.Forsyth 
439*74a4d8c2SCharles.Forsyth 	/*
440*74a4d8c2SCharles.Forsyth 	 *  At this point, cp points to the first byte of data in the packet.
441*74a4d8c2SCharles.Forsyth 	 *  Back up cp by the TCP/IP header length to make room for the
442*74a4d8c2SCharles.Forsyth 	 *  reconstructed header.
443*74a4d8c2SCharles.Forsyth 	 *  We assume the packet we were handed has enough space to prepend
444*74a4d8c2SCharles.Forsyth 	 *  up to 128 bytes of header.
445*74a4d8c2SCharles.Forsyth 	 */
446*74a4d8c2SCharles.Forsyth 	b->rp = cp;
447*74a4d8c2SCharles.Forsyth 	if(b->rp - b->base < len){
448*74a4d8c2SCharles.Forsyth 		b = padblock(b, len);
449*74a4d8c2SCharles.Forsyth 		b = pullupblock(b, blocklen(b));
450*74a4d8c2SCharles.Forsyth 	} else
451*74a4d8c2SCharles.Forsyth 		b->rp -= len;
452*74a4d8c2SCharles.Forsyth 	hnputs(ip->length, BLEN(b));
453*74a4d8c2SCharles.Forsyth 	memmove(b->rp, ip, len);
454*74a4d8c2SCharles.Forsyth 
455*74a4d8c2SCharles.Forsyth 	/* recompute the ip header checksum */
456*74a4d8c2SCharles.Forsyth 	ip = (Iphdr*)b->rp;
457*74a4d8c2SCharles.Forsyth 	hnputs(ip->cksum, ipcsum(b->rp));
458*74a4d8c2SCharles.Forsyth 	return b;
459*74a4d8c2SCharles.Forsyth 
460*74a4d8c2SCharles.Forsyth raise:
461*74a4d8c2SCharles.Forsyth 	netlog(f, Logcompress, "Bad Packet!\n");
462*74a4d8c2SCharles.Forsyth 	comp->err = 1;
463*74a4d8c2SCharles.Forsyth 	freeblist(b);
464*74a4d8c2SCharles.Forsyth 	return nil;
465*74a4d8c2SCharles.Forsyth }
466*74a4d8c2SCharles.Forsyth 
467*74a4d8c2SCharles.Forsyth Tcpc*
compress_init(Tcpc * c)468*74a4d8c2SCharles.Forsyth compress_init(Tcpc *c)
469*74a4d8c2SCharles.Forsyth {
470*74a4d8c2SCharles.Forsyth 	int i;
471*74a4d8c2SCharles.Forsyth 	Hdr *h;
472*74a4d8c2SCharles.Forsyth 
473*74a4d8c2SCharles.Forsyth 	if(c == nil){
474*74a4d8c2SCharles.Forsyth 		c = malloc(sizeof(Tcpc));
475*74a4d8c2SCharles.Forsyth 		if(c == nil)
476*74a4d8c2SCharles.Forsyth 			return nil;
477*74a4d8c2SCharles.Forsyth 	}
478*74a4d8c2SCharles.Forsyth 	memset(c, 0, sizeof(*c));
479*74a4d8c2SCharles.Forsyth 	for(i = 0; i < MAX_STATES; i++){
480*74a4d8c2SCharles.Forsyth 		h = &c->t[i];
481*74a4d8c2SCharles.Forsyth 		h->ip = (Iphdr*)h->buf;
482*74a4d8c2SCharles.Forsyth 		h->tcp = (Tcphdr*)(h->buf + 10);
483*74a4d8c2SCharles.Forsyth 		h->len = 20;
484*74a4d8c2SCharles.Forsyth 		h = &c->r[i];
485*74a4d8c2SCharles.Forsyth 		h->ip = (Iphdr*)h->buf;
486*74a4d8c2SCharles.Forsyth 		h->tcp = (Tcphdr*)(h->buf + 10);
487*74a4d8c2SCharles.Forsyth 		h->len = 20;
488*74a4d8c2SCharles.Forsyth 	}
489*74a4d8c2SCharles.Forsyth 
490*74a4d8c2SCharles.Forsyth 	return c;
491*74a4d8c2SCharles.Forsyth }
492*74a4d8c2SCharles.Forsyth 
493*74a4d8c2SCharles.Forsyth ushort
compress(Tcpc * tcp,Block * b,Fs * f)494*74a4d8c2SCharles.Forsyth compress(Tcpc *tcp, Block *b, Fs *f)
495*74a4d8c2SCharles.Forsyth {
496*74a4d8c2SCharles.Forsyth 	Iphdr		*ip;
497*74a4d8c2SCharles.Forsyth 
498*74a4d8c2SCharles.Forsyth 	/*
499*74a4d8c2SCharles.Forsyth 	 * Bail if this is not a compressible IP packet
500*74a4d8c2SCharles.Forsyth 	 */
501*74a4d8c2SCharles.Forsyth 	ip = (Iphdr*)b->rp;
502*74a4d8c2SCharles.Forsyth 	if((nhgets(ip->frag) & 0x3fff) != 0)
503*74a4d8c2SCharles.Forsyth 		return Pip;
504*74a4d8c2SCharles.Forsyth 
505*74a4d8c2SCharles.Forsyth 	switch(ip->proto) {
506*74a4d8c2SCharles.Forsyth 	case IP_TCPPROTO:
507*74a4d8c2SCharles.Forsyth 		return tcpcompress(tcp, b, f);
508*74a4d8c2SCharles.Forsyth 	default:
509*74a4d8c2SCharles.Forsyth 		return Pip;
510*74a4d8c2SCharles.Forsyth 	}
511*74a4d8c2SCharles.Forsyth }
512*74a4d8c2SCharles.Forsyth 
513*74a4d8c2SCharles.Forsyth int
compress_negotiate(Tcpc * tcp,uchar * data)514*74a4d8c2SCharles.Forsyth compress_negotiate(Tcpc *tcp, uchar *data)
515*74a4d8c2SCharles.Forsyth {
516*74a4d8c2SCharles.Forsyth 	if(data[0] != MAX_STATES - 1)
517*74a4d8c2SCharles.Forsyth 		return -1;
518*74a4d8c2SCharles.Forsyth 	tcp->compressid = data[1];
519*74a4d8c2SCharles.Forsyth 	return 0;
520*74a4d8c2SCharles.Forsyth }
521