home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 8
/
FreshFishVol8-CD2.bin
/
bbs
/
comm
/
amitcp-3.0ß2.lha
/
AmiTCP
/
src
/
devs
/
rhslip
/
cslip.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-01-28
|
19KB
|
665 lines
/* $Id: cslip.c,v 1.1 1993/08/17 00:00:00 rhialto Exp $
*
* COMPRESSED TCP HEADERS
*
* Reduce the 40 byte TCP header to as little as 5 or even 3 bytes
* (of which 2 bytes are the original TCP checksum).
*
* See RFC 1144 "Compression of TCP/IP Headers for Low-Speed Serial
* Links", V. Jacobson, February 1990, for the exciting details.
*
* The code in this file is derived from and very similar to the example
* implementation provided in RFC 1144, and is therefore sort-of covered
* by its copyright:
*
* Copyright (C) 1989 Regents of the University of California.
*
* This adaptation by Olaf 'Rhialto' Seibert.
* - fixed a number of sizeof(int) == 4 assumptions.
* - allow a few packets to be sent compressed before it is officially
* enabled, so as to trigger the other side into compressed mode.
*
* Memory usage: < 3K of 68000 code, plus ~ 4K buffer space per line.
*/
#include <string.h>
#include "slip_device.h"
#if DEBUG
#include "syslog.h"
#else
#define debug(x)
#endif
#define calloc(n,m) AllocVec((n) * (m), MEMF_CLEAR)
#define free(p) FreeVec(p)
#ifndef EXEC_MEMORY_H
#include <exec/memory.h>
#endif
#include <clib/exec_protos.h>
#ifdef __SASC
#include <pragmas/exec_pragmas.h>
#endif
#define ihl(v_ihl) lonibble(v_ihl) /* extract ip header length */
#define tcp_offset(tcp) ((u_char)(tcp)->offset >> DSHIFT)
/* Appendix A.2 Compression */
u_char
sl_compress_tcp(m, comp)
struct mbuf *m;
struct slcompress *comp;
{
register struct cstate *cs = comp->last_cs->cs_next;
register struct ip_header *ip = mtod(m, struct ip_header *);
register u_int hlen = ihl(ip->v_ihl);
register struct tcp_header *oth; /* last TCP header */
register struct tcp_header *th; /* current TCP header */
register u_long 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;
debug((">sl_compress_tcp %lx %d\n", m->m_off, m->m_len));
/* Appendix B.2 Backwards compatible SLIP servers */
#ifdef TRYCOUNT
if (comp->on == 0) {
debug(("<sl_compress_tcp: not on\n"));
return TYPE_IP;
}
#else
if ((comp->flags & SLF_ON) == 0) {
debug(("<sl_compress_tcp: not on\n"));
return TYPE_IP;
}
#endif
if (ip->protocol != TCP_PTCL) { /* Rhialto */
comp->sls_o_nontcp++;
debug(("<sl_compress_tcp: not tcp\n"));
return TYPE_IP;
}
/*
* 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->fl_offs & htons(0x3fff)) || m->m_len < 40) {
comp->sls_o_tcp++;
debug(("<sl_compress_tcp: tcp frag\n"));
return TYPE_IP;
}
th = (struct tcp_header *)&((int32 *) ip)[hlen];
if ((th->flags & (SYN | FIN | RST | ACK)) != ACK) {
comp->sls_o_tcp++;
debug(("<sl_compress_tcp: tcp flags\n"));
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->source != cs->cs_ip.source ||
ip->dest != cs->cs_ip.dest ||
*(int32 *)th != cs->cs_hdr4[ihl(cs->cs_ip.v_ihl)]) {
/*
* 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;
comp->sls_o_searches++;
if (ip->source == cs->cs_ip.source &&
ip->dest == cs->cs_ip.dest &&
*(int32 *)th == cs->cs_hdr4[ihl(cs->cs_ip.v_ihl)])
goto found;
} while (cs != lastcs);
/*
* 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.
*/
comp->sls_o_misses++;
comp->last_cs = lcs;
hlen += tcp_offset(th); /* add tcp header length */
hlen *= sizeof(int32);
goto uncompressed;
found:
/* Found it -- move to the front on the connection list. */
if (lastcs == cs)
comp->last_cs = lcs;
else {
lcs->cs_next = cs->cs_next;
cs->cs_next = lastcs->cs_next;
lastcs->cs_next = cs;
}
}
debug((" sl_compress_tcp: found\n"));
#ifdef TRYCOUNT
/*
* For found connections, try a few TCP_UNCOMPRESSED packets even if
* we're not SL_ON yet. (This doesn't happen on new connections;
* they will be TCP_UNCOMPRESSED regardless - FIXME.)
*/
if (comp->on > 0) {
comp->on--;
debug((" sl_compress_tcp: trycount %d\n", comp->on));
goto uncompressed;
}
#endif
/*
* Make sure that only what we expect to change changed. The 1st
* 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".
*/
oth = (struct tcp_header *) &cs->cs_hdr4[hlen];
deltaS = hlen;
hlen += tcp_offset(th);
hlen *= sizeof(int32);
if (((u_short *) ip)[0] != ((u_short *) &cs->cs_ip)[0] || /* v_ihl,tos */
((u_short *) ip)[3] != ((u_short *) &cs->cs_ip)[3] || /* fl_offs */
((u_short *) ip)[4] != ((u_short *) &cs->cs_ip)[4] || /* ttl, protocol */
th->offset != oth->offset ||
(deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) * sizeof(int32))) ||
(tcp_offset(th) > 5 && BCMP(th + 1, oth + 1, (tcp_offset(th) - 5) * sizeof(int32)))) {
debug((" sl_compress_tcp: header changed\n"));
goto uncompressed;
}
/*
* Figure out which of the changing fields changed. The receiver
* expects changes in the order: urgent, window, ack, seq.
*/
if (th->flags & URG) {
deltaS = ntohs(th->up);
ENCODEZ(deltaS);
changes |= NEW_U;
} else if (th->up != oth->up) {
/*
* argh! URG not set but urp changed -- a sensible
* implementation should never do this but RFC 793 doesn't
* prohibit the change so we have to deal with it.
*/
debug((" sl_compress_tcp: up changed\n"));
goto uncompressed;
}
if (deltaS = (u_short) (ntohs(th->wnd) - ntohs(oth->wnd))) {
ENCODE(deltaS);
changes |= NEW_W;
}
if (deltaA = ntohs(th->ack) - ntohs(oth->ack)) {
if (deltaA > 0xffff)
goto uncompressed;
ENCODE(deltaA);
changes |= NEW_A;
}
if (deltaS = ntohs(th->seq) - ntohs(oth->seq)) {
if (deltaS > 0xffff)
goto uncompressed;
ENCODE(deltaS);
changes |= NEW_S;
}
/*
* Look for the special-case encodings.
*/
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.
*/
if (ip->length != cs->cs_ip.length &&
ntohs(cs->cs_ip.length) == hlen)
break;
debug((" sl_compress_tcp: retransmission ->\n"));
/* fall through */
case