xref: /csrg-svn/sys/net/slcompress.c (revision 61345)
149340Sbostic /*-
249340Sbostic  * Copyright (c) 1989 The Regents of the University of California.
349340Sbostic  * All rights reserved.
449340Sbostic  *
549340Sbostic  * %sccs.include.redist.c%
649340Sbostic  *
7*61345Sbostic  *	@(#)slcompress.c	7.9 (Berkeley) 06/04/93
849340Sbostic  */
949340Sbostic 
1038361Ssam /*
1138361Ssam  * Routines to compress and uncompess tcp packets (for transmission
1238361Ssam  * over low speed serial lines.
1338361Ssam  *
1449340Sbostic  * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
1549340Sbostic  *	- Initial distribution.
1639948Ssam  *
1749340Sbostic  * static char rcsid[] =
1849340Sbostic  * "$Header: slcompress.c,v 1.19 89/12/31 08:52:59 van Exp $";
1938361Ssam  */
2038361Ssam 
2138361Ssam #include <sys/param.h>
2238361Ssam #include <sys/mbuf.h>
2356529Sbostic 
2438361Ssam #include <netinet/in.h>
2538361Ssam #include <netinet/in_systm.h>
2638361Ssam #include <netinet/ip.h>
2738361Ssam #include <netinet/tcp.h>
2838361Ssam 
2956529Sbostic #include <net/slcompress.h>
3038361Ssam 
3139948Ssam #ifndef SL_NO_STATS
3238374Skarels #define INCR(counter) ++comp->counter;
3338374Skarels #else
3438374Skarels #define INCR(counter)
3538374Skarels #endif
3638361Ssam 
3738361Ssam #define BCMP(p1, p2, n) bcmp((char *)(p1), (char *)(p2), (int)(n))
3838361Ssam #define BCOPY(p1, p2, n) bcopy((char *)(p1), (char *)(p2), (int)(n))
3939948Ssam #ifndef KERNEL
4039948Ssam #define ovbcopy bcopy
4139948Ssam #endif
4238361Ssam 
4338361Ssam void
4438361Ssam sl_compress_init(comp)
4538361Ssam 	struct slcompress *comp;
4638361Ssam {
4738361Ssam 	register u_int i;
4838361Ssam 	register struct cstate *tstate = comp->tstate;
4938361Ssam 
5038361Ssam 	bzero((char *)comp, sizeof(*comp));
5138361Ssam 	for (i = MAX_STATES - 1; i > 0; --i) {
5238361Ssam 		tstate[i].cs_id = i;
5338361Ssam 		tstate[i].cs_next = &tstate[i - 1];
5438361Ssam 	}
5538361Ssam 	tstate[0].cs_next = &tstate[MAX_STATES - 1];
5638361Ssam 	tstate[0].cs_id = 0;
5738361Ssam 	comp->last_cs = &tstate[0];
5838361Ssam 	comp->last_recv = 255;
5938361Ssam 	comp->last_xmit = 255;
6038361Ssam }
6138361Ssam 
6238361Ssam 
6338361Ssam /* ENCODE encodes a number that is known to be non-zero.  ENCODEZ
6438361Ssam  * checks for zero (since zero has to be encoded in the long, 3 byte
6538361Ssam  * form).
6638361Ssam  */
6738361Ssam #define ENCODE(n) { \
6838361Ssam 	if ((u_short)(n) >= 256) { \
6938361Ssam 		*cp++ = 0; \
7038361Ssam 		cp[1] = (n); \
7138361Ssam 		cp[0] = (n) >> 8; \
7238361Ssam 		cp += 2; \
7338361Ssam 	} else { \
7438361Ssam 		*cp++ = (n); \
7538361Ssam 	} \
7638361Ssam }
7738361Ssam #define ENCODEZ(n) { \
7838361Ssam 	if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
7938361Ssam 		*cp++ = 0; \
8038361Ssam 		cp[1] = (n); \
8138361Ssam 		cp[0] = (n) >> 8; \
8238361Ssam 		cp += 2; \
8338361Ssam 	} else { \
8438361Ssam 		*cp++ = (n); \
8538361Ssam 	} \
8638361Ssam }
8738361Ssam 
8838361Ssam #define DECODEL(f) { \
8938361Ssam 	if (*cp == 0) {\
9038361Ssam 		(f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \
9138361Ssam 		cp += 3; \
9238361Ssam 	} else { \
9338361Ssam 		(f) = htonl(ntohl(f) + (u_long)*cp++); \
9438361Ssam 	} \
9538361Ssam }
9638361Ssam 
9738361Ssam #define DECODES(f) { \
9838361Ssam 	if (*cp == 0) {\
9938361Ssam 		(f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \
10038361Ssam 		cp += 3; \
10138361Ssam 	} else { \
10238361Ssam 		(f) = htons(ntohs(f) + (u_long)*cp++); \
10338361Ssam 	} \
10438361Ssam }
10538361Ssam 
10639948Ssam #define DECODEU(f) { \
10739948Ssam 	if (*cp == 0) {\
10839948Ssam 		(f) = htons((cp[1] << 8) | cp[2]); \
10939948Ssam 		cp += 3; \
11039948Ssam 	} else { \
11139948Ssam 		(f) = htons((u_long)*cp++); \
11239948Ssam 	} \
11339948Ssam }
11438361Ssam 
115*61345Sbostic u_int
11639948Ssam sl_compress_tcp(m, ip, comp, compress_cid)
11738361Ssam 	struct mbuf *m;
11838361Ssam 	register struct ip *ip;
11938361Ssam 	struct slcompress *comp;
12039948Ssam 	int compress_cid;
12138361Ssam {
12238361Ssam 	register struct cstate *cs = comp->last_cs->cs_next;
12338361Ssam 	register u_int hlen = ip->ip_hl;
12438361Ssam 	register struct tcphdr *oth;
12538361Ssam 	register struct tcphdr *th;
12638361Ssam 	register u_int deltaS, deltaA;
12738361Ssam 	register u_int changes = 0;
12838361Ssam 	u_char new_seq[16];
12938361Ssam 	register u_char *cp = new_seq;
13038361Ssam 
13138361Ssam 	/*
13239948Ssam 	 * Bail if this is an IP fragment or if the TCP packet isn't
13339948Ssam 	 * `compressible' (i.e., ACK isn't set or some other control bit is
13439948Ssam 	 * set).  (We assume that the caller has already made sure the
13539948Ssam 	 * packet is IP proto TCP).
13638361Ssam 	 */
13739948Ssam 	if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40)
13838361Ssam 		return (TYPE_IP);
13938361Ssam 
14038361Ssam 	th = (struct tcphdr *)&((int *)ip)[hlen];
14138361Ssam 	if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK)
14238361Ssam 		return (TYPE_IP);
14339948Ssam 	/*
14439948Ssam 	 * Packet is compressible -- we're going to send either a
14539948Ssam 	 * COMPRESSED_TCP or UNCOMPRESSED_TCP packet.  Either way we need
14639948Ssam 	 * to locate (or create) the connection state.  Special case the
14739948Ssam 	 * most recently used connection since it's most likely to be used
14839948Ssam 	 * again & we don't have to do any reordering if it's used.
14939948Ssam 	 */
15038374Skarels 	INCR(sls_packets)
15138374Skarels 	if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
15238374Skarels 	    ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr ||
15338374Skarels 	    *(int *)th != ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) {
15438361Ssam 		/*
15538361Ssam 		 * Wasn't the first -- search for it.
15638361Ssam 		 *
15738361Ssam 		 * States are kept in a circularly linked list with
15839948Ssam 		 * last_cs pointing to the end of the list.  The
15938361Ssam 		 * list is kept in lru order by moving a state to the
16038361Ssam 		 * head of the list whenever it is referenced.  Since
16138361Ssam 		 * the list is short and, empirically, the connection
16238361Ssam 		 * we want is almost always near the front, we locate
16338361Ssam 		 * states via linear search.  If we don't find a state
16439948Ssam 		 * for the datagram, the oldest state is (re-)used.
16538361Ssam 		 */
16638361Ssam 		register struct cstate *lcs;
16739948Ssam 		register struct cstate *lastcs = comp->last_cs;
16838361Ssam 
16938361Ssam 		do {
17038361Ssam 			lcs = cs; cs = cs->cs_next;
17138374Skarels 			INCR(sls_searches)
17239948Ssam 			if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
17339948Ssam 			    && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr
17439948Ssam 			    && *(int *)th == ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl])
17538361Ssam 				goto found;
17639948Ssam 		} while (cs != lastcs);
17738361Ssam 
17838361Ssam 		/*
17939948Ssam 		 * Didn't find it -- re-use oldest cstate.  Send an
18039948Ssam 		 * uncompressed packet that tells the other side what
18139948Ssam 		 * connection number we're using for this conversation.
18239948Ssam 		 * Note that since the state list is circular, the oldest
18339948Ssam 		 * state points to the newest and we only need to set
18439948Ssam 		 * last_cs to update the lru linkage.
18538361Ssam 		 */
18639948Ssam 		INCR(sls_misses)
18738361Ssam 		comp->last_cs = lcs;
18838361Ssam 		hlen += th->th_off;
18938361Ssam 		hlen <<= 2;
19038361Ssam 		goto uncompressed;
19138361Ssam 
19238361Ssam 	found:
19338361Ssam 		/*
19438361Ssam 		 * Found it -- move to the front on the connection list.
19538361Ssam 		 */
19639948Ssam 		if (cs == lastcs)
19738361Ssam 			comp->last_cs = lcs;
19838361Ssam 		else {
19938361Ssam 			lcs->cs_next = cs->cs_next;
20039948Ssam 			cs->cs_next = lastcs->cs_next;
20139948Ssam 			lastcs->cs_next = cs;
20238361Ssam 		}
20338361Ssam 	}
20438361Ssam 
20538361Ssam 	/*
20639948Ssam 	 * Make sure that only what we expect to change changed. The first
20739948Ssam 	 * line of the `if' checks the IP protocol version, header length &
20839948Ssam 	 * type of service.  The 2nd line checks the "Don't fragment" bit.
20939948Ssam 	 * The 3rd line checks the time-to-live and protocol (the protocol
21039948Ssam 	 * check is unnecessary but costless).  The 4th line checks the TCP
21139948Ssam 	 * header length.  The 5th line checks IP options, if any.  The 6th
21239948Ssam 	 * line checks TCP options, if any.  If any of these things are
21339948Ssam 	 * different between the previous & current datagram, we send the
21439948Ssam 	 * current datagram `uncompressed'.
21538361Ssam 	 */
21638361Ssam 	oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen];
21738361Ssam 	deltaS = hlen;
21838361Ssam 	hlen += th->th_off;
21938361Ssam 	hlen <<= 2;
22038361Ssam 
22138361Ssam 	if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] ||
22239948Ssam 	    ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] ||
22338361Ssam 	    ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] ||
22438361Ssam 	    th->th_off != oth->th_off ||
22538361Ssam 	    (deltaS > 5 &&
22638361Ssam 	     BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
22738361Ssam 	    (th->th_off > 5 &&
22838361Ssam 	     BCMP(th + 1, oth + 1, (th->th_off - 5) << 2)))
22938361Ssam 		goto uncompressed;
23038361Ssam 
23138361Ssam 	/*
23238361Ssam 	 * Figure out which of the changing fields changed.  The
23338361Ssam 	 * receiver expects changes in the order: urgent, window,
23438361Ssam 	 * ack, seq (the order minimizes the number of temporaries
23538361Ssam 	 * needed in this section of code).
23638361Ssam 	 */
23738361Ssam 	if (th->th_flags & TH_URG) {
23838361Ssam 		deltaS = ntohs(th->th_urp);
23938361Ssam 		ENCODEZ(deltaS);
24038361Ssam 		changes |= NEW_U;
24138361Ssam 	} else if (th->th_urp != oth->th_urp)
24238361Ssam 		/* argh! URG not set but urp changed -- a sensible
24338361Ssam 		 * implementation should never do this but RFC793
24438361Ssam 		 * doesn't prohibit the change so we have to deal
24539948Ssam 		 * with it. */
24638361Ssam 		 goto uncompressed;
24738361Ssam 
24838361Ssam 	if (deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) {
24938361Ssam 		ENCODE(deltaS);
25038361Ssam 		changes |= NEW_W;
25138361Ssam 	}
25238361Ssam 
25338361Ssam 	if (deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack)) {
25438361Ssam 		if (deltaA > 0xffff)
25538361Ssam 			goto uncompressed;
25638361Ssam 		ENCODE(deltaA);
25738361Ssam 		changes |= NEW_A;
25838361Ssam 	}
25938361Ssam 
26038361Ssam 	if (deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq)) {
26138361Ssam 		if (deltaS > 0xffff)
26238361Ssam 			goto uncompressed;
26338361Ssam 		ENCODE(deltaS);
26438361Ssam 		changes |= NEW_S;
26538361Ssam 	}
26638361Ssam 
26738361Ssam 	switch(changes) {
26838361Ssam 
26938361Ssam 	case 0:
27038361Ssam 		/*
27139948Ssam 		 * Nothing changed. If this packet contains data and the
27239948Ssam 		 * last one didn't, this is probably a data packet following
27339948Ssam 		 * an ack (normal on an interactive connection) and we send
27439948Ssam 		 * it compressed.  Otherwise it's probably a retransmit,
27539948Ssam 		 * retransmitted ack or window probe.  Send it uncompressed
27639948Ssam 		 * in case the other side missed the compressed version.
27738361Ssam 		 */
27839948Ssam 		if (ip->ip_len != cs->cs_ip.ip_len &&
27939948Ssam 		    ntohs(cs->cs_ip.ip_len) == hlen)
28039948Ssam 			break;
28138361Ssam 
28239948Ssam 		/* (fall through) */
28339948Ssam 
28438361Ssam 	case SPECIAL_I:
28538361Ssam 	case SPECIAL_D:
28638361Ssam 		/*
28738361Ssam 		 * actual changes match one of our special case encodings --
28838361Ssam 		 * send packet uncompressed.
28938361Ssam 		 */
29038361Ssam 		goto uncompressed;
29138361Ssam 
29238361Ssam 	case NEW_S|NEW_A:
29338361Ssam 		if (deltaS == deltaA &&
29438361Ssam 		    deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
29538361Ssam 			/* special case for echoed terminal traffic */
29638361Ssam 			changes = SPECIAL_I;
29738361Ssam 			cp = new_seq;
29838361Ssam 		}
29938361Ssam 		break;
30038361Ssam 
30138361Ssam 	case NEW_S:
30238361Ssam 		if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
30338361Ssam 			/* special case for data xfer */
30438361Ssam 			changes = SPECIAL_D;
30538361Ssam 			cp = new_seq;
30638361Ssam 		}
30738361Ssam 		break;
30838361Ssam 	}
30938361Ssam 
31038361Ssam 	deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
31138361Ssam 	if (deltaS != 1) {
31238361Ssam 		ENCODEZ(deltaS);
31338361Ssam 		changes |= NEW_I;
31438361Ssam 	}
31538361Ssam 	if (th->th_flags & TH_PUSH)
31638361Ssam 		changes |= TCP_PUSH_BIT;
31738361Ssam 	/*
31838361Ssam 	 * Grab the cksum before we overwrite it below.  Then update our
31938361Ssam 	 * state with this packet's header.
32038361Ssam 	 */
32138361Ssam 	deltaA = ntohs(th->th_sum);
32238361Ssam 	BCOPY(ip, &cs->cs_ip, hlen);
32338361Ssam 
32438361Ssam 	/*
32538361Ssam 	 * We want to use the original packet as our compressed packet.
32638361Ssam 	 * (cp - new_seq) is the number of bytes we need for compressed
32738361Ssam 	 * sequence numbers.  In addition we need one byte for the change
32838361Ssam 	 * mask, one for the connection id and two for the tcp checksum.
32938361Ssam 	 * So, (cp - new_seq) + 4 bytes of header are needed.  hlen is how
33038361Ssam 	 * many bytes of the original packet to toss so subtract the two to
33138361Ssam 	 * get the new packet size.
33238361Ssam 	 */
33338361Ssam 	deltaS = cp - new_seq;
33438361Ssam 	cp = (u_char *)ip;
33539948Ssam 	if (compress_cid == 0 || comp->last_xmit != cs->cs_id) {
33638361Ssam 		comp->last_xmit = cs->cs_id;
33738361Ssam 		hlen -= deltaS + 4;
33838374Skarels 		cp += hlen;
33938374Skarels 		*cp++ = changes | NEW_C;
34038361Ssam 		*cp++ = cs->cs_id;
34138361Ssam 	} else {
34238361Ssam 		hlen -= deltaS + 3;
34338374Skarels 		cp += hlen;
34438374Skarels 		*cp++ = changes;
34538361Ssam 	}
34639948Ssam 	m->m_len -= hlen;
34739948Ssam 	m->m_data += hlen;
34838361Ssam 	*cp++ = deltaA >> 8;
34938361Ssam 	*cp++ = deltaA;
35038361Ssam 	BCOPY(new_seq, cp, deltaS);
35138374Skarels 	INCR(sls_compressed)
35238361Ssam 	return (TYPE_COMPRESSED_TCP);
35338361Ssam 
35438361Ssam 	/*
35538361Ssam 	 * Update connection state cs & send uncompressed packet ('uncompressed'
35638361Ssam 	 * means a regular ip/tcp packet but with the 'conversation id' we hope
35738361Ssam 	 * to use on future compressed packets in the protocol field).
35838361Ssam 	 */
35938361Ssam uncompressed:
36038361Ssam 	BCOPY(ip, &cs->cs_ip, hlen);
36138361Ssam 	ip->ip_p = cs->cs_id;
36238361Ssam 	comp->last_xmit = cs->cs_id;
36338361Ssam 	return (TYPE_UNCOMPRESSED_TCP);
36438361Ssam }
36538361Ssam 
36638361Ssam 
36738374Skarels int
36838374Skarels sl_uncompress_tcp(bufp, len, type, comp)
36938374Skarels 	u_char **bufp;
37038374Skarels 	int len;
37138374Skarels 	u_int type;
37238361Ssam 	struct slcompress *comp;
37338361Ssam {
37438361Ssam 	register u_char *cp;
37538361Ssam 	register u_int hlen, changes;
37638361Ssam 	register struct tcphdr *th;
37738361Ssam 	register struct cstate *cs;
37838361Ssam 	register struct ip *ip;
37938361Ssam 
38038361Ssam 	switch (type) {
38138361Ssam 
38238361Ssam 	case TYPE_UNCOMPRESSED_TCP:
38338374Skarels 		ip = (struct ip *) *bufp;
38439948Ssam 		if (ip->ip_p >= MAX_STATES)
38539948Ssam 			goto bad;
38638361Ssam 		cs = &comp->rstate[comp->last_recv = ip->ip_p];
38738361Ssam 		comp->flags &=~ SLF_TOSS;
38838361Ssam 		ip->ip_p = IPPROTO_TCP;
38938361Ssam 		hlen = ip->ip_hl;
39038361Ssam 		hlen += ((struct tcphdr *)&((int *)ip)[hlen])->th_off;
39138361Ssam 		hlen <<= 2;
39238361Ssam 		BCOPY(ip, &cs->cs_ip, hlen);
39338361Ssam 		cs->cs_ip.ip_sum = 0;
39438361Ssam 		cs->cs_hlen = hlen;
39538374Skarels 		INCR(sls_uncompressedin)
39638374Skarels 		return (len);
39738361Ssam 
39838374Skarels 	default:
39939948Ssam 		goto bad;
40038361Ssam 
40138361Ssam 	case TYPE_COMPRESSED_TCP:
40238361Ssam 		break;
40338361Ssam 	}
40438361Ssam 	/* We've got a compressed packet. */
40538374Skarels 	INCR(sls_compressedin)
40638374Skarels 	cp = *bufp;
40738361Ssam 	changes = *cp++;
40838361Ssam 	if (changes & NEW_C) {
40938361Ssam 		/* Make sure the state index is in range, then grab the state.
41038361Ssam 		 * If we have a good state index, clear the 'discard' flag. */
41139948Ssam 		if (*cp >= MAX_STATES)
41239948Ssam 			goto bad;
41339948Ssam 
41438361Ssam 		comp->flags &=~ SLF_TOSS;
41538361Ssam 		comp->last_recv = *cp++;
41638361Ssam 	} else {
41738361Ssam 		/* this packet has an implicit state index.  If we've
41838361Ssam 		 * had a line error since the last time we got an
41938361Ssam 		 * explicit state index, we have to toss the packet. */
42038374Skarels 		if (comp->flags & SLF_TOSS) {
42138374Skarels 			INCR(sls_tossed)
42238374Skarels 			return (0);
42338374Skarels 		}
42438361Ssam 	}
42538361Ssam 	cs = &comp->rstate[comp->last_recv];
42638361Ssam 	hlen = cs->cs_ip.ip_hl << 2;
42738361Ssam 	th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen];
42838361Ssam 	th->th_sum = htons((*cp << 8) | cp[1]);
42938361Ssam 	cp += 2;
43038361Ssam 	if (changes & TCP_PUSH_BIT)
43138361Ssam 		th->th_flags |= TH_PUSH;
43238361Ssam 	else
43338361Ssam 		th->th_flags &=~ TH_PUSH;
43438361Ssam 
43538361Ssam 	switch (changes & SPECIALS_MASK) {
43638361Ssam 	case SPECIAL_I:
43738361Ssam 		{
43838361Ssam 		register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
43938361Ssam 		th->th_ack = htonl(ntohl(th->th_ack) + i);
44038361Ssam 		th->th_seq = htonl(ntohl(th->th_seq) + i);
44138361Ssam 		}
44238361Ssam 		break;
44338361Ssam 
44438361Ssam 	case SPECIAL_D:
44538361Ssam 		th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
44638361Ssam 				   - cs->cs_hlen);
44738361Ssam 		break;
44838361Ssam 
44938361Ssam 	default:
45038361Ssam 		if (changes & NEW_U) {
45138361Ssam 			th->th_flags |= TH_URG;
45239948Ssam 			DECODEU(th->th_urp)
45338361Ssam 		} else
45438361Ssam 			th->th_flags &=~ TH_URG;
45538361Ssam 		if (changes & NEW_W)
45638361Ssam 			DECODES(th->th_win)
45738361Ssam 		if (changes & NEW_A)
45838361Ssam 			DECODEL(th->th_ack)
45938361Ssam 		if (changes & NEW_S)
46038361Ssam 			DECODEL(th->th_seq)
46138361Ssam 		break;
46238361Ssam 	}
46338361Ssam 	if (changes & NEW_I) {
46438361Ssam 		DECODES(cs->cs_ip.ip_id)
46538361Ssam 	} else
46638361Ssam 		cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
46738361Ssam 
46838361Ssam 	/*
46938361Ssam 	 * At this point, cp points to the first byte of data in the
47038374Skarels 	 * packet.  If we're not aligned on a 4-byte boundary, copy the
47138374Skarels 	 * data down so the ip & tcp headers will be aligned.  Then back up
47238374Skarels 	 * cp by the tcp/ip header length to make room for the reconstructed
47338374Skarels 	 * header (we assume the packet we were handed has enough space to
47439948Ssam 	 * prepend 128 bytes of header).  Adjust the length to account for
47538374Skarels 	 * the new header & fill in the IP total length.
47638361Ssam 	 */
47738374Skarels 	len -= (cp - *bufp);
47839948Ssam 	if (len < 0)
47938374Skarels 		/* we must have dropped some characters (crc should detect
48038374Skarels 		 * this but the old slip framing won't) */
48139948Ssam 		goto bad;
48239948Ssam 
48338379Ssam 	if ((int)cp & 3) {
48438379Ssam 		if (len > 0)
48539948Ssam 			(void) ovbcopy(cp, (caddr_t)((int)cp &~ 3), len);
48638374Skarels 		cp = (u_char *)((int)cp &~ 3);
48738374Skarels 	}
48838374Skarels 	cp -= cs->cs_hlen;
48938374Skarels 	len += cs->cs_hlen;
49038374Skarels 	cs->cs_ip.ip_len = htons(len);
49138374Skarels 	BCOPY(&cs->cs_ip, cp, cs->cs_hlen);
49238374Skarels 	*bufp = cp;
49338361Ssam 
49438374Skarels 	/* recompute the ip header checksum */
49538374Skarels 	{
49638374Skarels 		register u_short *bp = (u_short *)cp;
49738374Skarels 		for (changes = 0; hlen > 0; hlen -= 2)
49838374Skarels 			changes += *bp++;
49938374Skarels 		changes = (changes & 0xffff) + (changes >> 16);
50038374Skarels 		changes = (changes & 0xffff) + (changes >> 16);
50138374Skarels 		((struct ip *)cp)->ip_sum = ~ changes;
50238374Skarels 	}
50338374Skarels 	return (len);
50439948Ssam bad:
50539948Ssam 	comp->flags |= SLF_TOSS;
50639948Ssam 	INCR(sls_errorin)
50739948Ssam 	return (0);
50838361Ssam }
509