slcompress.c revision 1.23
1/*	$NetBSD: slcompress.c,v 1.23 2001/11/12 23:49:49 lukem Exp $   */
2/*	Id: slcompress.c,v 1.3 1996/05/24 07:04:47 paulus Exp 	*/
3
4/*
5 * Copyright (c) 1989, 1993, 1994
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	@(#)slcompress.c	8.2 (Berkeley) 4/16/94
37 */
38
39/*
40 * Routines to compress and uncompess tcp packets (for transmission
41 * over low speed serial lines.
42 *
43 * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
44 *	- Initial distribution.
45 */
46
47#include <sys/cdefs.h>
48__KERNEL_RCSID(0, "$NetBSD: slcompress.c,v 1.23 2001/11/12 23:49:49 lukem Exp $");
49
50#include <sys/param.h>
51#include <sys/mbuf.h>
52#include <sys/systm.h>
53
54#include <netinet/in.h>
55#include <netinet/in_systm.h>
56#include <netinet/ip.h>
57#include <netinet/tcp.h>
58
59#include <net/slcompress.h>
60
61#ifndef SL_NO_STATS
62#define INCR(counter) ++comp->counter;
63#else
64#define INCR(counter)
65#endif
66
67#define BCMP(p1, p2, n) bcmp((char *)(p1), (char *)(p2), (int)(n))
68#define BCOPY(p1, p2, n) bcopy((char *)(p1), (char *)(p2), (int)(n))
69
70
71void
72sl_compress_init(comp)
73	struct slcompress *comp;
74{
75	u_int i;
76	struct cstate *tstate = comp->tstate;
77
78	memset((char *)comp, 0, sizeof(*comp));
79	for (i = MAX_STATES - 1; i > 0; --i) {
80		tstate[i].cs_id = i;
81		tstate[i].cs_next = &tstate[i - 1];
82	}
83	tstate[0].cs_next = &tstate[MAX_STATES - 1];
84	tstate[0].cs_id = 0;
85	comp->last_cs = &tstate[0];
86	comp->last_recv = 255;
87	comp->last_xmit = 255;
88	comp->flags = SLF_TOSS;
89}
90
91
92/*
93 * Like sl_compress_init, but we get to specify the maximum connection
94 * ID to use on transmission.
95 */
96void
97sl_compress_setup(comp, max_state)
98 	struct slcompress *comp;
99 	int max_state;
100{
101	u_int i;
102	struct cstate *tstate = comp->tstate;
103
104	if (max_state == -1) {
105		max_state = MAX_STATES - 1;
106		memset((char *)comp, 0, sizeof(*comp));
107	} else {
108		/* Don't reset statistics */
109		memset((char *)comp->tstate, 0, sizeof(comp->tstate));
110		memset((char *)comp->rstate, 0, sizeof(comp->rstate));
111	}
112	for (i = max_state; i > 0; --i) {
113		tstate[i].cs_id = i;
114		tstate[i].cs_next = &tstate[i - 1];
115	}
116	tstate[0].cs_next = &tstate[max_state];
117	tstate[0].cs_id = 0;
118	comp->last_cs = &tstate[0];
119	comp->last_recv = 255;
120	comp->last_xmit = 255;
121	comp->flags = SLF_TOSS;
122}
123
124
125/* ENCODE encodes a number that is known to be non-zero.  ENCODEZ
126 * checks for zero (since zero has to be encoded in the long, 3 byte
127 * form).
128 */
129#define ENCODE(n) { \
130	if ((u_int16_t)(n) >= 256) { \
131		*cp++ = 0; \
132		cp[1] = (n); \
133		cp[0] = (n) >> 8; \
134		cp += 2; \
135	} else { \
136		*cp++ = (n); \
137	} \
138}
139#define ENCODEZ(n) { \
140	if ((u_int16_t)(n) >= 256 || (u_int16_t)(n) == 0) { \
141		*cp++ = 0; \
142		cp[1] = (n); \
143		cp[0] = (n) >> 8; \
144		cp += 2; \
145	} else { \
146		*cp++ = (n); \
147	} \
148}
149
150#define DECODEL(f) { \
151	if (*cp == 0) {\
152		(f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \
153		cp += 3; \
154	} else { \
155		(f) = htonl(ntohl(f) + (u_int32_t)*cp++); \
156	} \
157}
158
159#define DECODES(f) { \
160	if (*cp == 0) {\
161		(f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \
162		cp += 3; \
163	} else { \
164		(f) = htons(ntohs(f) + (u_int32_t)*cp++); \
165	} \
166}
167
168#define DECODEU(f) { \
169	if (*cp == 0) {\
170		(f) = htons((cp[1] << 8) | cp[2]); \
171		cp += 3; \
172	} else { \
173		(f) = htons((u_int32_t)*cp++); \
174	} \
175}
176
177u_int
178sl_compress_tcp(m, ip, comp, compress_cid)
179	struct mbuf *m;
180	struct ip *ip;
181	struct slcompress *comp;
182	int compress_cid;
183{
184	struct cstate *cs = comp->last_cs->cs_next;
185	u_int hlen = ip->ip_hl;
186	struct tcphdr *oth;
187	struct tcphdr *th;
188	u_int deltaS, deltaA;
189	u_int changes = 0;
190	u_char new_seq[16];
191	u_char *cp = new_seq;
192
193	/*
194	 * Bail if this is an IP fragment or if the TCP packet isn't
195	 * `compressible' (i.e., ACK isn't set or some other control bit is
196	 * set).  (We assume that the caller has already made sure the
197	 * packet is IP proto TCP).
198	 */
199	if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40)
200		return (TYPE_IP);
201
202	th = (struct tcphdr *)&((int32_t *)ip)[hlen];
203	if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK)
204		return (TYPE_IP);
205	/*
206	 * Packet is compressible -- we're going to send either a
207	 * COMPRESSED_TCP or UNCOMPRESSED_TCP packet.  Either way we need
208	 * to locate (or create) the connection state.  Special case the
209	 * most recently used connection since it's most likely to be used
210	 * again & we don't have to do any reordering if it's used.
211	 */
212	INCR(sls_packets)
213	if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
214	    ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr ||
215	    *(int32_t *)th != ((int32_t *)&cs->cs_ip)[cs->cs_ip.ip_hl]) {
216		/*
217		 * Wasn't the first -- search for it.
218		 *
219		 * States are kept in a circularly linked list with
220		 * last_cs pointing to the end of the list.  The
221		 * list is kept in lru order by moving a state to the
222		 * head of the list whenever it is referenced.  Since
223		 * the list is short and, empirically, the connection
224		 * we want is almost always near the front, we locate
225		 * states via linear search.  If we don't find a state
226		 * for the datagram, the oldest state is (re-)used.
227		 */
228		struct cstate *lcs;
229		struct cstate *lastcs = comp->last_cs;
230
231		do {
232			lcs = cs; cs = cs->cs_next;
233			INCR(sls_searches)
234			if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
235			    && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr
236			    && *(int32_t *)th ==
237			    ((int32_t *)&cs->cs_ip)[cs->cs_ip.ip_hl])
238				goto found;
239		} while (cs != lastcs);
240
241		/*
242		 * Didn't find it -- re-use oldest cstate.  Send an
243		 * uncompressed packet that tells the other side what
244		 * connection number we're using for this conversation.
245		 * Note that since the state list is circular, the oldest
246		 * state points to the newest and we only need to set
247		 * last_cs to update the lru linkage.
248		 */
249		INCR(sls_misses)
250		comp->last_cs = lcs;
251		hlen += th->th_off;
252		hlen <<= 2;
253		if (hlen > m->m_len)
254			return (TYPE_IP);
255		goto uncompressed;
256
257	found:
258		/*
259		 * Found it -- move to the front on the connection list.
260		 */
261		if (cs == lastcs)
262			comp->last_cs = lcs;
263		else {
264			lcs->cs_next = cs->cs_next;
265			cs->cs_next = lastcs->cs_next;
266			lastcs->cs_next = cs;
267		}
268	}
269
270	/*
271	 * Make sure that only what we expect to change changed. The first
272	 * line of the `if' checks the IP protocol version, header length &
273	 * type of service.  The 2nd line checks the "Don't fragment" bit.
274	 * The 3rd line checks the time-to-live and protocol (the protocol
275	 * check is unnecessary but costless).  The 4th line checks the TCP
276	 * header length.  The 5th line checks IP options, if any.  The 6th
277	 * line checks TCP options, if any.  If any of these things are
278	 * different between the previous & current datagram, we send the
279	 * current datagram `uncompressed'.
280	 */
281	oth = (struct tcphdr *)&((int32_t *)&cs->cs_ip)[hlen];
282	deltaS = hlen;
283	hlen += th->th_off;
284	hlen <<= 2;
285	if (hlen > m->m_len)
286		return (TYPE_IP);
287
288	if (((u_int16_t *)ip)[0] != ((u_int16_t *)&cs->cs_ip)[0] ||
289	    ((u_int16_t *)ip)[3] != ((u_int16_t *)&cs->cs_ip)[3] ||
290	    ((u_int16_t *)ip)[4] != ((u_int16_t *)&cs->cs_ip)[4] ||
291	    th->th_off != oth->th_off ||
292	    (deltaS > 5 &&
293	     BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
294	    (th->th_off > 5 &&
295	     BCMP(th + 1, oth + 1, (th->th_off - 5) << 2)))
296		goto uncompressed;
297
298	/*
299	 * Figure out which of the changing fields changed.  The
300	 * receiver expects changes in the order: urgent, window,
301	 * ack, seq (the order minimizes the number of temporaries
302	 * needed in this section of code).
303	 */
304	if (th->th_flags & TH_URG) {
305		deltaS = ntohs(th->th_urp);
306		ENCODEZ(deltaS);
307		changes |= NEW_U;
308	} else if (th->th_urp != oth->th_urp)
309		/* argh! URG not set but urp changed -- a sensible
310		 * implementation should never do this but RFC793
311		 * doesn't prohibit the change so we have to deal
312		 * with it. */
313		 goto uncompressed;
314
315	deltaS = (u_int16_t)(ntohs(th->th_win) - ntohs(oth->th_win));
316	if (deltaS) {
317		ENCODE(deltaS);
318		changes |= NEW_W;
319	}
320
321	deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack);
322	if (deltaA) {
323		if (deltaA > 0xffff)
324			goto uncompressed;
325		ENCODE(deltaA);
326		changes |= NEW_A;
327	}
328
329	deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq);
330	if (deltaS) {
331		if (deltaS > 0xffff)
332			goto uncompressed;
333		ENCODE(deltaS);
334		changes |= NEW_S;
335	}
336
337	switch(changes) {
338
339	case 0:
340		/*
341		 * Nothing changed. If this packet contains data and the
342		 * last one didn't, this is probably a data packet following
343		 * an ack (normal on an interactive connection) and we send
344		 * it compressed.  Otherwise it's probably a retransmit,
345		 * retransmitted ack or window probe.  Send it uncompressed
346		 * in case the other side missed the compressed version.
347		 */
348		if (ip->ip_len != cs->cs_ip.ip_len &&
349		    ntohs(cs->cs_ip.ip_len) == hlen)
350			break;
351
352		/* (fall through) */
353
354	case SPECIAL_I:
355	case SPECIAL_D:
356		/*
357		 * actual changes match one of our special case encodings --
358		 * send packet uncompressed.
359		 */
360		goto uncompressed;
361
362	case NEW_S|NEW_A:
363		if (deltaS == deltaA &&
364		    deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
365			/* special case for echoed terminal traffic */
366			changes = SPECIAL_I;
367			cp = new_seq;
368		}
369		break;
370
371	case NEW_S:
372		if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
373			/* special case for data xfer */
374			changes = SPECIAL_D;
375			cp = new_seq;
376		}
377		break;
378	}
379
380	deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
381	if (deltaS != 1) {
382		ENCODEZ(deltaS);
383		changes |= NEW_I;
384	}
385	if (th->th_flags & TH_PUSH)
386		changes |= TCP_PUSH_BIT;
387	/*
388	 * Grab the cksum before we overwrite it below.  Then update our
389	 * state with this packet's header.
390	 */
391	deltaA = ntohs(th->th_sum);
392	BCOPY(ip, &cs->cs_ip, hlen);
393
394	/*
395	 * We want to use the original packet as our compressed packet.
396	 * (cp - new_seq) is the number of bytes we need for compressed
397	 * sequence numbers.  In addition we need one byte for the change
398	 * mask, one for the connection id and two for the tcp checksum.
399	 * So, (cp - new_seq) + 4 bytes of header are needed.  hlen is how
400	 * many bytes of the original packet to toss so subtract the two to
401	 * get the new packet size.
402	 */
403	deltaS = cp - new_seq;
404	cp = (u_char *)ip;
405	if (compress_cid == 0 || comp->last_xmit != cs->cs_id) {
406		comp->last_xmit = cs->cs_id;
407		hlen -= deltaS + 4;
408		cp += hlen;
409		*cp++ = changes | NEW_C;
410		*cp++ = cs->cs_id;
411	} else {
412		hlen -= deltaS + 3;
413		cp += hlen;
414		*cp++ = changes;
415	}
416	m->m_len -= hlen;
417	m->m_data += hlen;
418	*cp++ = deltaA >> 8;
419	*cp++ = deltaA;
420	BCOPY(new_seq, cp, deltaS);
421	INCR(sls_compressed)
422	return (TYPE_COMPRESSED_TCP);
423
424	/*
425	 * Update connection state cs & send uncompressed packet ('uncompressed'
426	 * means a regular ip/tcp packet but with the 'conversation id' we hope
427	 * to use on future compressed packets in the protocol field).
428	 */
429uncompressed:
430	BCOPY(ip, &cs->cs_ip, hlen);
431	ip->ip_p = cs->cs_id;
432	comp->last_xmit = cs->cs_id;
433	return (TYPE_UNCOMPRESSED_TCP);
434}
435
436
437int
438sl_uncompress_tcp(bufp, len, type, comp)
439	u_char **bufp;
440	int len;
441	u_int type;
442	struct slcompress *comp;
443{
444	u_char *hdr, *cp;
445	int vjlen;
446	u_int hlen;
447
448	cp = bufp? *bufp: NULL;
449	vjlen = sl_uncompress_tcp_core(cp, len, len, type, comp, &hdr, &hlen);
450	if (vjlen < 0)
451		return (0);	/* error */
452	if (vjlen == 0)
453		return (len);	/* was uncompressed already */
454
455	cp += vjlen;
456	len -= vjlen;
457
458	/*
459	 * At this point, cp points to the first byte of data in the
460	 * packet.  If we're not aligned on a 4-byte boundary, copy the
461	 * data down so the ip & tcp headers will be aligned.  Then back up
462	 * cp by the tcp/ip header length to make room for the reconstructed
463	 * header (we assume the packet we were handed has enough space to
464	 * prepend 128 bytes of header).
465	 */
466	if ((long)cp & 3) {
467		if (len > 0)
468			memmove((caddr_t)((long)cp &~ 3), cp, len);
469		cp = (u_char *)((long)cp &~ 3);
470	}
471	cp -= hlen;
472	len += hlen;
473	BCOPY(hdr, cp, hlen);
474
475	*bufp = cp;
476	return (len);
477}
478
479/*
480 * Uncompress a packet of total length total_len.  The first buflen
481 * bytes are at buf; this must include the entire (compressed or
482 * uncompressed) TCP/IP header.  This procedure returns the length
483 * of the VJ header, with a pointer to the uncompressed IP header
484 * in *hdrp and its length in *hlenp.
485 */
486int
487sl_uncompress_tcp_core(buf, buflen, total_len, type, comp, hdrp, hlenp)
488	u_char *buf;
489	int buflen, total_len;
490	u_int type;
491	struct slcompress *comp;
492	u_char **hdrp;
493	u_int *hlenp;
494{
495	u_char *cp;
496	u_int hlen, changes;
497	struct tcphdr *th;
498	struct cstate *cs;
499	struct ip *ip;
500	u_int16_t *bp;
501	u_int vjlen;
502
503	switch (type) {
504
505	case TYPE_UNCOMPRESSED_TCP:
506		ip = (struct ip *) buf;
507		if (ip->ip_p >= MAX_STATES)
508			goto bad;
509		cs = &comp->rstate[comp->last_recv = ip->ip_p];
510		comp->flags &=~ SLF_TOSS;
511		ip->ip_p = IPPROTO_TCP;
512		/*
513		 * Calculate the size of the TCP/IP header and make sure that
514		 * we don't overflow the space we have available for it.
515		 */
516		hlen = ip->ip_hl << 2;
517		if (hlen + sizeof(struct tcphdr) > buflen)
518			goto bad;
519		hlen += ((struct tcphdr *)&((char *)ip)[hlen])->th_off << 2;
520		if (hlen > MAX_HDR || hlen > buflen)
521			goto bad;
522		BCOPY(ip, &cs->cs_ip, hlen);
523		cs->cs_hlen = hlen;
524		INCR(sls_uncompressedin)
525		*hdrp = (u_char *) &cs->cs_ip;
526		*hlenp = hlen;
527		return (0);
528
529	default:
530		goto bad;
531
532	case TYPE_COMPRESSED_TCP:
533		break;
534	}
535	/* We've got a compressed packet. */
536	INCR(sls_compressedin)
537	cp = buf;
538	changes = *cp++;
539	if (changes & NEW_C) {
540		/* Make sure the state index is in range, then grab the state.
541		 * If we have a good state index, clear the 'discard' flag. */
542		if (*cp >= MAX_STATES)
543			goto bad;
544
545		comp->flags &=~ SLF_TOSS;
546		comp->last_recv = *cp++;
547	} else {
548		/* this packet has an implicit state index.  If we've
549		 * had a line error since the last time we got an
550		 * explicit state index, we have to toss the packet. */
551		if (comp->flags & SLF_TOSS) {
552			INCR(sls_tossed)
553			return (-1);
554		}
555	}
556	cs = &comp->rstate[comp->last_recv];
557	hlen = cs->cs_ip.ip_hl << 2;
558	th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen];
559	th->th_sum = htons((*cp << 8) | cp[1]);
560	cp += 2;
561	if (changes & TCP_PUSH_BIT)
562		th->th_flags |= TH_PUSH;
563	else
564		th->th_flags &=~ TH_PUSH;
565
566	switch (changes & SPECIALS_MASK) {
567	case SPECIAL_I:
568		{
569		u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
570		th->th_ack = htonl(ntohl(th->th_ack) + i);
571		th->th_seq = htonl(ntohl(th->th_seq) + i);
572		}
573		break;
574
575	case SPECIAL_D:
576		th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
577				   - cs->cs_hlen);
578		break;
579
580	default:
581		if (changes & NEW_U) {
582			th->th_flags |= TH_URG;
583			DECODEU(th->th_urp)
584		} else
585			th->th_flags &=~ TH_URG;
586		if (changes & NEW_W)
587			DECODES(th->th_win)
588		if (changes & NEW_A)
589			DECODEL(th->th_ack)
590		if (changes & NEW_S)
591			DECODEL(th->th_seq)
592		break;
593	}
594	if (changes & NEW_I) {
595		DECODES(cs->cs_ip.ip_id)
596	} else
597		cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
598
599	/*
600	 * At this point, cp points to the first byte of data in the
601	 * packet.  Fill in the IP total length and update the IP
602	 * header checksum.
603	 */
604	vjlen = cp - buf;
605	buflen -= vjlen;
606	if (buflen < 0)
607		/* we must have dropped some characters (crc should detect
608		 * this but the old slip framing won't) */
609		goto bad;
610
611	total_len += cs->cs_hlen - vjlen;
612	cs->cs_ip.ip_len = htons(total_len);
613
614	/* recompute the ip header checksum */
615	bp = (u_int16_t *) &cs->cs_ip;
616	cs->cs_ip.ip_sum = 0;
617	for (changes = 0; hlen > 0; hlen -= 2)
618		changes += *bp++;
619	changes = (changes & 0xffff) + (changes >> 16);
620	changes = (changes & 0xffff) + (changes >> 16);
621	cs->cs_ip.ip_sum = ~ changes;
622
623	*hdrp = (u_char *) &cs->cs_ip;
624	*hlenp = cs->cs_hlen;
625	return vjlen;
626
627bad:
628	comp->flags |= SLF_TOSS;
629	INCR(sls_errorin)
630	return (-1);
631}
632