11541Srgrimes/*-
21541Srgrimes * Copyright (c) 1989, 1993, 1994
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * Redistribution and use in source and binary forms, with or without
61541Srgrimes * modification, are permitted provided that the following conditions
71541Srgrimes * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
91541Srgrimes *    notice, this list of conditions and the following disclaimer.
101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111541Srgrimes *    notice, this list of conditions and the following disclaimer in the
121541Srgrimes *    documentation and/or other materials provided with the distribution.
131541Srgrimes * 4. Neither the name of the University nor the names of its contributors
141541Srgrimes *    may be used to endorse or promote products derived from this software
151541Srgrimes *    without specific prior written permission.
161541Srgrimes *
171541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271541Srgrimes * SUCH DAMAGE.
281541Srgrimes *
291541Srgrimes *	@(#)slcompress.c	8.2 (Berkeley) 4/16/94
3050477Speter * $FreeBSD$
311541Srgrimes */
321541Srgrimes
331541Srgrimes/*
341541Srgrimes * Routines to compress and uncompess tcp packets (for transmission
351541Srgrimes * over low speed serial lines.
361541Srgrimes *
371541Srgrimes * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
381541Srgrimes *	- Initial distribution.
391541Srgrimes *
401541Srgrimes */
411541Srgrimes
421541Srgrimes#include <sys/param.h>
4328415Speter#include <sys/mbuf.h>
442112Swollman#include <sys/systm.h>
451541Srgrimes
461541Srgrimes#include <netinet/in.h>
471541Srgrimes#include <netinet/in_systm.h>
481541Srgrimes#include <netinet/ip.h>
491541Srgrimes#include <netinet/tcp.h>
501541Srgrimes
511541Srgrimes#include <net/slcompress.h>
521541Srgrimes
531541Srgrimes#ifndef SL_NO_STATS
541541Srgrimes#define INCR(counter) ++comp->counter;
551541Srgrimes#else
561541Srgrimes#define INCR(counter)
571541Srgrimes#endif
581541Srgrimes
59113072Sdes#define BCMP(p1, p2, n) bcmp((void *)(p1), (void *)(p2), (int)(n))
60113072Sdes#define BCOPY(p1, p2, n) bcopy((void *)(p1), (void *)(p2), (int)(n))
611541Srgrimes
621541Srgrimesvoid
6311964Spetersl_compress_init(comp, max_state)
641541Srgrimes	struct slcompress *comp;
6511964Speter	int max_state;
661541Srgrimes{
671541Srgrimes	register u_int i;
681541Srgrimes	register struct cstate *tstate = comp->tstate;
691541Srgrimes
7028415Speter	if (max_state == -1) {
7111964Speter		max_state = MAX_STATES - 1;
7228415Speter		bzero((char *)comp, sizeof(*comp));
7328415Speter	} else {
7428415Speter		/* Don't reset statistics */
7528415Speter		bzero((char *)comp->tstate, sizeof(comp->tstate));
7628415Speter		bzero((char *)comp->rstate, sizeof(comp->rstate));
7728415Speter	}
7828415Speter  	for (i = max_state; i > 0; --i) {
791541Srgrimes		tstate[i].cs_id = i;
801541Srgrimes		tstate[i].cs_next = &tstate[i - 1];
811541Srgrimes	}
8211964Speter	tstate[0].cs_next = &tstate[max_state];
831541Srgrimes	tstate[0].cs_id = 0;
841541Srgrimes	comp->last_cs = &tstate[0];
851541Srgrimes	comp->last_recv = 255;
861541Srgrimes	comp->last_xmit = 255;
871541Srgrimes	comp->flags = SLF_TOSS;
881541Srgrimes}
891541Srgrimes
901541Srgrimes
911541Srgrimes/* ENCODE encodes a number that is known to be non-zero.  ENCODEZ
921541Srgrimes * checks for zero (since zero has to be encoded in the long, 3 byte
931541Srgrimes * form).
941541Srgrimes */
951541Srgrimes#define ENCODE(n) { \
9628415Speter	if ((u_int16_t)(n) >= 256) { \
971541Srgrimes		*cp++ = 0; \
981541Srgrimes		cp[1] = (n); \
991541Srgrimes		cp[0] = (n) >> 8; \
1001541Srgrimes		cp += 2; \
1011541Srgrimes	} else { \
1021541Srgrimes		*cp++ = (n); \
1031541Srgrimes	} \
1041541Srgrimes}
1051541Srgrimes#define ENCODEZ(n) { \
10628415Speter	if ((u_int16_t)(n) >= 256 || (u_int16_t)(n) == 0) { \
1071541Srgrimes		*cp++ = 0; \
1081541Srgrimes		cp[1] = (n); \
1091541Srgrimes		cp[0] = (n) >> 8; \
1101541Srgrimes		cp += 2; \
1111541Srgrimes	} else { \
1121541Srgrimes		*cp++ = (n); \
1131541Srgrimes	} \
1141541Srgrimes}
1151541Srgrimes
1161541Srgrimes#define DECODEL(f) { \
1171541Srgrimes	if (*cp == 0) {\
1181541Srgrimes		(f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \
1191541Srgrimes		cp += 3; \
1201541Srgrimes	} else { \
12128415Speter		(f) = htonl(ntohl(f) + (u_int32_t)*cp++); \
1221541Srgrimes	} \
1231541Srgrimes}
1241541Srgrimes
1251541Srgrimes#define DECODES(f) { \
1261541Srgrimes	if (*cp == 0) {\
1271541Srgrimes		(f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \
1281541Srgrimes		cp += 3; \
1291541Srgrimes	} else { \
13028415Speter		(f) = htons(ntohs(f) + (u_int32_t)*cp++); \
1311541Srgrimes	} \
1321541Srgrimes}
1331541Srgrimes
1341541Srgrimes#define DECODEU(f) { \
1351541Srgrimes	if (*cp == 0) {\
1361541Srgrimes		(f) = htons((cp[1] << 8) | cp[2]); \
1371541Srgrimes		cp += 3; \
1381541Srgrimes	} else { \
13928415Speter		(f) = htons((u_int32_t)*cp++); \
1401541Srgrimes	} \
1411541Srgrimes}
1421541Srgrimes
14352631Sarchie/*
14453192Sarchie * Attempt to compress an outgoing TCP packet and return the type of
14553192Sarchie * the result.  The caller must have already verified that the protocol
14653192Sarchie * is TCP.  The first mbuf must contain the complete IP and TCP headers,
14753192Sarchie * and "ip" must be == mtod(m, struct ip *).  "comp" supplies the
14853192Sarchie * compression state, and "compress_cid" tells us whether it is OK
14953192Sarchie * to leave out the CID field when feasible.
15053192Sarchie *
15153192Sarchie * The caller is responsible for adjusting m->m_pkthdr.len upon return,
15253192Sarchie * if m is an M_PKTHDR mbuf.
15352631Sarchie */
1541541Srgrimesu_int
1551541Srgrimessl_compress_tcp(m, ip, comp, compress_cid)
1561541Srgrimes	struct mbuf *m;
1571541Srgrimes	register struct ip *ip;
1581541Srgrimes	struct slcompress *comp;
1591541Srgrimes	int compress_cid;
1601541Srgrimes{
1611541Srgrimes	register struct cstate *cs = comp->last_cs->cs_next;
1621541Srgrimes	register u_int hlen = ip->ip_hl;
1631541Srgrimes	register struct tcphdr *oth;
1641541Srgrimes	register struct tcphdr *th;
1651541Srgrimes	register u_int deltaS, deltaA;
1661541Srgrimes	register u_int changes = 0;
1671541Srgrimes	u_char new_seq[16];
1681541Srgrimes	register u_char *cp = new_seq;
1691541Srgrimes
1701541Srgrimes	/*
1711541Srgrimes	 * Bail if this is an IP fragment or if the TCP packet isn't
1721541Srgrimes	 * `compressible' (i.e., ACK isn't set or some other control bit is
1731541Srgrimes	 * set).  (We assume that the caller has already made sure the
1741541Srgrimes	 * packet is IP proto TCP).
1751541Srgrimes	 */
1761541Srgrimes	if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40)
1771541Srgrimes		return (TYPE_IP);
1781541Srgrimes
17928415Speter	th = (struct tcphdr *)&((int32_t *)ip)[hlen];
1801541Srgrimes	if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK)
1811541Srgrimes		return (TYPE_IP);
1821541Srgrimes	/*
1831541Srgrimes	 * Packet is compressible -- we're going to send either a
1841541Srgrimes	 * COMPRESSED_TCP or UNCOMPRESSED_TCP packet.  Either way we need
1851541Srgrimes	 * to locate (or create) the connection state.  Special case the
1861541Srgrimes	 * most recently used connection since it's most likely to be used
1871541Srgrimes	 * again & we don't have to do any reordering if it's used.
1881541Srgrimes	 */
1891541Srgrimes	INCR(sls_packets)
1901541Srgrimes	if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
1911541Srgrimes	    ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr ||
19228415Speter	    *(int32_t *)th != ((int32_t *)&cs->cs_ip)[cs->cs_ip.ip_hl]) {
1931541Srgrimes		/*
1941541Srgrimes		 * Wasn't the first -- search for it.
1951541Srgrimes		 *
1961541Srgrimes		 * States are kept in a circularly linked list with
1971541Srgrimes		 * last_cs pointing to the end of the list.  The
1981541Srgrimes		 * list is kept in lru order by moving a state to the
1991541Srgrimes		 * head of the list whenever it is referenced.  Since
2001541Srgrimes		 * the list is short and, empirically, the connection
2011541Srgrimes		 * we want is almost always near the front, we locate
2021541Srgrimes		 * states via linear search.  If we don't find a state
2031541Srgrimes		 * for the datagram, the oldest state is (re-)used.
2041541Srgrimes		 */
2051541Srgrimes		register struct cstate *lcs;
2061541Srgrimes		register struct cstate *lastcs = comp->last_cs;
2071541Srgrimes
2081541Srgrimes		do {
2091541Srgrimes			lcs = cs; cs = cs->cs_next;
2101541Srgrimes			INCR(sls_searches)
2111541Srgrimes			if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
2121541Srgrimes			    && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr
21328415Speter			    && *(int32_t *)th ==
21428415Speter			    ((int32_t *)&cs->cs_ip)[cs->cs_ip.ip_hl])
2151541Srgrimes				goto found;
2161541Srgrimes		} while (cs != lastcs);
2171541Srgrimes
2181541Srgrimes		/*
2191541Srgrimes		 * Didn't find it -- re-use oldest cstate.  Send an
2201541Srgrimes		 * uncompressed packet that tells the other side what
2211541Srgrimes		 * connection number we're using for this conversation.
2221541Srgrimes		 * Note that since the state list is circular, the oldest
2231541Srgrimes		 * state points to the newest and we only need to set
2241541Srgrimes		 * last_cs to update the lru linkage.
2251541Srgrimes		 */
2261541Srgrimes		INCR(sls_misses)
2271541Srgrimes		comp->last_cs = lcs;
2281541Srgrimes		hlen += th->th_off;
2291541Srgrimes		hlen <<= 2;
23026778Sbrian		if (hlen > m->m_len)
23126778Sbrian		    return TYPE_IP;
2321541Srgrimes		goto uncompressed;
2331541Srgrimes
2341541Srgrimes	found:
2351541Srgrimes		/*
2361541Srgrimes		 * Found it -- move to the front on the connection list.
2371541Srgrimes		 */
2381541Srgrimes		if (cs == lastcs)
2391541Srgrimes			comp->last_cs = lcs;
2401541Srgrimes		else {
2411541Srgrimes			lcs->cs_next = cs->cs_next;
2421541Srgrimes			cs->cs_next = lastcs->cs_next;
2431541Srgrimes			lastcs->cs_next = cs;
2441541Srgrimes		}
2451541Srgrimes	}
2461541Srgrimes
2471541Srgrimes	/*
2481541Srgrimes	 * Make sure that only what we expect to change changed. The first
2491541Srgrimes	 * line of the `if' checks the IP protocol version, header length &
2501541Srgrimes	 * type of service.  The 2nd line checks the "Don't fragment" bit.
2511541Srgrimes	 * The 3rd line checks the time-to-live and protocol (the protocol
2521541Srgrimes	 * check is unnecessary but costless).  The 4th line checks the TCP
2531541Srgrimes	 * header length.  The 5th line checks IP options, if any.  The 6th
2541541Srgrimes	 * line checks TCP options, if any.  If any of these things are
2551541Srgrimes	 * different between the previous & current datagram, we send the
2561541Srgrimes	 * current datagram `uncompressed'.
2571541Srgrimes	 */
25828415Speter	oth = (struct tcphdr *)&((int32_t *)&cs->cs_ip)[hlen];
2591541Srgrimes	deltaS = hlen;
2601541Srgrimes	hlen += th->th_off;
2611541Srgrimes	hlen <<= 2;
26226778Sbrian	if (hlen > m->m_len)
26326778Sbrian	    return TYPE_IP;
2641541Srgrimes
26528415Speter	if (((u_int16_t *)ip)[0] != ((u_int16_t *)&cs->cs_ip)[0] ||
26628415Speter	    ((u_int16_t *)ip)[3] != ((u_int16_t *)&cs->cs_ip)[3] ||
26728415Speter	    ((u_int16_t *)ip)[4] != ((u_int16_t *)&cs->cs_ip)[4] ||
2681541Srgrimes	    th->th_off != oth->th_off ||
2691541Srgrimes	    (deltaS > 5 &&
2701541Srgrimes	     BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
2711541Srgrimes	    (th->th_off > 5 &&
2721541Srgrimes	     BCMP(th + 1, oth + 1, (th->th_off - 5) << 2)))
2731541Srgrimes		goto uncompressed;
2741541Srgrimes
2751541Srgrimes	/*
2761541Srgrimes	 * Figure out which of the changing fields changed.  The
2771541Srgrimes	 * receiver expects changes in the order: urgent, window,
2781541Srgrimes	 * ack, seq (the order minimizes the number of temporaries
2791541Srgrimes	 * needed in this section of code).
2801541Srgrimes	 */
2811541Srgrimes	if (th->th_flags & TH_URG) {
2821541Srgrimes		deltaS = ntohs(th->th_urp);
2831541Srgrimes		ENCODEZ(deltaS);
2841541Srgrimes		changes |= NEW_U;
2851541Srgrimes	} else if (th->th_urp != oth->th_urp)
2861541Srgrimes		/* argh! URG not set but urp changed -- a sensible
2871541Srgrimes		 * implementation should never do this but RFC793
2881541Srgrimes		 * doesn't prohibit the change so we have to deal
2891541Srgrimes		 * with it. */
2901541Srgrimes		 goto uncompressed;
2911541Srgrimes
29228415Speter	deltaS = (u_int16_t)(ntohs(th->th_win) - ntohs(oth->th_win));
2933443Sphk	if (deltaS) {
2941541Srgrimes		ENCODE(deltaS);
2951541Srgrimes		changes |= NEW_W;
2961541Srgrimes	}
2971541Srgrimes
2983443Sphk	deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack);
2993443Sphk	if (deltaA) {
3001541Srgrimes		if (deltaA > 0xffff)
3011541Srgrimes			goto uncompressed;
3021541Srgrimes		ENCODE(deltaA);
3031541Srgrimes		changes |= NEW_A;
3041541Srgrimes	}
3051541Srgrimes
3063443Sphk	deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq);
3073443Sphk	if (deltaS) {
3081541Srgrimes		if (deltaS > 0xffff)
3091541Srgrimes			goto uncompressed;
3101541Srgrimes		ENCODE(deltaS);
3111541Srgrimes		changes |= NEW_S;
3121541Srgrimes	}
3131541Srgrimes
3141541Srgrimes	switch(changes) {
3151541Srgrimes
3161541Srgrimes	case 0:
3171541Srgrimes		/*
3181541Srgrimes		 * Nothing changed. If this packet contains data and the
3191541Srgrimes		 * last one didn't, this is probably a data packet following
3201541Srgrimes		 * an ack (normal on an interactive connection) and we send
3211541Srgrimes		 * it compressed.  Otherwise it's probably a retransmit,
3221541Srgrimes		 * retransmitted ack or window probe.  Send it uncompressed
3231541Srgrimes		 * in case the other side missed the compressed version.
3241541Srgrimes		 */
3251541Srgrimes		if (ip->ip_len != cs->cs_ip.ip_len &&
3261541Srgrimes		    ntohs(cs->cs_ip.ip_len) == hlen)
3271541Srgrimes			break;
3281541Srgrimes
329102412Scharnier		/* FALLTHROUGH */
3301541Srgrimes
3311541Srgrimes	case SPECIAL_I:
3321541Srgrimes	case SPECIAL_D:
3331541Srgrimes		/*
3341541Srgrimes		 * actual changes match one of our special case encodings --
3351541Srgrimes		 * send packet uncompressed.
3361541Srgrimes		 */
3371541Srgrimes		goto uncompressed;
3381541Srgrimes
3391541Srgrimes	case NEW_S|NEW_A:
3401541Srgrimes		if (deltaS == deltaA &&
3411541Srgrimes		    deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
3421541Srgrimes			/* special case for echoed terminal traffic */
3431541Srgrimes			changes = SPECIAL_I;
3441541Srgrimes			cp = new_seq;
3451541Srgrimes		}
3461541Srgrimes		break;
3471541Srgrimes
3481541Srgrimes	case NEW_S:
3491541Srgrimes		if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
3501541Srgrimes			/* special case for data xfer */
3511541Srgrimes			changes = SPECIAL_D;
3521541Srgrimes			cp = new_seq;
3531541Srgrimes		}
3541541Srgrimes		break;
3551541Srgrimes	}
3561541Srgrimes
3571541Srgrimes	deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
3581541Srgrimes	if (deltaS != 1) {
3591541Srgrimes		ENCODEZ(deltaS);
3601541Srgrimes		changes |= NEW_I;
3611541Srgrimes	}
3621541Srgrimes	if (th->th_flags & TH_PUSH)
3631541Srgrimes		changes |= TCP_PUSH_BIT;
3641541Srgrimes	/*
3651541Srgrimes	 * Grab the cksum before we overwrite it below.  Then update our
3661541Srgrimes	 * state with this packet's header.
3671541Srgrimes	 */
3681541Srgrimes	deltaA = ntohs(th->th_sum);
3691541Srgrimes	BCOPY(ip, &cs->cs_ip, hlen);
3701541Srgrimes
3711541Srgrimes	/*
3721541Srgrimes	 * We want to use the original packet as our compressed packet.
3731541Srgrimes	 * (cp - new_seq) is the number of bytes we need for compressed
3741541Srgrimes	 * sequence numbers.  In addition we need one byte for the change
3751541Srgrimes	 * mask, one for the connection id and two for the tcp checksum.
3761541Srgrimes	 * So, (cp - new_seq) + 4 bytes of header are needed.  hlen is how
3771541Srgrimes	 * many bytes of the original packet to toss so subtract the two to
3781541Srgrimes	 * get the new packet size.
3791541Srgrimes	 */
3801541Srgrimes	deltaS = cp - new_seq;
3811541Srgrimes	cp = (u_char *)ip;
3821541Srgrimes	if (compress_cid == 0 || comp->last_xmit != cs->cs_id) {
3831541Srgrimes		comp->last_xmit = cs->cs_id;
3841541Srgrimes		hlen -= deltaS + 4;
3851541Srgrimes		cp += hlen;
3861541Srgrimes		*cp++ = changes | NEW_C;
3871541Srgrimes		*cp++ = cs->cs_id;
3881541Srgrimes	} else {
3891541Srgrimes		hlen -= deltaS + 3;
3901541Srgrimes		cp += hlen;
3911541Srgrimes		*cp++ = changes;
3921541Srgrimes	}
3931541Srgrimes	m->m_len -= hlen;
3941541Srgrimes	m->m_data += hlen;
3951541Srgrimes	*cp++ = deltaA >> 8;
3961541Srgrimes	*cp++ = deltaA;
3971541Srgrimes	BCOPY(new_seq, cp, deltaS);
3981541Srgrimes	INCR(sls_compressed)
3991541Srgrimes	return (TYPE_COMPRESSED_TCP);
4001541Srgrimes
4011541Srgrimes	/*
4021541Srgrimes	 * Update connection state cs & send uncompressed packet ('uncompressed'
4031541Srgrimes	 * means a regular ip/tcp packet but with the 'conversation id' we hope
4041541Srgrimes	 * to use on future compressed packets in the protocol field).
4051541Srgrimes	 */
4061541Srgrimesuncompressed:
4071541Srgrimes	BCOPY(ip, &cs->cs_ip, hlen);
4081541Srgrimes	ip->ip_p = cs->cs_id;
4091541Srgrimes	comp->last_xmit = cs->cs_id;
4101541Srgrimes	return (TYPE_UNCOMPRESSED_TCP);
4111541Srgrimes}
4121541Srgrimes
4131541Srgrimes
4141541Srgrimesint
4151541Srgrimessl_uncompress_tcp(bufp, len, type, comp)
4161541Srgrimes	u_char **bufp;
4171541Srgrimes	int len;
4181541Srgrimes	u_int type;
4191541Srgrimes	struct slcompress *comp;
4201541Srgrimes{
42111964Speter	u_char *hdr, *cp;
42211964Speter	int hlen, vjlen;
42311964Speter
42411964Speter	cp = bufp? *bufp: NULL;
42511964Speter	vjlen = sl_uncompress_tcp_core(cp, len, len, type, comp, &hdr, &hlen);
42611964Speter	if (vjlen < 0)
42711964Speter		return (0);	/* error */
42811964Speter	if (vjlen == 0)
42911964Speter		return (len);	/* was uncompressed already */
43011964Speter
43111964Speter	cp += vjlen;
43211964Speter	len -= vjlen;
43311964Speter
43411964Speter	/*
43511964Speter	 * At this point, cp points to the first byte of data in the
43611964Speter	 * packet.  If we're not aligned on a 4-byte boundary, copy the
43711964Speter	 * data down so the ip & tcp headers will be aligned.  Then back up
43811964Speter	 * cp by the tcp/ip header length to make room for the reconstructed
43911964Speter	 * header (we assume the packet we were handed has enough space to
44011964Speter	 * prepend 128 bytes of header).
44111964Speter	 */
44237649Sbde	if ((intptr_t)cp & 3) {
44311964Speter		if (len > 0)
444113072Sdes			BCOPY(cp, ((intptr_t)cp &~ 3), len);
44537649Sbde		cp = (u_char *)((intptr_t)cp &~ 3);
44611964Speter	}
44711964Speter	cp -= hlen;
44811964Speter	len += hlen;
44911964Speter	BCOPY(hdr, cp, hlen);
45011964Speter
45111964Speter	*bufp = cp;
45211964Speter	return (len);
45311964Speter}
45411964Speter
45511964Speter/*
45611964Speter * Uncompress a packet of total length total_len.  The first buflen
45711964Speter * bytes are at buf; this must include the entire (compressed or
45811964Speter * uncompressed) TCP/IP header.  This procedure returns the length
45911964Speter * of the VJ header, with a pointer to the uncompressed IP header
46011964Speter * in *hdrp and its length in *hlenp.
46111964Speter */
46211964Speterint
46311964Spetersl_uncompress_tcp_core(buf, buflen, total_len, type, comp, hdrp, hlenp)
46411964Speter	u_char *buf;
46511964Speter	int buflen, total_len;
46611964Speter	u_int type;
46711964Speter	struct slcompress *comp;
46811964Speter	u_char **hdrp;
46911964Speter	u_int *hlenp;
47011964Speter{
4711541Srgrimes	register u_char *cp;
4721541Srgrimes	register u_int hlen, changes;
4731541Srgrimes	register struct tcphdr *th;
4741541Srgrimes	register struct cstate *cs;
4751541Srgrimes	register struct ip *ip;
47628415Speter	register u_int16_t *bp;
47711964Speter	register u_int vjlen;
4781541Srgrimes
4791541Srgrimes	switch (type) {
4801541Srgrimes
4811541Srgrimes	case TYPE_UNCOMPRESSED_TCP:
48211964Speter		ip = (struct ip *) buf;
4831541Srgrimes		if (ip->ip_p >= MAX_STATES)
4841541Srgrimes			goto bad;
4851541Srgrimes		cs = &comp->rstate[comp->last_recv = ip->ip_p];
4861541Srgrimes		comp->flags &=~ SLF_TOSS;
4871541Srgrimes		ip->ip_p = IPPROTO_TCP;
48815185Sdg		/*
48915185Sdg		 * Calculate the size of the TCP/IP header and make sure that
49015185Sdg		 * we don't overflow the space we have available for it.
49115185Sdg		 */
49215185Sdg		hlen = ip->ip_hl << 2;
49315185Sdg		if (hlen + sizeof(struct tcphdr) > buflen)
49415185Sdg			goto bad;
49515185Sdg		hlen += ((struct tcphdr *)&((char *)ip)[hlen])->th_off << 2;
49628415Speter		if (hlen > MAX_HDR || hlen > buflen)
49715185Sdg			goto bad;
4981541Srgrimes		BCOPY(ip, &cs->cs_ip, hlen);
4991541Srgrimes		cs->cs_hlen = hlen;
5001541Srgrimes		INCR(sls_uncompressedin)
50111964Speter		*hdrp = (u_char *) &cs->cs_ip;
50211964Speter		*hlenp = hlen;
50311964Speter		return (0);
5041541Srgrimes
5051541Srgrimes	default:
5061541Srgrimes		goto bad;
5071541Srgrimes
5081541Srgrimes	case TYPE_COMPRESSED_TCP:
5091541Srgrimes		break;
5101541Srgrimes	}
5111541Srgrimes	/* We've got a compressed packet. */
5121541Srgrimes	INCR(sls_compressedin)
51311964Speter	cp = buf;
5141541Srgrimes	changes = *cp++;
5151541Srgrimes	if (changes & NEW_C) {
5161541Srgrimes		/* Make sure the state index is in range, then grab the state.
5171541Srgrimes		 * If we have a good state index, clear the 'discard' flag. */
5181541Srgrimes		if (*cp >= MAX_STATES)
5191541Srgrimes			goto bad;
5201541Srgrimes
5211541Srgrimes		comp->flags &=~ SLF_TOSS;
5221541Srgrimes		comp->last_recv = *cp++;
5231541Srgrimes	} else {
5241541Srgrimes		/* this packet has an implicit state index.  If we've
5251541Srgrimes		 * had a line error since the last time we got an
5261541Srgrimes		 * explicit state index, we have to toss the packet. */
5271541Srgrimes		if (comp->flags & SLF_TOSS) {
5281541Srgrimes			INCR(sls_tossed)
52911964Speter			return (-1);
5301541Srgrimes		}
5311541Srgrimes	}
5321541Srgrimes	cs = &comp->rstate[comp->last_recv];
5331541Srgrimes	hlen = cs->cs_ip.ip_hl << 2;
5341541Srgrimes	th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen];
5351541Srgrimes	th->th_sum = htons((*cp << 8) | cp[1]);
5361541Srgrimes	cp += 2;
5371541Srgrimes	if (changes & TCP_PUSH_BIT)
5381541Srgrimes		th->th_flags |= TH_PUSH;
5391541Srgrimes	else
5401541Srgrimes		th->th_flags &=~ TH_PUSH;
5411541Srgrimes
5421541Srgrimes	switch (changes & SPECIALS_MASK) {
5431541Srgrimes	case SPECIAL_I:
5441541Srgrimes		{
5451541Srgrimes		register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
5461541Srgrimes		th->th_ack = htonl(ntohl(th->th_ack) + i);
5471541Srgrimes		th->th_seq = htonl(ntohl(th->th_seq) + i);
5481541Srgrimes		}
5491541Srgrimes		break;
5501541Srgrimes
5511541Srgrimes	case SPECIAL_D:
5521541Srgrimes		th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
5531541Srgrimes				   - cs->cs_hlen);
5541541Srgrimes		break;
5551541Srgrimes
5561541Srgrimes	default:
5571541Srgrimes		if (changes & NEW_U) {
5581541Srgrimes			th->th_flags |= TH_URG;
5591541Srgrimes			DECODEU(th->th_urp)
5601541Srgrimes		} else
5611541Srgrimes			th->th_flags &=~ TH_URG;
5621541Srgrimes		if (changes & NEW_W)
5631541Srgrimes			DECODES(th->th_win)
5641541Srgrimes		if (changes & NEW_A)
5651541Srgrimes			DECODEL(th->th_ack)
5661541Srgrimes		if (changes & NEW_S)
5671541Srgrimes			DECODEL(th->th_seq)
5681541Srgrimes		break;
5691541Srgrimes	}
5701541Srgrimes	if (changes & NEW_I) {
5711541Srgrimes		DECODES(cs->cs_ip.ip_id)
5721541Srgrimes	} else
5731541Srgrimes		cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
5741541Srgrimes
5751541Srgrimes	/*
5761541Srgrimes	 * At this point, cp points to the first byte of data in the
57711964Speter	 * packet.  Fill in the IP total length and update the IP
57811964Speter	 * header checksum.
5791541Srgrimes	 */
58011964Speter	vjlen = cp - buf;
58111964Speter	buflen -= vjlen;
58211964Speter	if (buflen < 0)
5831541Srgrimes		/* we must have dropped some characters (crc should detect
5841541Srgrimes		 * this but the old slip framing won't) */
5851541Srgrimes		goto bad;
5861541Srgrimes
58711964Speter	total_len += cs->cs_hlen - vjlen;
58811964Speter	cs->cs_ip.ip_len = htons(total_len);
5891541Srgrimes
5901541Srgrimes	/* recompute the ip header checksum */
59128415Speter	bp = (u_int16_t *) &cs->cs_ip;
59211964Speter	cs->cs_ip.ip_sum = 0;
5931541Srgrimes		for (changes = 0; hlen > 0; hlen -= 2)
5941541Srgrimes			changes += *bp++;
5951541Srgrimes		changes = (changes & 0xffff) + (changes >> 16);
5961541Srgrimes		changes = (changes & 0xffff) + (changes >> 16);
59711964Speter	cs->cs_ip.ip_sum = ~ changes;
59811964Speter
59911964Speter	*hdrp = (u_char *) &cs->cs_ip;
60011964Speter	*hlenp = cs->cs_hlen;
60111964Speter	return vjlen;
60211964Speter
6031541Srgrimesbad:
6041541Srgrimes	comp->flags |= SLF_TOSS;
6051541Srgrimes	INCR(sls_errorin)
60611964Speter	return (-1);
6071541Srgrimes}
608