/*
* Copyright (c) 2000 by Sun Microsystems, Inc.
* All rights reserved.
*
* Routines to compress and uncompess tcp packets (for transmission
* over low speed serial lines.
*
* Copyright (c) 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
* - Initial distribution.
*
* Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au,
* so that the entire packet being decompressed doesn't have
* to be in contiguous memory (just the compressed header).
*/
/*
* This version is used under STREAMS in Solaris 2
*
* $Id: vjcompress.c,v 1.10 1999/09/15 23:49:06 masputra Exp $
*/
#include <sys/sysmacros.h>
#include <netinet/in_systm.h>
#include <net/ppp_defs.h>
#include <net/vjcompress.h>
#pragma ident "%Z%%M% %I% %E% SMI"
#ifndef VJ_NO_STATS
#else
#endif
/*
* I'd like to use offsetof(struct ip,ip_hl) and offsetof(struct
* tcp,th_off), but these are bitfields.
*/
/*
* vj_compress_init()
*/
void
{
register uint_t i;
if (max_state == -1) {
}
for (i = max_state; i > 0; --i) {
}
}
/*
* ENCODE encodes a number that is known to be non-zero. ENCODEZ
* checks for zero (since zero has to be encoded in the long, 3 byte
* form).
*/
#define ENCODE(n) { \
if ((ushort_t)(n) >= 256) { \
*cp++ = 0; \
cp += 2; \
} else { \
*cp++ = (n) & 0xff; \
} \
}
#define ENCODEZ(n) { \
*cp++ = 0; \
cp += 2; \
} else { \
*cp++ = (n) & 0xff; \
} \
}
#define DECODEL(f) { \
if (*cp == 0) { \
cp += 3; \
} else { \
} \
}
#define DECODES(f) { \
if (*cp == 0) { \
cp += 3; \
} else { \
} \
}
#define DECODEU(f) { \
if (*cp == 0) { \
cp += 3; \
} else { \
} \
}
{
/*
* Bail if this is an IP fragment or if the TCP packet isn't
* `compressible' (i.e., ACK isn't set or some other control bit is
* set). (We assume that the caller has already made sure the
* packet is IP proto TCP)
*/
return (TYPE_IP);
}
return (TYPE_IP);
}
return (TYPE_IP);
}
/*
* Packet is compressible -- we're going to send either a
* COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need
* to locate (or create) the connection state. Special case the
* most recently used connection since it's most likely to be used
* again & we don't have to do any reordering if it's used.
*/
/*
* Wasn't the first -- search for it.
*
* States are kept in a circularly linked list with
* last_cs pointing to the end of the list. The
* list is kept in lru order by moving a state to the
* head of the list whenever it is referenced. Since
* the list is short and, empirically, the connection
* we want is almost always near the front, we locate
* states via linear search. If we don't find a state
* for the datagram, the oldest state is (re-)used.
*/
do {
*(int *)th == ((int *)
goto found;
}
/*
* Didn't find it -- re-use oldest cstate. Send an
* uncompressed packet that tells the other side what
* connection number we're using for this conversation.
* Note that since the state list is circular, the oldest
* state points to the newest and we only need to set
* last_cs to update the lru linkage.
*/
goto uncompressed;
/*
* Found it -- move to the front on the connection list.
*/
} else {
}
}
/*
* Make sure that only what we expect to change changed. The first
* line of the `if' checks the IP protocol version, header length &
* type of service. The 2nd line checks the "Don't fragment" bit.
* The 3rd line checks the time-to-live and protocol (the protocol
* check is unnecessary but costless). The 4th line checks the TCP
* header length. The 5th line checks IP options, if any. The 6th
* line checks TCP options, if any. If any of these things are
* different between the previous & current datagram, we send the
* current datagram `uncompressed'.
*/
/* Used to check for IP options. */
(deltaS > 5 &&
goto uncompressed;
}
/*
* Figure out which of the changing fields changed. The
* receiver expects changes in the order: urgent, window,
* ack, seq (the order minimizes the number of temporaries
* needed in this section of code).
*/
/*
* argh! URG not set but urp changed -- a sensible
* implementation should never do this but RFC793
* doesn't prohibit the change so we have to deal
* with it
*/
goto uncompressed;
}
}
if (deltaA > 0xffff) {
goto uncompressed;
}
}
if (deltaS > 0xffff) {
goto uncompressed;
}
}
switch (changes) {
case 0:
/*
* Nothing changed. If this packet contains data and the
* last one didn't, this is probably a data packet following
* an ack (normal on an interactive connection) and we send
* it compressed. Otherwise it's probably a retransmit,
* retransmitted ack or window probe. Send it uncompressed
* in case the other side missed the compressed version.
*/
break;
}
/* (otherwise fall through) */
/* FALLTHRU */
case SPECIAL_I:
case SPECIAL_D:
/*
* actual changes match one of our special case encodings --
* send packet uncompressed.
*/
goto uncompressed;
/*
* special case for echoed terminal traffic
*/
}
break;
case NEW_S:
/*
* special case for data xfer
*/
}
break;
}
if (deltaS != 1) {
}
changes |= TCP_PUSH_BIT;
}
/*
* Grab the cksum before we overwrite it below. Then update our
* state with this packet's header.
*/
/*
* We want to use the original packet as our compressed packet.
* (cp - new_seq) is the number of bytes we need for compressed
* sequence numbers. In addition we need one byte for the change
* mask, one for the connection id and two for the tcp checksum.
* So, (cp - new_seq) + 4 bytes of header are needed. thlen is how
* many bytes of the original packet to toss so subtract the two to
* get the new packet size.
*/
} else {
}
return (TYPE_COMPRESSED_TCP);
/*
* Update connection state cs & send uncompressed packet (that is,
* to use on future compressed packets in the protocol field).
*/
return (TYPE_UNCOMPRESSED_TCP);
}
/*
* vj_uncompress_err()
*
* Called when we may have missed a packet.
*/
void
{
}
/*
* vj_uncompress_uncomp()
*
* "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP.
*/
int
{
return (0);
}
return (1);
}
/*
* vj_uncompress_tcp()
*
* Uncompress a packet of type TYPE_COMPRESSED_TCP.
* The packet starts at buf and is of total length total_len.
* The first buflen bytes are at buf; this must include the entire
* of the VJ header, with a pointer to the uncompressed IP header
* in *hdrp and its length in *hlenp.
*/
int
{
/*
* Make sure the state index is in range, then grab the state.
* If we have a good state index, clear the 'discard' flag.
*/
if (*cp >= MAX_STATES) {
goto bad;
}
} else {
/*
* this packet has an implicit state index. If we've
* had a line error since the last time we got an
* explicit state index, we have to toss the packet
*/
return (-1);
}
}
cp += 2;
if (changes & TCP_PUSH_BIT) {
} else {
}
switch (changes & SPECIALS_MASK) {
case SPECIAL_I:
{
register uint32_t i;
}
break;
case SPECIAL_D:
break;
default:
} else {
}
}
}
}
break;
}
} else {
}
/*
* At this point, cp points to the first byte of data in the
* packet. Fill in the IP total length and update the IP
* header checksum.
*/
if (buflen < 0) {
/*
* we must have dropped some characters (crc should detect
* this but the old slip framing won't)
*/
goto bad;
}
/*
* recompute the ip header checksum
*/
}
return (vjlen);
bad:
return (-1);
}