📄 slhc.c
字号:
/*
* Routines to compress and uncompress 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 for KA9Q Internet Software Package by
* Katie Stevens (dkstevens@ucdavis.edu)
* University of California, Davis
* Computing Services
* - 01-31-90 initial adaptation (from 1.19)
* PPP.05 02-15-90 [ks]
* PPP.08 05-02-90 [ks] use PPP protocol field to signal compression
* PPP.15 09-90 [ks] improve mbuf handling
* PPP.16 11-02 [karn] substantially rewritten to use NOS facilities
*
* - Feb 1991 Bill_Simpson@um.cc.umich.edu
* variable number of conversation slots
* allow zero or one slots
* separate routines
* status display
*/
#include "global.h"
#include "mbuf.h"
#include "internet.h"
#include "ip.h"
#include "tcp.h"
#include "slhc.h"
static uint8 *encode(uint8 *cp,uint n);
static long decode(struct mbuf **bpp);
/* Initialize compression data structure
* slots must be in range 0 to 255 (zero meaning no compression)
*/
struct slcompress *
slhc_init(rslots,tslots)
int rslots;
int tslots;
{
uint i;
struct cstate *ts;
struct slcompress *comp;
comp = callocw( 1, sizeof(struct slcompress) );
if ( rslots > 0 && rslots < 256 ) {
comp->rstate = callocw( rslots, sizeof(struct cstate) );
comp->rslot_limit = rslots - 1;
}
if ( tslots > 0 && tslots < 256 ) {
comp->tstate = callocw( tslots, sizeof(struct cstate) );
comp->tslot_limit = tslots - 1;
}
comp->xmit_oldest = 0;
comp->xmit_current = 255;
comp->recv_current = 255;
if ( tslots > 0 ) {
ts = comp->tstate;
for(i = comp->tslot_limit; i > 0; --i){
ts[i].this = i;
ts[i].next = &(ts[i - 1]);
}
ts[0].next = &(ts[comp->tslot_limit]);
ts[0].this = 0;
}
return comp;
}
/* Free a compression data structure */
void
slhc_free(comp)
struct slcompress *comp;
{
if ( comp == NULL )
return;
if ( comp->rstate != NULL )
free( comp->rstate );
if ( comp->tstate != NULL )
free( comp->tstate );
free( comp );
}
/* Encode a number */
static uint8 *
encode(cp,n)
uint8 *cp;
uint n;
{
if(n >= 256 || n == 0){
*cp++ = 0;
cp = put16(cp,n);
} else {
*cp++ = n;
}
return cp;
}
/* Decode a number */
static long
decode(bpp)
struct mbuf **bpp;
{
int x;
x = PULLCHAR(bpp);
if(x == 0){
return pull16(bpp); /* pull16 returns -1 on error */
} else {
return (long)x; /* -1 if PULLCHAR returned error */
}
}
int
slhc_compress(comp, bpp, compress_cid)
struct slcompress *comp;
struct mbuf **bpp;
int compress_cid;
{
struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
struct cstate *lcs = ocs;
struct cstate *cs = lcs->next;
uint hlen,iplen;
struct tcp *oth;
unsigned long deltaS, deltaA;
uint changes = 0;
uint8 new_seq[16];
uint8 *cp = new_seq;
struct tcp th;
struct ip iph;
struct mbuf *copy;
/* Copy TCP/IP header, allowing for worst-case options in both
* Using dup_p seemed to result in unexplained
* memory leaks -- but only some of the time. Must find out why.
*/
/* dup_p(©,*bpp,0,IPLEN+IP_MAXOPT+TCPLEN+TCP_MAXOPT); */
copy = copy_p(*bpp,IPLEN+IP_MAXOPT+TCPLEN+TCP_MAXOPT);
/* Peek at IP header */
iplen = hlen = ntohip(&iph,©);
/* Bail if this packet isn't TCP, or is an IP fragment */
if(iph.protocol != TCP_PTCL || iph.offset != 0 || iph.flags.mf){
/* Send as regular IP */
if(iph.protocol != TCP_PTCL)
comp->sls_o_nontcp++;
else
comp->sls_o_tcp++;
free_p(©);
return SL_TYPE_IP;
}
/* Extract TCP header */
hlen += ntohtcp(&th,©);
free_p(©); /* Done with copy */
/* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or
* some other control bit is set, or has options).
*/
if(th.flags.syn || th.flags.fin || th.flags.rst || !th.flags.ack
|| th.flags.mss || th.flags.wscale || th.flags.tstamp){
/* TCP connection stuff; send as regular IP */
comp->sls_o_tcp++;
return SL_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.
*
* States are kept in a circularly linked list with
* xmit_oldest 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.
*/
for ( ; ; ) {
if( iph.source == cs->cs_ip.source
&& iph.dest == cs->cs_ip.dest
&& th.source == cs->cs_tcp.source
&& th.dest == cs->cs_tcp.dest)
goto found;
/* if current equal oldest, at end of list */
if ( cs == ocs )
break;
lcs = cs;
cs = cs->next;
comp->sls_o_searches++;
};
/*
* 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
* xmit_oldest to update the lru linkage.
*/
comp->sls_o_misses++;
comp->xmit_oldest = lcs->this;
goto uncompressed;
found:
/*
* Found it -- move to the front on the connection list.
*/
if(lcs == ocs) {
/* found at most recently used */
} else if (cs == ocs) {
/* found at least recently used */
comp->xmit_oldest = lcs->this;
} else {
/* more than 2 elements */
lcs->next = cs->next;
cs->next = ocs->next;
ocs->next = cs;
}
/*
* Make sure that only what we expect to change changed.
* Check the following:
* IP protocol version, header length & type of service.
* The "Don't fragment" bit.
* The time-to-live field.
* The TCP header length.
* IP options, if any.
* TCP options, if any.
* If any of these things are different between the previous &
* current datagram, we send the current datagram `uncompressed'.
*/
oth = &cs->cs_tcp;
if(iph.version != cs->cs_ip.version || iph.optlen != cs->cs_ip.optlen
|| iph.tos != cs->cs_ip.tos
|| iph.flags.df != cs->cs_ip.flags.df
|| iph.ttl != cs->cs_ip.ttl
|| (iph.optlen > 0 && memcmp(iph.options,cs->cs_ip.options,iph.optlen) != 0)){
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).
*/
if(th.flags.urg){
deltaS = th.up;
cp = encode(cp,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 RFC793
* doesn't prohibit the change so we have to deal
* with it. */
goto uncompressed;
}
if((deltaS = th.wnd - oth->wnd) != 0){
cp = encode(cp,deltaS);
changes |= NEW_W;
}
if((deltaA = th.ack - oth->ack) != 0L){
if(deltaA > 0x0000ffff)
goto uncompressed;
cp = encode(cp,deltaA);
changes |= NEW_A;
}
if((deltaS = th.seq - oth->seq) != 0L){
if(deltaS > 0x0000ffff)
goto uncompressed;
cp = encode(cp,deltaS);
changes |= NEW_S;
}
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -