xref: /csrg-svn/sys/net/slcompress.c (revision 38379)
138361Ssam /*
2*38379Ssam  *	@(#)slcompress.c	7.4 (Berkeley) 07/01/89
338374Skarels  *
438361Ssam  *			THIS CODE IS NOT FOR DISTRIBUTION!
538361Ssam  *	KEEP YOUR GRUBBY HANDS OFF UNLESS AUTHORIZED BY VAN JACOBSON TO COPY!
638361Ssam  *			ASK SAM, MIKE, OR BILL ABOUT IT.
738374Skarels  *
838361Ssam  * Routines to compress and uncompess tcp packets (for transmission
938361Ssam  * over low speed serial lines.
1038361Ssam  *
1138361Ssam  * Copyright (c) 1988, 1989 by Van Jacobson, Lawrence Berkeley Laboratory
1238361Ssam  * All rights reserved.
1338361Ssam  */
1438361Ssam 
1538374Skarels #include "sl.h"
1638374Skarels #if NSL > 0
1738361Ssam #ifndef lint
1838374Skarels static char rcsid[] = "$Header: slcompress.c,v 1.10 89/06/05 08:28:52 van Exp $";
1938361Ssam #endif
2038361Ssam 
2138361Ssam #include <sys/types.h>
2238361Ssam #include <sys/param.h>
2338361Ssam #include <sys/mbuf.h>
2438361Ssam #include <netinet/in.h>
2538361Ssam #include <netinet/in_systm.h>
2638361Ssam #include <netinet/ip.h>
2738361Ssam #include <netinet/tcp.h>
2838361Ssam 
2938361Ssam #include "slcompress.h"
3038361Ssam 
3138374Skarels #ifndef NO_SL_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))
3938361Ssam 
4038361Ssam 
4138361Ssam void
4238361Ssam sl_compress_init(comp)
4338361Ssam 	struct slcompress *comp;
4438361Ssam {
4538361Ssam 	register u_int i;
4638361Ssam 	register struct cstate *tstate = comp->tstate;
4738361Ssam 
4838361Ssam 	bzero((char *)comp, sizeof(*comp));
4938361Ssam 	for (i = MAX_STATES - 1; i > 0; --i) {
5038361Ssam 		tstate[i].cs_id = i;
5138361Ssam 		tstate[i].cs_next = &tstate[i - 1];
5238361Ssam 	}
5338361Ssam 	tstate[0].cs_next = &tstate[MAX_STATES - 1];
5438361Ssam 	tstate[0].cs_id = 0;
5538361Ssam 	comp->last_cs = &tstate[0];
5638361Ssam 	comp->last_recv = 255;
5738361Ssam 	comp->last_xmit = 255;
5838361Ssam }
5938361Ssam 
6038361Ssam 
6138361Ssam /* ENCODE encodes a number that is known to be non-zero.  ENCODEZ
6238361Ssam  * checks for zero (since zero has to be encoded in the long, 3 byte
6338361Ssam  * form).
6438361Ssam  */
6538361Ssam #define ENCODE(n) { \
6638361Ssam 	if ((u_short)(n) >= 256) { \
6738361Ssam 		*cp++ = 0; \
6838361Ssam 		cp[1] = (n); \
6938361Ssam 		cp[0] = (n) >> 8; \
7038361Ssam 		cp += 2; \
7138361Ssam 	} else { \
7238361Ssam 		*cp++ = (n); \
7338361Ssam 	} \
7438361Ssam }
7538361Ssam #define ENCODEZ(n) { \
7638361Ssam 	if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
7738361Ssam 		*cp++ = 0; \
7838361Ssam 		cp[1] = (n); \
7938361Ssam 		cp[0] = (n) >> 8; \
8038361Ssam 		cp += 2; \
8138361Ssam 	} else { \
8238361Ssam 		*cp++ = (n); \
8338361Ssam 	} \
8438361Ssam }
8538361Ssam 
8638361Ssam #define DECODEL(f) { \
8738361Ssam 	if (*cp == 0) {\
8838361Ssam 		(f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \
8938361Ssam 		cp += 3; \
9038361Ssam 	} else { \
9138361Ssam 		(f) = htonl(ntohl(f) + (u_long)*cp++); \
9238361Ssam 	} \
9338361Ssam }
9438361Ssam 
9538361Ssam #define DECODES(f) { \
9638361Ssam 	if (*cp == 0) {\
9738361Ssam 		(f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \
9838361Ssam 		cp += 3; \
9938361Ssam 	} else { \
10038361Ssam 		(f) = htons(ntohs(f) + (u_long)*cp++); \
10138361Ssam 	} \
10238361Ssam }
10338361Ssam 
10438361Ssam 
10538361Ssam u_char
10638361Ssam sl_compress_tcp(m, ip, comp)
10738361Ssam 	struct mbuf *m;
10838361Ssam 	register struct ip *ip;
10938361Ssam 	struct slcompress *comp;
11038361Ssam {
11138361Ssam 	register struct cstate *cs = comp->last_cs->cs_next;
11238361Ssam 	register u_int hlen = ip->ip_hl;
11338361Ssam 	register struct tcphdr *oth;
11438361Ssam 	register struct tcphdr *th;
11538361Ssam 	register u_int deltaS, deltaA;
11638361Ssam 	register u_int changes = 0;
11738361Ssam 	u_char new_seq[16];
11838361Ssam 	register u_char *cp = new_seq;
11938361Ssam 
12038361Ssam 	/*
12138361Ssam 	 * Bail if this is an ip fragment or if we don't have
12238361Ssam 	 * a complete ip & tcp header in the first mbuf.  Otherwise,
12338361Ssam 	 * check flags to see if this is a packet we might compress
12438361Ssam 	 * and, if so, try to locate the connection state.
12538361Ssam 	 * special case the most recently used connection since
12638361Ssam 	 * it's most likely to be used again & we don't have to
12738361Ssam 	 * do any reordering if it's used.
12838361Ssam 	 */
12938361Ssam 	if ((ip->ip_off & 0x3fff) || m->m_len < 40)
13038361Ssam 		return (TYPE_IP);
13138361Ssam 
13238361Ssam 	th = (struct tcphdr *)&((int *)ip)[hlen];
13338361Ssam 	if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK)
13438361Ssam 		return (TYPE_IP);
13538361Ssam 
13638374Skarels 	INCR(sls_packets)
13738374Skarels 	if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
13838374Skarels 	    ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr ||
13938374Skarels 	    *(int *)th != ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) {
14038361Ssam 		/*
14138361Ssam 		 * Wasn't the first -- search for it.
14238361Ssam 		 *
14338361Ssam 		 * States are kept in a circularly linked list with
14438361Ssam 		 * first_cs pointing to the head of the list.  The
14538361Ssam 		 * list is kept in lru order by moving a state to the
14638361Ssam 		 * head of the list whenever it is referenced.  Since
14738361Ssam 		 * the list is short and, empirically, the connection
14838361Ssam 		 * we want is almost always near the front, we locate
14938361Ssam 		 * states via linear search.  If we don't find a state
15038361Ssam 		 * for the datagram, the oldest state is used.
15138361Ssam 		 */
15238361Ssam 		register struct cstate *lcs;
15338361Ssam 
15438361Ssam 		do {
15538361Ssam 			lcs = cs; cs = cs->cs_next;
15638374Skarels 			INCR(sls_searches)
15738361Ssam 			if (*(int *)th == ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]
15838361Ssam 			    && ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
15938361Ssam 			    && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr)
16038361Ssam 				goto found;
16138361Ssam 		} while (cs != comp->last_cs);
16238374Skarels 		INCR(sls_misses)
16338361Ssam 
16438361Ssam 		/*
16538361Ssam 		 * Didn't find it -- re-use oldest cstate.
16638361Ssam 		 * Send an uncompressed packet that tells
16738361Ssam 		 * the other side what connection number
16838361Ssam 		 * we're using for this conversation.  Note
16938361Ssam 		 * that since the state list is circular, the
17038361Ssam 		 * oldest state points to the newest and we only
17138361Ssam 		 * need to set last_cs to update the lru linkage.
17238361Ssam 		 */
17338361Ssam 		comp->last_cs = lcs;
17438361Ssam 		hlen += th->th_off;
17538361Ssam 		hlen <<= 2;
17638361Ssam 		goto uncompressed;
17738361Ssam 
17838361Ssam 	found:
17938361Ssam 		/*
18038361Ssam 		 * Found it -- move to the front on the connection list.
18138361Ssam 		 */
18238361Ssam 		if (comp->last_cs == cs)
18338361Ssam 			comp->last_cs = lcs;
18438361Ssam 		else {
18538361Ssam 			lcs->cs_next = cs->cs_next;
18638361Ssam 			cs->cs_next = comp->last_cs->cs_next;
18738361Ssam 			comp->last_cs->cs_next = cs;
18838361Ssam 		}
18938361Ssam 	}
19038361Ssam 
19138361Ssam 	/*
19238361Ssam 	 * Make sure that only what we expect to change changed.
19338361Ssam 	 */
19438361Ssam 	oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen];
19538361Ssam 	deltaS = hlen;
19638361Ssam 	hlen += th->th_off;
19738361Ssam 	hlen <<= 2;
19838361Ssam 
19938361Ssam 	if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] ||
20038361Ssam 	    ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] ||
20138361Ssam 	    th->th_off != oth->th_off ||
20238361Ssam 	    (deltaS > 5 &&
20338361Ssam 	     BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
20438361Ssam 	    (th->th_off > 5 &&
20538361Ssam 	     BCMP(th + 1, oth + 1, (th->th_off - 5) << 2)))
20638361Ssam 		goto uncompressed;
20738361Ssam 
20838361Ssam 	/*
20938361Ssam 	 * Figure out which of the changing fields changed.  The
21038361Ssam 	 * receiver expects changes in the order: urgent, window,
21138361Ssam 	 * ack, seq (the order minimizes the number of temporaries
21238361Ssam 	 * needed in this section of code).
21338361Ssam 	 */
21438361Ssam 	if (th->th_flags & TH_URG) {
21538361Ssam 		deltaS = ntohs(th->th_urp);
21638361Ssam 		ENCODEZ(deltaS);
21738361Ssam 		changes |= NEW_U;
21838361Ssam 	} else if (th->th_urp != oth->th_urp)
21938361Ssam 		/* argh! URG not set but urp changed -- a sensible
22038361Ssam 		 * implementation should never do this but RFC793
22138361Ssam 		 * doesn't prohibit the change so we have to deal
22238361Ssam 		 * with it.  */
22338361Ssam 		 goto uncompressed;
22438361Ssam 
22538361Ssam 	if (deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) {
22638361Ssam 		ENCODE(deltaS);
22738361Ssam 		changes |= NEW_W;
22838361Ssam 	}
22938361Ssam 
23038361Ssam 	if (deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack)) {
23138361Ssam 		if (deltaA > 0xffff)
23238361Ssam 			goto uncompressed;
23338361Ssam 		ENCODE(deltaA);
23438361Ssam 		changes |= NEW_A;
23538361Ssam 	}
23638361Ssam 
23738361Ssam 	if (deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq)) {
23838361Ssam 		if (deltaS > 0xffff)
23938361Ssam 			goto uncompressed;
24038361Ssam 		ENCODE(deltaS);
24138361Ssam 		changes |= NEW_S;
24238361Ssam 	}
24338361Ssam 
24438361Ssam 	switch(changes) {
24538361Ssam 
24638361Ssam 	case 0:
24738361Ssam 		if (ip->ip_len != cs->cs_ip.ip_len && ntohs(ip->ip_len) != hlen)
24838361Ssam 			break;
24938361Ssam 		/*
25038361Ssam 		 * Nothing changed and this packet looks like a duplicate
25138361Ssam 		 * of the last or contains no data -- this is probably a
25238361Ssam 		 * retransmitted ack or window probe.  Send it
25338361Ssam 		 * uncompressed in case the other side missed the
25438361Ssam 		 * compressed version.
25538361Ssam 		 *
25638361Ssam 		 * (fall through)
25738361Ssam 		 */
25838361Ssam 
25938361Ssam 	case SPECIAL_I:
26038361Ssam 	case SPECIAL_D:
26138361Ssam 		/*
26238361Ssam 		 * actual changes match one of our special case encodings --
26338361Ssam 		 * send packet uncompressed.
26438361Ssam 		 */
26538361Ssam 		goto uncompressed;
26638361Ssam 
26738361Ssam 	case NEW_S|NEW_A:
26838361Ssam 		if (deltaS == deltaA &&
26938361Ssam 		    deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
27038361Ssam 			/* special case for echoed terminal traffic */
27138361Ssam 			changes = SPECIAL_I;
27238361Ssam 			cp = new_seq;
27338361Ssam 		}
27438361Ssam 		break;
27538361Ssam 
27638361Ssam 	case NEW_S:
27738361Ssam 		if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
27838361Ssam 			/* special case for data xfer */
27938361Ssam 			changes = SPECIAL_D;
28038361Ssam 			cp = new_seq;
28138361Ssam 		}
28238361Ssam 		break;
28338361Ssam 	}
28438361Ssam 
28538361Ssam 	deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
28638361Ssam 	if (deltaS != 1) {
28738361Ssam 		ENCODEZ(deltaS);
28838361Ssam 		changes |= NEW_I;
28938361Ssam 	}
29038361Ssam 	if (th->th_flags & TH_PUSH)
29138361Ssam 		changes |= TCP_PUSH_BIT;
29238361Ssam 	/*
29338361Ssam 	 * Grab the cksum before we overwrite it below.  Then update our
29438361Ssam 	 * state with this packet's header.
29538361Ssam 	 */
29638361Ssam 	deltaA = ntohs(th->th_sum);
29738361Ssam 	BCOPY(ip, &cs->cs_ip, hlen);
29838361Ssam 
29938361Ssam 	/*
30038361Ssam 	 * We want to use the original packet as our compressed packet.
30138361Ssam 	 * (cp - new_seq) is the number of bytes we need for compressed
30238361Ssam 	 * sequence numbers.  In addition we need one byte for the change
30338361Ssam 	 * mask, one for the connection id and two for the tcp checksum.
30438361Ssam 	 * So, (cp - new_seq) + 4 bytes of header are needed.  hlen is how
30538361Ssam 	 * many bytes of the original packet to toss so subtract the two to
30638361Ssam 	 * get the new packet size.
30738361Ssam 	 */
30838361Ssam 	deltaS = cp - new_seq;
30938361Ssam 	cp = (u_char *)ip;
31038361Ssam 	if (comp->last_xmit != cs->cs_id) {
31138361Ssam 		comp->last_xmit = cs->cs_id;
31238361Ssam 		hlen -= deltaS + 4;
31338374Skarels 		cp += hlen;
31438374Skarels 		m->m_len -= hlen;
31538374Skarels 		m->m_data += hlen;
31638374Skarels 		*cp++ = changes | NEW_C;
31738361Ssam 		*cp++ = cs->cs_id;
31838361Ssam 	} else {
31938361Ssam 		hlen -= deltaS + 3;
32038374Skarels 		cp += hlen;
32138374Skarels 		m->m_len -= hlen;
32238374Skarels 		m->m_data += hlen;
32338374Skarels 		*cp++ = changes;
32438361Ssam 	}
32538361Ssam 	*cp++ = deltaA >> 8;
32638361Ssam 	*cp++ = deltaA;
32738361Ssam 	BCOPY(new_seq, cp, deltaS);
32838374Skarels 	INCR(sls_compressed)
32938361Ssam 	return (TYPE_COMPRESSED_TCP);
33038361Ssam 
33138361Ssam 	/*
33238361Ssam 	 * Update connection state cs & send uncompressed packet ('uncompressed'
33338361Ssam 	 * means a regular ip/tcp packet but with the 'conversation id' we hope
33438361Ssam 	 * to use on future compressed packets in the protocol field).
33538361Ssam 	 */
33638361Ssam uncompressed:
33738361Ssam 	BCOPY(ip, &cs->cs_ip, hlen);
33838361Ssam 	ip->ip_p = cs->cs_id;
33938361Ssam 	comp->last_xmit = cs->cs_id;
34038361Ssam 	return (TYPE_UNCOMPRESSED_TCP);
34138361Ssam }
34238361Ssam 
34338361Ssam 
34438374Skarels int
34538374Skarels sl_uncompress_tcp(bufp, len, type, comp)
34638374Skarels 	u_char **bufp;
34738374Skarels 	int len;
34838374Skarels 	u_int type;
34938361Ssam 	struct slcompress *comp;
35038361Ssam {
35138361Ssam 	register u_char *cp;
35238361Ssam 	register u_int hlen, changes;
35338361Ssam 	register struct tcphdr *th;
35438361Ssam 	register struct cstate *cs;
35538361Ssam 	register struct ip *ip;
35638361Ssam 
35738361Ssam 	switch (type) {
35838361Ssam 
35938361Ssam 	case TYPE_UNCOMPRESSED_TCP:
36038374Skarels 		ip = (struct ip *) *bufp;
36138374Skarels 		if (ip->ip_p >= MAX_STATES) {
36238374Skarels 			INCR(sls_errorin)
36338374Skarels 			return (0);
36438374Skarels 		}
36538361Ssam 		cs = &comp->rstate[comp->last_recv = ip->ip_p];
36638361Ssam 		comp->flags &=~ SLF_TOSS;
36738361Ssam 		ip->ip_p = IPPROTO_TCP;
36838361Ssam 		hlen = ip->ip_hl;
36938361Ssam 		hlen += ((struct tcphdr *)&((int *)ip)[hlen])->th_off;
37038361Ssam 		hlen <<= 2;
37138361Ssam 		BCOPY(ip, &cs->cs_ip, hlen);
37238361Ssam 		cs->cs_ip.ip_sum = 0;
37338361Ssam 		cs->cs_hlen = hlen;
37438374Skarels 		INCR(sls_uncompressedin)
37538374Skarels 		return (len);
37638361Ssam 
37738361Ssam 	case TYPE_ERROR:
37838361Ssam 		comp->flags |= SLF_TOSS;
37938374Skarels 	default:
38038374Skarels 		INCR(sls_errorin)
38138374Skarels 		return (0);
38238361Ssam 
38338361Ssam 	case TYPE_COMPRESSED_TCP:
38438361Ssam 		break;
38538361Ssam 	}
38638361Ssam 	/* We've got a compressed packet. */
38738374Skarels 	INCR(sls_compressedin)
38838374Skarels 	cp = *bufp;
38938361Ssam 	changes = *cp++;
39038361Ssam 	if (changes & NEW_C) {
39138361Ssam 		/* Make sure the state index is in range, then grab the state.
39238361Ssam 		 * If we have a good state index, clear the 'discard' flag. */
39338374Skarels 		if (*cp >= MAX_STATES) {
39438374Skarels 			INCR(sls_errorin)
39538374Skarels 			return (0);
39638374Skarels 		}
39738361Ssam 		comp->flags &=~ SLF_TOSS;
39838361Ssam 		comp->last_recv = *cp++;
39938361Ssam 	} else {
40038361Ssam 		/* this packet has an implicit state index.  If we've
40138361Ssam 		 * had a line error since the last time we got an
40238361Ssam 		 * explicit state index, we have to toss the packet. */
40338374Skarels 		if (comp->flags & SLF_TOSS) {
40438374Skarels 			INCR(sls_tossed)
40538374Skarels 			return (0);
40638374Skarels 		}
40738361Ssam 	}
40838361Ssam 	cs = &comp->rstate[comp->last_recv];
40938361Ssam 	hlen = cs->cs_ip.ip_hl << 2;
41038361Ssam 	th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen];
41138361Ssam 	th->th_sum = htons((*cp << 8) | cp[1]);
41238361Ssam 	cp += 2;
41338361Ssam 	if (changes & TCP_PUSH_BIT)
41438361Ssam 		th->th_flags |= TH_PUSH;
41538361Ssam 	else
41638361Ssam 		th->th_flags &=~ TH_PUSH;
41738361Ssam 
41838361Ssam 	switch (changes & SPECIALS_MASK) {
41938361Ssam 	case SPECIAL_I:
42038361Ssam 		{
42138361Ssam 		register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
42238361Ssam 		th->th_ack = htonl(ntohl(th->th_ack) + i);
42338361Ssam 		th->th_seq = htonl(ntohl(th->th_seq) + i);
42438361Ssam 		}
42538361Ssam 		break;
42638361Ssam 
42738361Ssam 	case SPECIAL_D:
42838361Ssam 		th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
42938361Ssam 				   - cs->cs_hlen);
43038361Ssam 		break;
43138361Ssam 
43238361Ssam 	default:
43338361Ssam 		if (changes & NEW_U) {
43438361Ssam 			th->th_flags |= TH_URG;
43538361Ssam 			DECODES(th->th_urp)
43638361Ssam 		} else
43738361Ssam 			th->th_flags &=~ TH_URG;
43838361Ssam 		if (changes & NEW_W)
43938361Ssam 			DECODES(th->th_win)
44038361Ssam 		if (changes & NEW_A)
44138361Ssam 			DECODEL(th->th_ack)
44238361Ssam 		if (changes & NEW_S)
44338361Ssam 			DECODEL(th->th_seq)
44438361Ssam 		break;
44538361Ssam 	}
44638361Ssam 	if (changes & NEW_I) {
44738361Ssam 		DECODES(cs->cs_ip.ip_id)
44838361Ssam 	} else
44938361Ssam 		cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
45038361Ssam 
45138361Ssam 	/*
45238361Ssam 	 * At this point, cp points to the first byte of data in the
45338374Skarels 	 * packet.  If we're not aligned on a 4-byte boundary, copy the
45438374Skarels 	 * data down so the ip & tcp headers will be aligned.  Then back up
45538374Skarels 	 * cp by the tcp/ip header length to make room for the reconstructed
45638374Skarels 	 * header (we assume the packet we were handed has enough space to
45738374Skarels 	 * prepend 128 bytes of header).  Adjust the lenth to account for
45838374Skarels 	 * the new header & fill in the IP total length.
45938361Ssam 	 */
46038374Skarels 	len -= (cp - *bufp);
46138374Skarels 	if (len < 0) {
46238374Skarels 		/* we must have dropped some characters (crc should detect
46338374Skarels 		 * this but the old slip framing won't) */
46438374Skarels 		INCR(sls_errorin)
46538374Skarels 		return (0);
46638374Skarels 	}
467*38379Ssam 	if ((int)cp & 3) {
468*38379Ssam 		if (len > 0)
469*38379Ssam 			BCOPY(cp, (int)cp &~ 3, len);
47038374Skarels 		cp = (u_char *)((int)cp &~ 3);
47138374Skarels 	}
47238374Skarels 	cp -= cs->cs_hlen;
47338374Skarels 	len += cs->cs_hlen;
47438374Skarels 	cs->cs_ip.ip_len = htons(len);
47538374Skarels 	BCOPY(&cs->cs_ip, cp, cs->cs_hlen);
47638374Skarels 	*bufp = cp;
47738361Ssam 
47838374Skarels 	/* recompute the ip header checksum */
47938374Skarels 	{
48038374Skarels 		register u_short *bp = (u_short *)cp;
48138374Skarels 		for (changes = 0; hlen > 0; hlen -= 2)
48238374Skarels 			changes += *bp++;
48338374Skarels 		changes = (changes & 0xffff) + (changes >> 16);
48438374Skarels 		changes = (changes & 0xffff) + (changes >> 16);
48538374Skarels 		((struct ip *)cp)->ip_sum = ~ changes;
48638374Skarels 	}
48738374Skarels 	return (len);
48838361Ssam }
48938374Skarels #endif
490