Home | History | Annotate | Line # | Download | only in net
slcompress.c revision 1.3
      1 /*-
      2  * Copyright (c) 1989, 1991 Regents of the University of California.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms are permitted
      6  * provided that the above copyright notice and this paragraph are
      7  * duplicated in all such forms and that any documentation,
      8  * advertising materials, and other materials related to such
      9  * distribution and use acknowledge that the software was developed
     10  * by the University of California, Berkeley.  The name of the
     11  * University may not be used to endorse or promote products derived
     12  * from this software without specific prior written permission.
     13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
     14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
     15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     16  *
     17  *    Van Jacobson (van (at) ee.lbl.gov), Dec 31, 1989:
     18  *    - Initial distribution.
     19  *
     20  *	$Id: slcompress.c,v 1.3 1993/05/20 03:06:12 cgd Exp $
     21  */
     22 
     23 #include <sys/types.h>
     24 #include <sys/param.h>
     25 #include <sys/mbuf.h>
     26 #include <netinet/in.h>
     27 #include <netinet/in_systm.h>
     28 #include <netinet/ip.h>
     29 #include <netinet/tcp.h>
     30 
     31 #include "slcompress.h"
     32 
     33 #ifndef SL_NO_STATS
     34 #define INCR(counter) ++comp->counter;
     35 #else
     36 #define INCR(counter)
     37 #endif
     38 
     39 #define BCMP(p1, p2, n) bcmp((char *)(p1), (char *)(p2), (int)(n))
     40 #define BCOPY(p1, p2, n) bcopy((char *)(p1), (char *)(p2), (int)(n))
     41 #ifndef KERNEL
     42 #define ovbcopy bcopy
     43 #endif
     44 
     45 
     46 void
     47 sl_compress_init(comp)
     48 	struct slcompress *comp;
     49 {
     50 	register u_int i;
     51 	register struct cstate *tstate = comp->tstate;
     52 
     53 	bzero((char *)comp, sizeof(*comp));
     54 	for (i = MAX_STATES - 1; i > 0; --i) {
     55 		tstate[i].cs_id = i;
     56 		tstate[i].cs_next = &tstate[i - 1];
     57 	}
     58 	tstate[0].cs_next = &tstate[MAX_STATES - 1];
     59 	tstate[0].cs_id = 0;
     60 	comp->last_cs = &tstate[0];
     61 	comp->last_recv = 255;
     62 	comp->last_xmit = 255;
     63 	comp->flags = SLF_TOSS;
     64 }
     65 
     66 
     67 /* ENCODE encodes a number that is known to be non-zero.  ENCODEZ
     68  * checks for zero (since zero has to be encoded in the long, 3 byte
     69  * form).
     70  */
     71 #define ENCODE(n) { \
     72 	if ((u_short)(n) >= 256) { \
     73 		*cp++ = 0; \
     74 		cp[1] = (n); \
     75 		cp[0] = (n) >> 8; \
     76 		cp += 2; \
     77 	} else { \
     78 		*cp++ = (n); \
     79 	} \
     80 }
     81 #define ENCODEZ(n) { \
     82 	if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
     83 		*cp++ = 0; \
     84 		cp[1] = (n); \
     85 		cp[0] = (n) >> 8; \
     86 		cp += 2; \
     87 	} else { \
     88 		*cp++ = (n); \
     89 	} \
     90 }
     91 
     92 #define DECODEL(f) { \
     93 	if (*cp == 0) {\
     94 		(f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \
     95 		cp += 3; \
     96 	} else { \
     97 		(f) = htonl(ntohl(f) + (u_long)*cp++); \
     98 	} \
     99 }
    100 
    101 #define DECODES(f) { \
    102 	if (*cp == 0) {\
    103 		(f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \
    104 		cp += 3; \
    105 	} else { \
    106 		(f) = htons(ntohs(f) + (u_long)*cp++); \
    107 	} \
    108 }
    109 
    110 #define DECODEU(f) { \
    111 	if (*cp == 0) {\
    112 		(f) = htons((cp[1] << 8) | cp[2]); \
    113 		cp += 3; \
    114 	} else { \
    115 		(f) = htons((u_long)*cp++); \
    116 	} \
    117 }
    118 
    119 
    120 u_char
    121 sl_compress_tcp(m, ip, comp, compress_cid)
    122 	struct mbuf *m;
    123 	register struct ip *ip;
    124 	struct slcompress *comp;
    125 	int compress_cid;
    126 {
    127 	register struct cstate *cs = comp->last_cs->cs_next;
    128 	register u_int hlen = ip->ip_hl;
    129 	register struct tcphdr *oth;
    130 	register struct tcphdr *th;
    131 	register u_int deltaS, deltaA;
    132 	register u_int changes = 0;
    133 	u_char new_seq[16];
    134 	register u_char *cp = new_seq;
    135 
    136 	/*
    137 	 * Bail if this is an IP fragment or if the TCP packet isn't
    138 	 * `compressible' (i.e., ACK isn't set or some other control bit is
    139 	 * set).  (We assume that the caller has already made sure the
    140 	 * packet is IP proto TCP).
    141 	 */
    142 	if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40)
    143 		return (TYPE_IP);
    144 
    145 	th = (struct tcphdr *)&((int *)ip)[hlen];
    146 	if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK)
    147 		return (TYPE_IP);
    148 	/*
    149 	 * Packet is compressible -- we're going to send either a
    150 	 * COMPRESSED_TCP or UNCOMPRESSED_TCP packet.  Either way we need
    151 	 * to locate (or create) the connection state.  Special case the
    152 	 * most recently used connection since it's most likely to be used
    153 	 * again & we don't have to do any reordering if it's used.
    154 	 */
    155 	INCR(sls_packets)
    156 	if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
    157 	    ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr ||
    158 	    *(int *)th != ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) {
    159 		/*
    160 		 * Wasn't the first -- search for it.
    161 		 *
    162 		 * States are kept in a circularly linked list with
    163 		 * last_cs pointing to the end of the list.  The
    164 		 * list is kept in lru order by moving a state to the
    165 		 * head of the list whenever it is referenced.  Since
    166 		 * the list is short and, empirically, the connection
    167 		 * we want is almost always near the front, we locate
    168 		 * states via linear search.  If we don't find a state
    169 		 * for the datagram, the oldest state is (re-)used.
    170 		 */
    171 		register struct cstate *lcs;
    172 		register struct cstate *lastcs = comp->last_cs;
    173 
    174 		do {
    175 			lcs = cs; cs = cs->cs_next;
    176 			INCR(sls_searches)
    177 			if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
    178 			    && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr
    179 			    && *(int *)th == ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl])
    180 				goto found;
    181 		} while (cs != lastcs);
    182 
    183 		/*
    184 		 * Didn't find it -- re-use oldest cstate.  Send an
    185 		 * uncompressed packet that tells the other side what
    186 		 * connection number we're using for this conversation.
    187 		 * Note that since the state list is circular, the oldest
    188 		 * state points to the newest and we only need to set
    189 		 * last_cs to update the lru linkage.
    190 		 */
    191 		INCR(sls_misses)
    192 		comp->last_cs = lcs;
    193 		hlen += th->th_off;
    194 		hlen <<= 2;
    195 		if (hlen > m->m_len)
    196 			return (TYPE_IP);
    197 		goto uncompressed;
    198 
    199 	found:
    200 		/*
    201 		 * Found it -- move to the front on the connection list.
    202 		 */
    203 		if (cs == lastcs)
    204 			comp->last_cs = lcs;
    205 		else {
    206 			lcs->cs_next = cs->cs_next;
    207 			cs->cs_next = lastcs->cs_next;
    208 			lastcs->cs_next = cs;
    209 		}
    210 	}
    211 
    212 	/*
    213 	 * Make sure that only what we expect to change changed. The first
    214 	 * line of the `if' checks the IP protocol version, header length &
    215 	 * type of service.  The 2nd line checks the "Don't fragment" bit.
    216 	 * The 3rd line checks the time-to-live and protocol (the protocol
    217 	 * check is unnecessary but costless).  The 4th line checks the TCP
    218 	 * header length.  The 5th line checks IP options, if any.  The 6th
    219 	 * line checks TCP options, if any.  If any of these things are
    220 	 * different between the previous & current datagram, we send the
    221 	 * current datagram `uncompressed'.
    222 	 */
    223 	oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen];
    224 	deltaS = hlen;
    225 	hlen += th->th_off;
    226 	hlen <<= 2;
    227 	if (hlen > m->m_len)
    228 		return (TYPE_IP);
    229 
    230 	if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] ||
    231 	    ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] ||
    232 	    ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] ||
    233 	    th->th_off != oth->th_off ||
    234 	    (deltaS > 5 &&
    235 	     BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
    236 	    (th->th_off > 5 &&
    237 	     BCMP(th + 1, oth + 1, (th->th_off - 5) << 2)))
    238 		goto uncompressed;
    239 
    240 	/*
    241 	 * Figure out which of the changing fields changed.  The
    242 	 * receiver expects changes in the order: urgent, window,
    243 	 * ack, seq (the order minimizes the number of temporaries
    244 	 * needed in this section of code).
    245 	 */
    246 	if (th->th_flags & TH_URG) {
    247 		deltaS = ntohs(th->th_urp);
    248 		ENCODEZ(deltaS);
    249 		changes |= NEW_U;
    250 	} else if (th->th_urp != oth->th_urp)
    251 		/* argh! URG not set but urp changed -- a sensible
    252 		 * implementation should never do this but RFC793
    253 		 * doesn't prohibit the change so we have to deal
    254 		 * with it. */
    255 		 goto uncompressed;
    256 
    257 	if (deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) {
    258 		ENCODE(deltaS);
    259 		changes |= NEW_W;
    260 	}
    261 
    262 	if (deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack)) {
    263 		if (deltaA > 0xffff)
    264 			goto uncompressed;
    265 		ENCODE(deltaA);
    266 		changes |= NEW_A;
    267 	}
    268 
    269 	if (deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq)) {
    270 		if (deltaS > 0xffff)
    271 			goto uncompressed;
    272 		ENCODE(deltaS);
    273 		changes |= NEW_S;
    274 	}
    275 
    276 	switch(changes) {
    277 
    278 	case 0:
    279 		/*
    280 		 * Nothing changed. If this packet contains data and the
    281 		 * last one didn't, this is probably a data packet following
    282 		 * an ack (normal on an interactive connection) and we send
    283 		 * it compressed.  Otherwise it's probably a retransmit,
    284 		 * retransmitted ack or window probe.  Send it uncompressed
    285 		 * in case the other side missed the compressed version.
    286 		 */
    287 		if (ip->ip_len != cs->cs_ip.ip_len &&
    288 		    ntohs(cs->cs_ip.ip_len) == hlen)
    289 			break;
    290 
    291 		/* (fall through) */
    292 
    293 	case SPECIAL_I:
    294 	case SPECIAL_D:
    295 		/*
    296 		 * actual changes match one of our special case encodings --
    297 		 * send packet uncompressed.
    298 		 */
    299 		goto uncompressed;
    300 
    301 	case NEW_S|NEW_A:
    302 		if (deltaS == deltaA &&
    303 		    deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
    304 			/* special case for echoed terminal traffic */
    305 			changes = SPECIAL_I;
    306 			cp = new_seq;
    307 		}
    308 		break;
    309 
    310 	case NEW_S:
    311 		if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
    312 			/* special case for data xfer */
    313 			changes = SPECIAL_D;
    314 			cp = new_seq;
    315 		}
    316 		break;
    317 	}
    318 
    319 	deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
    320 	if (deltaS != 1) {
    321 		ENCODEZ(deltaS);
    322 		changes |= NEW_I;
    323 	}
    324 	if (th->th_flags & TH_PUSH)
    325 		changes |= TCP_PUSH_BIT;
    326 	/*
    327 	 * Grab the cksum before we overwrite it below.  Then update our
    328 	 * state with this packet's header.
    329 	 */
    330 	deltaA = ntohs(th->th_sum);
    331 	BCOPY(ip, &cs->cs_ip, hlen);
    332 
    333 	/*
    334 	 * We want to use the original packet as our compressed packet.
    335 	 * (cp - new_seq) is the number of bytes we need for compressed
    336 	 * sequence numbers.  In addition we need one byte for the change
    337 	 * mask, one for the connection id and two for the tcp checksum.
    338 	 * So, (cp - new_seq) + 4 bytes of header are needed.  hlen is how
    339 	 * many bytes of the original packet to toss so subtract the two to
    340 	 * get the new packet size.
    341 	 */
    342 	deltaS = cp - new_seq;
    343 	cp = (u_char *)ip;
    344 	if (compress_cid == 0 || comp->last_xmit != cs->cs_id) {
    345 		comp->last_xmit = cs->cs_id;
    346 		hlen -= deltaS + 4;
    347 		cp += hlen;
    348 		*cp++ = changes | NEW_C;
    349 		*cp++ = cs->cs_id;
    350 	} else {
    351 		hlen -= deltaS + 3;
    352 		cp += hlen;
    353 		*cp++ = changes;
    354 	}
    355 	m->m_len -= hlen;
    356 	m->m_data += hlen;
    357 	*cp++ = deltaA >> 8;
    358 	*cp++ = deltaA;
    359 	BCOPY(new_seq, cp, deltaS);
    360 	INCR(sls_compressed)
    361 	return (TYPE_COMPRESSED_TCP);
    362 
    363 	/*
    364 	 * Update connection state cs & send uncompressed packet ('uncompressed'
    365 	 * means a regular ip/tcp packet but with the 'conversation id' we hope
    366 	 * to use on future compressed packets in the protocol field).
    367 	 */
    368 uncompressed:
    369 	BCOPY(ip, &cs->cs_ip, hlen);
    370 	ip->ip_p = cs->cs_id;
    371 	comp->last_xmit = cs->cs_id;
    372 	return (TYPE_UNCOMPRESSED_TCP);
    373 }
    374 
    375 
    376 int
    377 sl_uncompress_tcp(bufp, len, type, comp)
    378 	u_char **bufp;
    379 	int len;
    380 	u_int type;
    381 	struct slcompress *comp;
    382 {
    383 	register u_char *cp;
    384 	register u_int hlen, changes;
    385 	register struct tcphdr *th;
    386 	register struct cstate *cs;
    387 	register struct ip *ip;
    388 
    389 	switch (type) {
    390 
    391 	case TYPE_UNCOMPRESSED_TCP:
    392 		ip = (struct ip *) *bufp;
    393 		if (ip->ip_p >= MAX_STATES)
    394 			goto bad;
    395 		cs = &comp->rstate[comp->last_recv = ip->ip_p];
    396 		comp->flags &=~ SLF_TOSS;
    397 		ip->ip_p = IPPROTO_TCP;
    398 		hlen = ip->ip_hl;
    399 		hlen += ((struct tcphdr *)&((int *)ip)[hlen])->th_off;
    400 		hlen <<= 2;
    401 		BCOPY(ip, &cs->cs_ip, hlen);
    402 		cs->cs_ip.ip_sum = 0;
    403 		cs->cs_hlen = hlen;
    404 		INCR(sls_uncompressedin)
    405 		return (len);
    406 
    407 	default:
    408 		goto bad;
    409 
    410 	case TYPE_COMPRESSED_TCP:
    411 		break;
    412 	}
    413 	/* We've got a compressed packet. */
    414 	INCR(sls_compressedin)
    415 	cp = *bufp;
    416 	changes = *cp++;
    417 	if (changes & NEW_C) {
    418 		/* Make sure the state index is in range, then grab the state.
    419 		 * If we have a good state index, clear the 'discard' flag. */
    420 		if (*cp >= MAX_STATES)
    421 			goto bad;
    422 
    423 		comp->flags &=~ SLF_TOSS;
    424 		comp->last_recv = *cp++;
    425 	} else {
    426 		/* this packet has an implicit state index.  If we've
    427 		 * had a line error since the last time we got an
    428 		 * explicit state index, we have to toss the packet. */
    429 		if (comp->flags & SLF_TOSS) {
    430 			INCR(sls_tossed)
    431 			return (0);
    432 		}
    433 	}
    434 	cs = &comp->rstate[comp->last_recv];
    435 	hlen = cs->cs_ip.ip_hl << 2;
    436 	th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen];
    437 	th->th_sum = htons((*cp << 8) | cp[1]);
    438 	cp += 2;
    439 	if (changes & TCP_PUSH_BIT)
    440 		th->th_flags |= TH_PUSH;
    441 	else
    442 		th->th_flags &=~ TH_PUSH;
    443 
    444 	switch (changes & SPECIALS_MASK) {
    445 	case SPECIAL_I:
    446 		{
    447 		register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
    448 		th->th_ack = htonl(ntohl(th->th_ack) + i);
    449 		th->th_seq = htonl(ntohl(th->th_seq) + i);
    450 		}
    451 		break;
    452 
    453 	case SPECIAL_D:
    454 		th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
    455 				   - cs->cs_hlen);
    456 		break;
    457 
    458 	default:
    459 		if (changes & NEW_U) {
    460 			th->th_flags |= TH_URG;
    461 			DECODEU(th->th_urp)
    462 		} else
    463 			th->th_flags &=~ TH_URG;
    464 		if (changes & NEW_W)
    465 			DECODES(th->th_win)
    466 		if (changes & NEW_A)
    467 			DECODEL(th->th_ack)
    468 		if (changes & NEW_S)
    469 			DECODEL(th->th_seq)
    470 		break;
    471 	}
    472 	if (changes & NEW_I) {
    473 		DECODES(cs->cs_ip.ip_id)
    474 	} else
    475 		cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
    476 
    477 	/*
    478 	 * At this point, cp points to the first byte of data in the
    479 	 * packet.  If we're not aligned on a 4-byte boundary, copy the
    480 	 * data down so the ip & tcp headers will be aligned.  Then back up
    481 	 * cp by the tcp/ip header length to make room for the reconstructed
    482 	 * header (we assume the packet we were handed has enough space to
    483 	 * prepend 128 bytes of header).  Adjust the length to account for
    484 	 * the new header & fill in the IP total length.
    485 	 */
    486 	len -= (cp - *bufp);
    487 	if (len < 0)
    488 		/* we must have dropped some characters (crc should detect
    489 		 * this but the old slip framing won't) */
    490 		goto bad;
    491 
    492 	if ((int)cp & 3) {
    493 		if (len > 0)
    494 			(void) ovbcopy(cp, (caddr_t)((int)cp &~ 3), len);
    495 		cp = (u_char *)((int)cp &~ 3);
    496 	}
    497 	cp -= cs->cs_hlen;
    498 	len += cs->cs_hlen;
    499 	cs->cs_ip.ip_len = htons(len);
    500 	BCOPY(&cs->cs_ip, cp, cs->cs_hlen);
    501 	*bufp = cp;
    502 
    503 	/* recompute the ip header checksum */
    504 	{
    505 		register u_short *bp = (u_short *)cp;
    506 		for (changes = 0; hlen > 0; hlen -= 2)
    507 			changes += *bp++;
    508 		changes = (changes & 0xffff) + (changes >> 16);
    509 		changes = (changes & 0xffff) + (changes >> 16);
    510 		((struct ip *)cp)->ip_sum = ~ changes;
    511 	}
    512 	return (len);
    513 bad:
    514 	comp->flags |= SLF_TOSS;
    515 	INCR(sls_errorin)
    516 	return (0);
    517 }
    518