home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Datafile PD-CD 3
/
PDCD_3.iso
/
internet
/
tcpipsrc
/
Drivers
/
c
/
sl_compres
< prev
next >
Wrap
Text File
|
1995-02-20
|
29KB
|
793 lines
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "global.h"
#include "timer.h"
#include "ip.h"
#include "bbc.h"
#include "terminal.h"
#include "sl_compres.h"
#define BCOPY(src, dst, amt) memcpy(dst, src, amt)
#define OVBCOPY(src, dst, amt) memmove(dst, src, amt)
#define BCMP(src, dst, amt) memcmp(dst, src, amt)
#define bzero(dst, amt) memset(dst, 0, amt);
#define mtod(m, t) ((t)(m->data))
typedef u_long tcp_seq;
#define IPPROTO_TCP 6 /* tcp */
/*
* TCP header.
* Per RFC 793, September, 1981.
*/
struct net_tcphdr {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
u_int th_x2:4; /* (unused) */
u_int th_off:4; /* data offset */
u_int th_flags:8;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
u_int th_win:16; /* window */
u_short th_sum; /* checksum */
u_short th_urp; /* urgent pointer */
};
static u_short ntohs(u_short n)
{
return ((n & 0xff) << 8) |
((n >> 8) & 0xff);
}
static u_long ntohl(u_long n)
{
return ((n & 0xff) << 24) |
((n & 0xff00) << 8) |
((n & 0xff0000) >> 8) |
((n & 0xff000000) >> 24);
}
/*
#define ntohl(a) \
((a >> 24) | \
((a & 0xff0000) >> 8) | \
((a & 0xff00) << 8) | \
(a << 24) )
*/
#define htonl ntohl
/*
#define ntohs(a) \
((( a & 0xff00) >> 8) | \
(( a & 0xff) << 8) )
*/
#define htons ntohs
/*
* See "sl_compress.h" for additional info
*/
/*
* The following macros are used to encode and decode numbers. They all
* assume that `cp' points to a buffer where the next byte encoded (decoded)
* is to be stored (retrieved). Since the decode routines do arithmetic,
* they have to convert from and to network byte order.
*/
/*
* ENCODE encodes a number that is known to be non-zero. ENCODEZ checks for
* zero (zero has to be encoded in the long, 3 byte form).
*/
#define ENCODE(n) { \
if ((u_short)(n) >= 256) { \
*cp++ = 0; \
cp[1] = (n); \
cp[0] = (n) >> 8; \
cp += 2; \
} else { \
*cp++ = (n); \
} \
}
#define ENCODEZ(n) { \
if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
*cp++ = 0; \
cp[1] = (n); \
cp[0] = (n) >> 8; \
cp += 2; \
} else { \
*cp++ = (n); \
} \
}
/*
* DECODEL takes the (compressed) change at byte cp and adds it to the
* current value of packet field 'f' (which must be a 4-byte (long) integer
* in network byte order). DECODES does the same for a 2-byte (short) field.
* DECODEU takes the change at cp and stuffs it into the (short) field f.
* 'cp' is updated to point to the next field in the compressed header.
*/
#define DECODEL(f) { \
if (*cp == 0) {\
(f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \
cp += 3; \
} else { \
(f) = htonl(ntohl(f) + (u_long)*cp++); \
} \
}
#define DECODES(f) { \
if (*cp == 0) {\
(f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \
cp += 3; \
} else { \
(f) = htons(ntohs(f) + (u_long)*cp++); \
} \
}
#define DECODEU(f) { \
if (*cp == 0) {\
(f) = htons((cp[1] << 8) | cp[2]); \
cp += 3; \
} else { \
(f) = htons((u_long)*cp++); \
} \
}
/*
A.2 Compression
This routine looks daunting but isn't really. The code splits into four
approximately equal sized sections: The first quarter manages a
circularly linked, least-recently-used list of `active' TCP
connections./47/ The second figures out the sequence/ack/window/urg
changes and builds the bulk of the compressed packet. The third handles
the special-case encodings. The last quarter does packet ID and
connection ID encoding and replaces the original packet header with the
compressed header.
The arguments to this routine are a pointer to a packet to be
compressed, a pointer to the compression state data for the serial line,
and a flag which enables or disables connection id (C bit) compression.
Compression is done `in-place' so, if a compressed packet is created,
both the start address and length of the incoming packet (the off and
len fields of m) will be updated to reflect the removal of the original
header and its replacement by the compressed header. If either a
compressed or uncompressed packet is created, the compression state is
updated. This routines returns the packet type for the transmit framer
(TYPE_IP, TYPE_UNCOMPRESSED_TCP or TYPE_COMPRESSED_TCP).
Because 16 and 32 bit arithmetic is done on various header fields, the
incoming IP packet must be aligned appropriately (e.g., on a SPARC, the
IP header is aligned on a 32-bit boundary). Substantial changes would
have to be made to the code below if this were not true (and it would
probably be cheaper to byte copy the incoming header to somewhere
correctly aligned than to make those changes).
Note that the outgoing packet will be aligned arbitrarily (e.g., it
could easily start on an odd-byte boundary).
*/
u_char
sl_compress_tcp(char **bufp, int *len, struct slcompress *comp, int compress_cid)
{
register struct cstate *cs = comp->last_cs->cs_next;
register struct net_ip *ip = (struct net_ip *)*bufp;
register u_int hlen = ip->ip_hl;
register struct net_tcphdr *oth; /* last TCP header */
register struct net_tcphdr *th; /* current TCP header */
/*
----------------------------
47. The two most common operations on the connection list are a `find'
that terminates at the first entry (a new packet for the most recently
used connection) and moving the last entry on the list to the head of
the list (the first packet from a new connection). A circular list
efficiently handles these two operations.
*/
register u_int deltaS, deltaA; /* general purpose temporaries */
register u_int changes = 0; /* change mask */
u_char new_seq[16]; /* changes from last to current */
register u_char *cp = new_seq;
/*
* 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).
*/
if ((ip->ip_off & htons(0x3fff)) || *len < 40)
return (TYPE_IP);
th = (struct net_tcphdr *) & ((int *) ip)[hlen];
if ((th->th_flags & (TH_SYN | TH_FIN | TH_RST | TH_ACK)) != TH_ACK)
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.
*/
if (ip->ip_src != cs->cs_ip.ip_src ||
ip->ip_dst != cs->cs_ip.ip_dst ||
*(int *) th != ((int *) &cs->cs_ip)[cs->cs_ip.ip_hl]) {
/*
* 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.
*/
register struct cstate *lcs;
register struct cstate *lastcs = comp->last_cs;
do {
lcs = cs;
cs = cs->cs_next;
if (ip->ip_src == cs