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: stable/11/sys/net/slcompress.c 331722 2018-03-29 02:50:57Z eadler $
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
63331643Sdimsl_compress_init(struct slcompress *comp, int max_state)
641541Srgrimes{
65331643Sdim	u_int i;
66331643Sdim	struct cstate *tstate = comp->tstate;
671541Srgrimes
6828415Speter	if (max_state == -1) {
6911964Speter		max_state = MAX_STATES - 1;
7028415Speter		bzero((char *)comp, sizeof(*comp));
7128415Speter	} else {
7228415Speter		/* Don't reset statistics */
7328415Speter		bzero((char *)comp->tstate, sizeof(comp->tstate));
7428415Speter		bzero((char *)comp->rstate, sizeof(comp->rstate));
7528415Speter	}
7628415Speter  	for (i = max_state; i > 0; --i) {
771541Srgrimes		tstate[i].cs_id = i;
781541Srgrimes		tstate[i].cs_next = &tstate[i - 1];
791541Srgrimes	}
8011964Speter	tstate[0].cs_next = &tstate[max_state];
811541Srgrimes	tstate[0].cs_id = 0;
821541Srgrimes	comp->last_cs = &tstate[0];
831541Srgrimes	comp->last_recv = 255;
841541Srgrimes	comp->last_xmit = 255;
851541Srgrimes	comp->flags = SLF_TOSS;
861541Srgrimes}
871541Srgrimes
881541Srgrimes
891541Srgrimes/* ENCODE encodes a number that is known to be non-zero.  ENCODEZ
901541Srgrimes * checks for zero (since zero has to be encoded in the long, 3 byte
911541Srgrimes * form).
921541Srgrimes */
931541Srgrimes#define ENCODE(n) { \
9428415Speter	if ((u_int16_t)(n) >= 256) { \
951541Srgrimes		*cp++ = 0; \
961541Srgrimes		cp[1] = (n); \
971541Srgrimes		cp[0] = (n) >> 8; \
981541Srgrimes		cp += 2; \
991541Srgrimes	} else { \
1001541Srgrimes		*cp++ = (n); \
1011541Srgrimes	} \
1021541Srgrimes}
1031541Srgrimes#define ENCODEZ(n) { \
10428415Speter	if ((u_int16_t)(n) >= 256 || (u_int16_t)(n) == 0) { \
1051541Srgrimes		*cp++ = 0; \
1061541Srgrimes		cp[1] = (n); \
1071541Srgrimes		cp[0] = (n) >> 8; \
1081541Srgrimes		cp += 2; \
1091541Srgrimes	} else { \
1101541Srgrimes		*cp++ = (n); \
1111541Srgrimes	} \
1121541Srgrimes}
1131541Srgrimes
1141541Srgrimes#define DECODEL(f) { \
1151541Srgrimes	if (*cp == 0) {\
1161541Srgrimes		(f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \
1171541Srgrimes		cp += 3; \
1181541Srgrimes	} else { \
11928415Speter		(f) = htonl(ntohl(f) + (u_int32_t)*cp++); \
1201541Srgrimes	} \
1211541Srgrimes}
1221541Srgrimes
1231541Srgrimes#define DECODES(f) { \
1241541Srgrimes	if (*cp == 0) {\
1251541Srgrimes		(f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \
1261541Srgrimes		cp += 3; \
1271541Srgrimes	} else { \
12828415Speter		(f) = htons(ntohs(f) + (u_int32_t)*cp++); \
1291541Srgrimes	} \
1301541Srgrimes}
1311541Srgrimes
1321541Srgrimes#define DECODEU(f) { \
1331541Srgrimes	if (*cp == 0) {\
1341541Srgrimes		(f) = htons((cp[1] << 8) | cp[2]); \
1351541Srgrimes		cp += 3; \
1361541Srgrimes	} else { \
13728415Speter		(f) = htons((u_int32_t)*cp++); \
1381541Srgrimes	} \
1391541Srgrimes}
1401541Srgrimes
14152631Sarchie/*
14253192Sarchie * Attempt to compress an outgoing TCP packet and return the type of
14353192Sarchie * the result.  The caller must have already verified that the protocol
14453192Sarchie * is TCP.  The first mbuf must contain the complete IP and TCP headers,
14553192Sarchie * and "ip" must be == mtod(m, struct ip *).  "comp" supplies the
14653192Sarchie * compression state, and "compress_cid" tells us whether it is OK
14753192Sarchie * to leave out the CID field when feasible.
14853192Sarchie *
14953192Sarchie * The caller is responsible for adjusting m->m_pkthdr.len upon return,
15053192Sarchie * if m is an M_PKTHDR mbuf.
15152631Sarchie */
1521541Srgrimesu_int
153331643Sdimsl_compress_tcp(struct mbuf *m, struct ip *ip, struct slcompress *comp,
154331643Sdim    int compress_cid)
1551541Srgrimes{
156331643Sdim	struct cstate *cs = comp->last_cs->cs_next;
157331643Sdim	u_int hlen = ip->ip_hl;
158331643Sdim	struct tcphdr *oth;
159331643Sdim	struct tcphdr *th;
160331643Sdim	u_int deltaS, deltaA;
161331643Sdim	u_int changes = 0;
1621541Srgrimes	u_char new_seq[16];
163331643Sdim	u_char *cp = new_seq;
1641541Srgrimes
1651541Srgrimes	/*
1661541Srgrimes	 * Bail if this is an IP fragment or if the TCP packet isn't
1671541Srgrimes	 * `compressible' (i.e., ACK isn't set or some other control bit is
1681541Srgrimes	 * set).  (We assume that the caller has already made sure the
1691541Srgrimes	 * packet is IP proto TCP).
1701541Srgrimes	 */
1711541Srgrimes	if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40)
1721541Srgrimes		return (TYPE_IP);
1731541Srgrimes
17428415Speter	th = (struct tcphdr *)&((int32_t *)ip)[hlen];
1751541Srgrimes	if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK)
1761541Srgrimes		return (TYPE_IP);
1771541Srgrimes	/*
1781541Srgrimes	 * Packet is compressible -- we're going to send either a
1791541Srgrimes	 * COMPRESSED_TCP or UNCOMPRESSED_TCP packet.  Either way we need
1801541Srgrimes	 * to locate (or create) the connection state.  Special case the
1811541Srgrimes	 * most recently used connection since it's most likely to be used
1821541Srgrimes	 * again & we don't have to do any reordering if it's used.
1831541Srgrimes	 */
1841541Srgrimes	INCR(sls_packets)
1851541Srgrimes	if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
1861541Srgrimes	    ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr ||
18728415Speter	    *(int32_t *)th != ((int32_t *)&cs->cs_ip)[cs->cs_ip.ip_hl]) {
1881541Srgrimes		/*
1891541Srgrimes		 * Wasn't the first -- search for it.
1901541Srgrimes		 *
1911541Srgrimes		 * States are kept in a circularly linked list with
1921541Srgrimes		 * last_cs pointing to the end of the list.  The
1931541Srgrimes		 * list is kept in lru order by moving a state to the
1941541Srgrimes		 * head of the list whenever it is referenced.  Since
1951541Srgrimes		 * the list is short and, empirically, the connection
1961541Srgrimes		 * we want is almost always near the front, we locate
1971541Srgrimes		 * states via linear search.  If we don't find a state
1981541Srgrimes		 * for the datagram, the oldest state is (re-)used.
1991541Srgrimes		 */
200331643Sdim		struct cstate *lcs;
201331643Sdim		struct cstate *lastcs = comp->last_cs;
2021541Srgrimes
2031541Srgrimes		do {
2041541Srgrimes			lcs = cs; cs = cs->cs_next;
2051541Srgrimes			INCR(sls_searches)
2061541Srgrimes			if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
2071541Srgrimes			    && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr
20828415Speter			    && *(int32_t *)th ==
20928415Speter			    ((int32_t *)&cs->cs_ip)[cs->cs_ip.ip_hl])
2101541Srgrimes				goto found;
2111541Srgrimes		} while (cs != lastcs);
2121541Srgrimes
2131541Srgrimes		/*
2141541Srgrimes		 * Didn't find it -- re-use oldest cstate.  Send an
2151541Srgrimes		 * uncompressed packet that tells the other side what
2161541Srgrimes		 * connection number we're using for this conversation.
2171541Srgrimes		 * Note that since the state list is circular, the oldest
2181541Srgrimes		 * state points to the newest and we only need to set
2191541Srgrimes		 * last_cs to update the lru linkage.
2201541Srgrimes		 */
2211541Srgrimes		INCR(sls_misses)
2221541Srgrimes		comp->last_cs = lcs;
2231541Srgrimes		hlen += th->th_off;
2241541Srgrimes		hlen <<= 2;
22526778Sbrian		if (hlen > m->m_len)
22626778Sbrian		    return TYPE_IP;
2271541Srgrimes		goto uncompressed;
2281541Srgrimes
2291541Srgrimes	found:
2301541Srgrimes		/*
2311541Srgrimes		 * Found it -- move to the front on the connection list.
2321541Srgrimes		 */
2331541Srgrimes		if (cs == lastcs)
2341541Srgrimes			comp->last_cs = lcs;
2351541Srgrimes		else {
2361541Srgrimes			lcs->cs_next = cs->cs_next;
2371541Srgrimes			cs->cs_next = lastcs->cs_next;
2381541Srgrimes			lastcs->cs_next = cs;
2391541Srgrimes		}
2401541Srgrimes	}
2411541Srgrimes
2421541Srgrimes	/*
2431541Srgrimes	 * Make sure that only what we expect to change changed. The first
2441541Srgrimes	 * line of the `if' checks the IP protocol version, header length &
2451541Srgrimes	 * type of service.  The 2nd line checks the "Don't fragment" bit.
2461541Srgrimes	 * The 3rd line checks the time-to-live and protocol (the protocol
2471541Srgrimes	 * check is unnecessary but costless).  The 4th line checks the TCP
2481541Srgrimes	 * header length.  The 5th line checks IP options, if any.  The 6th
2491541Srgrimes	 * line checks TCP options, if any.  If any of these things are
2501541Srgrimes	 * different between the previous & current datagram, we send the
2511541Srgrimes	 * current datagram `uncompressed'.
2521541Srgrimes	 */
25328415Speter	oth = (struct tcphdr *)&((int32_t *)&cs->cs_ip)[hlen];
2541541Srgrimes	deltaS = hlen;
2551541Srgrimes	hlen += th->th_off;
2561541Srgrimes	hlen <<= 2;
25726778Sbrian	if (hlen > m->m_len)
25826778Sbrian	    return TYPE_IP;
2591541Srgrimes
26028415Speter	if (((u_int16_t *)ip)[0] != ((u_int16_t *)&cs->cs_ip)[0] ||
26128415Speter	    ((u_int16_t *)ip)[3] != ((u_int16_t *)&cs->cs_ip)[3] ||
26228415Speter	    ((u_int16_t *)ip)[4] != ((u_int16_t *)&cs->cs_ip)[4] ||
2631541Srgrimes	    th->th_off != oth->th_off ||
2641541Srgrimes	    (deltaS > 5 &&
2651541Srgrimes	     BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
2661541Srgrimes	    (th->th_off > 5 &&
2671541Srgrimes	     BCMP(th + 1, oth + 1, (th->th_off - 5) << 2)))
2681541Srgrimes		goto uncompressed;
2691541Srgrimes
2701541Srgrimes	/*
2711541Srgrimes	 * Figure out which of the changing fields changed.  The
2721541Srgrimes	 * receiver expects changes in the order: urgent, window,
2731541Srgrimes	 * ack, seq (the order minimizes the number of temporaries
2741541Srgrimes	 * needed in this section of code).
2751541Srgrimes	 */
2761541Srgrimes	if (th->th_flags & TH_URG) {
2771541Srgrimes		deltaS = ntohs(th->th_urp);
2781541Srgrimes		ENCODEZ(deltaS);
2791541Srgrimes		changes |= NEW_U;
2801541Srgrimes	} else if (th->th_urp != oth->th_urp)
2811541Srgrimes		/* argh! URG not set but urp changed -- a sensible
2821541Srgrimes		 * implementation should never do this but RFC793
2831541Srgrimes		 * doesn't prohibit the change so we have to deal
2841541Srgrimes		 * with it. */
2851541Srgrimes		 goto uncompressed;
2861541Srgrimes
28728415Speter	deltaS = (u_int16_t)(ntohs(th->th_win) - ntohs(oth->th_win));
2883443Sphk	if (deltaS) {
2891541Srgrimes		ENCODE(deltaS);
2901541Srgrimes		changes |= NEW_W;
2911541Srgrimes	}
2921541Srgrimes
2933443Sphk	deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack);
2943443Sphk	if (deltaA) {
2951541Srgrimes		if (deltaA > 0xffff)
2961541Srgrimes			goto uncompressed;
2971541Srgrimes		ENCODE(deltaA);
2981541Srgrimes		changes |= NEW_A;
2991541Srgrimes	}
3001541Srgrimes
3013443Sphk	deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq);
3023443Sphk	if (deltaS) {
3031541Srgrimes		if (deltaS > 0xffff)
3041541Srgrimes			goto uncompressed;
3051541Srgrimes		ENCODE(deltaS);
3061541Srgrimes		changes |= NEW_S;
3071541Srgrimes	}
3081541Srgrimes
3091541Srgrimes	switch(changes) {
3101541Srgrimes
3111541Srgrimes	case 0:
3121541Srgrimes		/*
3131541Srgrimes		 * Nothing changed. If this packet contains data and the
3141541Srgrimes		 * last one didn't, this is probably a data packet following
3151541Srgrimes		 * an ack (normal on an interactive connection) and we send
3161541Srgrimes		 * it compressed.  Otherwise it's probably a retransmit,
3171541Srgrimes		 * retransmitted ack or window probe.  Send it uncompressed
3181541Srgrimes		 * in case the other side missed the compressed version.
3191541Srgrimes		 */
3201541Srgrimes		if (ip->ip_len != cs->cs_ip.ip_len &&
3211541Srgrimes		    ntohs(cs->cs_ip.ip_len) == hlen)
3221541Srgrimes			break;
3231541Srgrimes
324102412Scharnier		/* FALLTHROUGH */
3251541Srgrimes
3261541Srgrimes	case SPECIAL_I:
3271541Srgrimes	case SPECIAL_D:
3281541Srgrimes		/*
3291541Srgrimes		 * actual changes match one of our special case encodings --
3301541Srgrimes		 * send packet uncompressed.
3311541Srgrimes		 */
3321541Srgrimes		goto uncompressed;
3331541Srgrimes
3341541Srgrimes	case NEW_S|NEW_A:
3351541Srgrimes		if (deltaS == deltaA &&
3361541Srgrimes		    deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
3371541Srgrimes			/* special case for echoed terminal traffic */
3381541Srgrimes			changes = SPECIAL_I;
3391541Srgrimes			cp = new_seq;
3401541Srgrimes		}
3411541Srgrimes		break;
3421541Srgrimes
3431541Srgrimes	case NEW_S:
3441541Srgrimes		if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
3451541Srgrimes			/* special case for data xfer */
3461541Srgrimes			changes = SPECIAL_D;
3471541Srgrimes			cp = new_seq;
3481541Srgrimes		}
3491541Srgrimes		break;
3501541Srgrimes	}
3511541Srgrimes
3521541Srgrimes	deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
3531541Srgrimes	if (deltaS != 1) {
3541541Srgrimes		ENCODEZ(deltaS);
3551541Srgrimes		changes |= NEW_I;
3561541Srgrimes	}
3571541Srgrimes	if (th->th_flags & TH_PUSH)
3581541Srgrimes		changes |= TCP_PUSH_BIT;
3591541Srgrimes	/*
3601541Srgrimes	 * Grab the cksum before we overwrite it below.  Then update our
3611541Srgrimes	 * state with this packet's header.
3621541Srgrimes	 */
3631541Srgrimes	deltaA = ntohs(th->th_sum);
3641541Srgrimes	BCOPY(ip, &cs->cs_ip, hlen);
3651541Srgrimes
3661541Srgrimes	/*
3671541Srgrimes	 * We want to use the original packet as our compressed packet.
3681541Srgrimes	 * (cp - new_seq) is the number of bytes we need for compressed
3691541Srgrimes	 * sequence numbers.  In addition we need one byte for the change
3701541Srgrimes	 * mask, one for the connection id and two for the tcp checksum.
3711541Srgrimes	 * So, (cp - new_seq) + 4 bytes of header are needed.  hlen is how
3721541Srgrimes	 * many bytes of the original packet to toss so subtract the two to
3731541Srgrimes	 * get the new packet size.
3741541Srgrimes	 */
3751541Srgrimes	deltaS = cp - new_seq;
3761541Srgrimes	cp = (u_char *)ip;
3771541Srgrimes	if (compress_cid == 0 || comp->last_xmit != cs->cs_id) {
3781541Srgrimes		comp->last_xmit = cs->cs_id;
3791541Srgrimes		hlen -= deltaS + 4;
3801541Srgrimes		cp += hlen;
3811541Srgrimes		*cp++ = changes | NEW_C;
3821541Srgrimes		*cp++ = cs->cs_id;
3831541Srgrimes	} else {
3841541Srgrimes		hlen -= deltaS + 3;
3851541Srgrimes		cp += hlen;
3861541Srgrimes		*cp++ = changes;
3871541Srgrimes	}
3881541Srgrimes	m->m_len -= hlen;
3891541Srgrimes	m->m_data += hlen;
3901541Srgrimes	*cp++ = deltaA >> 8;
3911541Srgrimes	*cp++ = deltaA;
3921541Srgrimes	BCOPY(new_seq, cp, deltaS);
3931541Srgrimes	INCR(sls_compressed)
3941541Srgrimes	return (TYPE_COMPRESSED_TCP);
3951541Srgrimes
3961541Srgrimes	/*
3971541Srgrimes	 * Update connection state cs & send uncompressed packet ('uncompressed'
3981541Srgrimes	 * means a regular ip/tcp packet but with the 'conversation id' we hope
3991541Srgrimes	 * to use on future compressed packets in the protocol field).
4001541Srgrimes	 */
4011541Srgrimesuncompressed:
4021541Srgrimes	BCOPY(ip, &cs->cs_ip, hlen);
4031541Srgrimes	ip->ip_p = cs->cs_id;
4041541Srgrimes	comp->last_xmit = cs->cs_id;
4051541Srgrimes	return (TYPE_UNCOMPRESSED_TCP);
4061541Srgrimes}
4071541Srgrimes
4081541Srgrimes
4091541Srgrimesint
410331643Sdimsl_uncompress_tcp(u_char **bufp, int len, u_int type, struct slcompress *comp)
4111541Srgrimes{
41211964Speter	u_char *hdr, *cp;
41311964Speter	int hlen, vjlen;
41411964Speter
41511964Speter	cp = bufp? *bufp: NULL;
41611964Speter	vjlen = sl_uncompress_tcp_core(cp, len, len, type, comp, &hdr, &hlen);
41711964Speter	if (vjlen < 0)
41811964Speter		return (0);	/* error */
41911964Speter	if (vjlen == 0)
42011964Speter		return (len);	/* was uncompressed already */
42111964Speter
42211964Speter	cp += vjlen;
42311964Speter	len -= vjlen;
42411964Speter
42511964Speter	/*
42611964Speter	 * At this point, cp points to the first byte of data in the
42711964Speter	 * packet.  If we're not aligned on a 4-byte boundary, copy the
42811964Speter	 * data down so the ip & tcp headers will be aligned.  Then back up
42911964Speter	 * cp by the tcp/ip header length to make room for the reconstructed
43011964Speter	 * header (we assume the packet we were handed has enough space to
43111964Speter	 * prepend 128 bytes of header).
43211964Speter	 */
43337649Sbde	if ((intptr_t)cp & 3) {
43411964Speter		if (len > 0)
435113072Sdes			BCOPY(cp, ((intptr_t)cp &~ 3), len);
43637649Sbde		cp = (u_char *)((intptr_t)cp &~ 3);
43711964Speter	}
43811964Speter	cp -= hlen;
43911964Speter	len += hlen;
44011964Speter	BCOPY(hdr, cp, hlen);
44111964Speter
44211964Speter	*bufp = cp;
44311964Speter	return (len);
44411964Speter}
44511964Speter
44611964Speter/*
44711964Speter * Uncompress a packet of total length total_len.  The first buflen
44811964Speter * bytes are at buf; this must include the entire (compressed or
44911964Speter * uncompressed) TCP/IP header.  This procedure returns the length
45011964Speter * of the VJ header, with a pointer to the uncompressed IP header
45111964Speter * in *hdrp and its length in *hlenp.
45211964Speter */
45311964Speterint
454331643Sdimsl_uncompress_tcp_core(u_char *buf, int buflen, int total_len, u_int type,
455331643Sdim    struct slcompress *comp, u_char **hdrp, u_int *hlenp)
45611964Speter{
457331643Sdim	u_char *cp;
458331643Sdim	u_int hlen, changes;
459331643Sdim	struct tcphdr *th;
460331643Sdim	struct cstate *cs;
461331643Sdim	struct ip *ip;
462331643Sdim	u_int16_t *bp;
463331643Sdim	u_int vjlen;
4641541Srgrimes
4651541Srgrimes	switch (type) {
4661541Srgrimes
4671541Srgrimes	case TYPE_UNCOMPRESSED_TCP:
46811964Speter		ip = (struct ip *) buf;
4691541Srgrimes		if (ip->ip_p >= MAX_STATES)
4701541Srgrimes			goto bad;
4711541Srgrimes		cs = &comp->rstate[comp->last_recv = ip->ip_p];
4721541Srgrimes		comp->flags &=~ SLF_TOSS;
4731541Srgrimes		ip->ip_p = IPPROTO_TCP;
47415185Sdg		/*
47515185Sdg		 * Calculate the size of the TCP/IP header and make sure that
47615185Sdg		 * we don't overflow the space we have available for it.
47715185Sdg		 */
47815185Sdg		hlen = ip->ip_hl << 2;
47915185Sdg		if (hlen + sizeof(struct tcphdr) > buflen)
48015185Sdg			goto bad;
48115185Sdg		hlen += ((struct tcphdr *)&((char *)ip)[hlen])->th_off << 2;
48228415Speter		if (hlen > MAX_HDR || hlen > buflen)
48315185Sdg			goto bad;
4841541Srgrimes		BCOPY(ip, &cs->cs_ip, hlen);
4851541Srgrimes		cs->cs_hlen = hlen;
4861541Srgrimes		INCR(sls_uncompressedin)
48711964Speter		*hdrp = (u_char *) &cs->cs_ip;
48811964Speter		*hlenp = hlen;
48911964Speter		return (0);
4901541Srgrimes
4911541Srgrimes	default:
4921541Srgrimes		goto bad;
4931541Srgrimes
4941541Srgrimes	case TYPE_COMPRESSED_TCP:
4951541Srgrimes		break;
4961541Srgrimes	}
4971541Srgrimes	/* We've got a compressed packet. */
4981541Srgrimes	INCR(sls_compressedin)
49911964Speter	cp = buf;
5001541Srgrimes	changes = *cp++;
5011541Srgrimes	if (changes & NEW_C) {
5021541Srgrimes		/* Make sure the state index is in range, then grab the state.
5031541Srgrimes		 * If we have a good state index, clear the 'discard' flag. */
5041541Srgrimes		if (*cp >= MAX_STATES)
5051541Srgrimes			goto bad;
5061541Srgrimes
5071541Srgrimes		comp->flags &=~ SLF_TOSS;
5081541Srgrimes		comp->last_recv = *cp++;
5091541Srgrimes	} else {
5101541Srgrimes		/* this packet has an implicit state index.  If we've
5111541Srgrimes		 * had a line error since the last time we got an
5121541Srgrimes		 * explicit state index, we have to toss the packet. */
5131541Srgrimes		if (comp->flags & SLF_TOSS) {
5141541Srgrimes			INCR(sls_tossed)
51511964Speter			return (-1);
5161541Srgrimes		}
5171541Srgrimes	}
5181541Srgrimes	cs = &comp->rstate[comp->last_recv];
5191541Srgrimes	hlen = cs->cs_ip.ip_hl << 2;
5201541Srgrimes	th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen];
5211541Srgrimes	th->th_sum = htons((*cp << 8) | cp[1]);
5221541Srgrimes	cp += 2;
5231541Srgrimes	if (changes & TCP_PUSH_BIT)
5241541Srgrimes		th->th_flags |= TH_PUSH;
5251541Srgrimes	else
5261541Srgrimes		th->th_flags &=~ TH_PUSH;
5271541Srgrimes
5281541Srgrimes	switch (changes & SPECIALS_MASK) {
5291541Srgrimes	case SPECIAL_I:
5301541Srgrimes		{
531331643Sdim		u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
5321541Srgrimes		th->th_ack = htonl(ntohl(th->th_ack) + i);
5331541Srgrimes		th->th_seq = htonl(ntohl(th->th_seq) + i);
5341541Srgrimes		}
5351541Srgrimes		break;
5361541Srgrimes
5371541Srgrimes	case SPECIAL_D:
5381541Srgrimes		th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
5391541Srgrimes				   - cs->cs_hlen);
5401541Srgrimes		break;
5411541Srgrimes
5421541Srgrimes	default:
5431541Srgrimes		if (changes & NEW_U) {
5441541Srgrimes			th->th_flags |= TH_URG;
5451541Srgrimes			DECODEU(th->th_urp)
5461541Srgrimes		} else
5471541Srgrimes			th->th_flags &=~ TH_URG;
5481541Srgrimes		if (changes & NEW_W)
5491541Srgrimes			DECODES(th->th_win)
5501541Srgrimes		if (changes & NEW_A)
5511541Srgrimes			DECODEL(th->th_ack)
5521541Srgrimes		if (changes & NEW_S)
5531541Srgrimes			DECODEL(th->th_seq)
5541541Srgrimes		break;
5551541Srgrimes	}
5561541Srgrimes	if (changes & NEW_I) {
5571541Srgrimes		DECODES(cs->cs_ip.ip_id)
5581541Srgrimes	} else
5591541Srgrimes		cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
5601541Srgrimes
5611541Srgrimes	/*
5621541Srgrimes	 * At this point, cp points to the first byte of data in the
56311964Speter	 * packet.  Fill in the IP total length and update the IP
56411964Speter	 * header checksum.
5651541Srgrimes	 */
56611964Speter	vjlen = cp - buf;
56711964Speter	buflen -= vjlen;
56811964Speter	if (buflen < 0)
5691541Srgrimes		/* we must have dropped some characters (crc should detect
5701541Srgrimes		 * this but the old slip framing won't) */
5711541Srgrimes		goto bad;
5721541Srgrimes
57311964Speter	total_len += cs->cs_hlen - vjlen;
57411964Speter	cs->cs_ip.ip_len = htons(total_len);
5751541Srgrimes
5761541Srgrimes	/* recompute the ip header checksum */
57728415Speter	bp = (u_int16_t *) &cs->cs_ip;
57811964Speter	cs->cs_ip.ip_sum = 0;
5791541Srgrimes		for (changes = 0; hlen > 0; hlen -= 2)
5801541Srgrimes			changes += *bp++;
5811541Srgrimes		changes = (changes & 0xffff) + (changes >> 16);
5821541Srgrimes		changes = (changes & 0xffff) + (changes >> 16);
58311964Speter	cs->cs_ip.ip_sum = ~ changes;
58411964Speter
58511964Speter	*hdrp = (u_char *) &cs->cs_ip;
58611964Speter	*hlenp = cs->cs_hlen;
58711964Speter	return vjlen;
58811964Speter
5891541Srgrimesbad:
5901541Srgrimes	comp->flags |= SLF_TOSS;
5911541Srgrimes	INCR(sls_errorin)
59211964Speter	return (-1);
5931541Srgrimes}
594