home *** CD-ROM | disk | FTP | other *** search
- #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->cs_ip.ip_src
- && ip->ip_dst == cs->cs_ip.ip_dst
- && *(int *) th == ((int *) &cs->cs_ip)[cs->cs_ip.ip_hl])
- 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->last_cs = lcs;
- hlen += th->th_off;
- hlen <<= 2;
- 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;
- }
- }
- /*
- * 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'.
- */
- oth = (struct net_tcphdr *) & ((int *) &cs->cs_ip)[hlen];
- deltaS = hlen;
- hlen += th->th_off;
- hlen <<= 2;
-
- if (((u_short *) ip)[0] != ((u_short *) &cs->cs_ip)[0] ||
- ((u_short *) ip)[3] != ((u_short *) &cs->cs_ip)[3] ||
- ((u_short *) ip)[4] != ((u_short *) &cs->cs_ip)[4] ||
- th->th_off != oth->th_off ||
- (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
- (th->th_off > 5 && BCMP(th + 1, oth + 1, (th->th_off - 5) << 2)))
- goto uncompressed;
-
- /*
- * Figure out which of the changing fields changed. The receiver
- * expects changes in the order: urgent, window, ack, seq.
- */
- if (th->th_flags & TH_URG) {
- deltaS = ntohs(th->th_urp);
- ENCODEZ(deltaS);
- changes |= NEW_U;
- } else if (th->th_urp != oth->th_urp)
- /*
- * 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 = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win)), deltaS!=0) {
- ENCODE(deltaS);
- changes |= NEW_W;
- }
- if (deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack), deltaA!=0) {
- if (deltaA > 0xffff)
- goto uncompressed;
- /*
- twprintf(">< %.8x + %d", ntohl(oth->th_ack), deltaA);
- twprintf(" = %.8x\r\n", ntohl(th->th_ack));
- */
- ENCODE(deltaA);
- changes |= NEW_A;
- }
- if (deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq), deltaS!=0) {
- 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->ip_len != cs->cs_ip.ip_len &&
- ntohs(cs->cs_ip.ip_len) == hlen)
- break;
-
- /* (fall through) */
-
- case SPECIAL_I:
- case SPECIAL_D:
- /*
- * Actual changes match one of our special case encodings --
- * send packet uncompressed.
- */
- goto uncompressed;
-
- case NEW_S | NEW_A:
- if (deltaS == deltaA &&
- deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
- /* special case for echoed terminal traffic */
- changes = SPECIAL_I;
- cp = new_seq;
- }
- break;
-
- case NEW_S:
- if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
- /* special case for data xfer */
- changes = SPECIAL_D;
- cp = new_seq;
- }
- break;
- }
- deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
- if (deltaS != 1) {
- ENCODEZ(deltaS);
- changes |= NEW_I;
- }
- if (th->th_flags & TH_PUSH)
- changes |= TCP_PUSH_BIT;
- /*
- * Grab the cksum before we overwrite it below. Then update our
- * state with this packet's header.
- */
- deltaA = ntohs(th->th_sum);
- BCOPY(ip, &cs->cs_ip, hlen);
-
- /*
- * 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. hlen is how many bytes
- * of the original packet to toss so subtract the two to get the new
- * packet size.
- */
- deltaS = cp - new_seq;
- cp = (u_char *) ip;
- if (compress_cid == 0 || comp->last_xmit != cs->cs_id) {
- comp->last_xmit = cs->cs_id;
- hlen -= deltaS + 4;
- cp += hlen;
- *cp++ = changes | NEW_C;
- *cp++ = cs->cs_id;
- } else {
- hlen -= deltaS + 3;
- cp += hlen;
- *cp++ = changes;
- }
- *len -= hlen;
- *bufp += hlen;
- *cp++ = deltaA >> 8;
- *cp++ = deltaA;
-
- BCOPY(new_seq, cp, deltaS);
- return (TYPE_COMPRESSED_TCP);
-
- uncompressed:
- /*
- * Update connection state cs & send uncompressed packet
- * ('uncompressed' means a regular ip/tcp packet but with the
- * 'conversation id' we hope to use on future compressed packets in
- * the protocol field).
- */
- BCOPY(ip, &cs->cs_ip, hlen);
- ip->ip_p = cs->cs_id;
- comp->last_xmit = cs->cs_id;
- return (TYPE_UNCOMPRESSED_TCP);
- }
-
-
- /*
- A.3 Decompression
-
- This routine decompresses a received packet. It is called with a
- pointer to the packet, the packet length and type, and a pointer to the
- compression state structure for the incoming serial line. It returns a
- pointer to the resulting packet or zero if there were errors in the
- incoming packet. If the packet is COMPRESSED_TCP or UNCOMPRESSED_TCP,
- the compression state will be updated.
-
- The new packet will be constructed in-place. That means that there must
- be 128 bytes of free space in front of bufp to allow room for the
- reconstructed IP and TCP headers. The reconstructed packet will be
- aligned on a 32-bit boundary.
- */
- u_char *
- sl_uncompress_tcp(char *bufp, int *len, u_int type, struct slcompress *comp)
- {
- register u_char *cp;
- register u_int hlen, changes;
- register struct net_tcphdr *th;
- register struct cstate *cs;
- register struct net_ip *ip;
-
- switch (type) {
-
- case TYPE_ERROR:
- default:
- goto bad;
-
- case TYPE_IP:
- return (bufp);
-
- case TYPE_UNCOMPRESSED_TCP:
- /*
- * Locate the saved state for this connection. If the state
- * index is legal, clear the 'discard' flag.
- */
- ip = (struct net_ip *) bufp;
- if (ip->ip_p >= MAX_STATES)
- goto bad;
-
- cs = &comp->rstate[comp->last_recv = ip->ip_p];
- comp->flags &= ~SLF_TOSS;
- /*
- * Restore the IP protocol field then save a copy of this
- * packet header. (The checksum is zeroed in the copy so we
- * don't have to zero it each time we process a compressed
- * packet.
- */
- ip->ip_p = IPPROTO_TCP;
- hlen = ip->ip_hl;
- hlen += ((struct net_tcphdr *) & ((int *) ip)[hlen])->th_off;
- hlen <<= 2;
- BCOPY(ip, &cs->cs_ip, hlen);
- cs->cs_ip.ip_sum = 0;
- cs->cs_hlen = hlen;
- return (bufp);
-
- case TYPE_COMPRESSED_TCP:
- break;
- }
- /* We've got a compressed packet. */
- cp = bufp;
- changes = *cp++;
- if (changes & NEW_C) {
- /*
- * 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;
-
- comp->flags &= ~SLF_TOSS;
- comp->last_recv = *cp++;
- } 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.
- */
- if (comp->flags & SLF_TOSS)
- return ((u_char *) 0);
- }
- /*
- * Find the state then fill in the TCP checksum and PUSH bit.
- */
- cs = &comp->rstate[comp->last_recv];
- hlen = cs->cs_ip.ip_hl << 2;
- th = (struct net_tcphdr *) & ((u_char *) &cs->cs_ip)[hlen];
- th->th_sum = htons( ((*cp & 0xff)<<8) | (cp[1]&0xff) );
- cp += 2;
- if (changes & TCP_PUSH_BIT)
- th->th_flags |= TH_PUSH;
- else
- th->th_flags &= ~TH_PUSH;
-
- /*
- * Fix up the state's ack, seq, urg and win fields based on the
- * changemask.
- */
- switch (changes & SPECIALS_MASK) {
- case SPECIAL_I:
- {
- register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
- th->th_ack = htonl(ntohl(th->th_ack) + i);
- th->th_seq = htonl(ntohl(th->th_seq) + i);
- }
- break;
-
- case SPECIAL_D:
- th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
- - cs->cs_hlen);
- break;
-
- default:
- if (changes & NEW_U) {
- th->th_flags |= TH_URG;
- DECODEU(th->th_urp)
- } else
- th->th_flags &= ~TH_URG;
- if (changes & NEW_W)
- DECODES(th->th_win)
- if (changes & NEW_A)
- /* {
- int offset;
- offset = (*cp==0)?(cp[1]<<8)+cp[2]:cp[0];
- twprintf("<> %.8x + %d", ntohl(th->th_ack), offset); */
- DECODEL(th->th_ack)
- /* twprintf(" = %.8x\r\n", ntohl(th->th_ack));
- } */
- if (changes & NEW_S)
- DECODEL(th->th_seq)
- break;
- }
- /* Update the IP ID */
- if (changes & NEW_I)
- DECODES(cs->cs_ip.ip_id)
- else
- cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
-
- /*
- * At this point, cp points to the first byte of data in the packet.
- * If we're not aligned on a 4-byte boundary, copy the data down so
- * the IP & TCP headers will be aligned. Then back up cp by the
- * TCP/IP header length to make room for the reconstructed header (we
- * assume the packet we were handed has enough space to prepend 128
- * bytes of header). Adjust the lenth to account for the new header
- * & fill in the IP total length.
- */
- *len -= (cp - bufp);
- if (*len < 0)
- /*
- * we must have dropped some characters (crc should detect
- * this but the old slip framing won't)
- */
- goto bad;
-
- if ((int) cp & 3) {
- if (*len > 0)
- OVBCOPY(cp, (u_char *) ((int) cp & ~3), *len);
- cp = (u_char *) ((int) cp & ~3);
- }
- cp -= cs->cs_hlen;
- *len += cs->cs_hlen;
- cs->cs_ip.ip_len = htons(*len);
- BCOPY(&cs->cs_ip, cp, cs->cs_hlen);
-
- /* recompute the ip header checksum */
- {
- register u_short *bp = (u_short *) cp;
- for (changes = 0; hlen > 0; hlen -= 2)
- changes += *bp++;
- changes = (changes & 0xffff) + (changes >> 16);
- changes = (changes & 0xffff) + (changes >> 16);
- ((struct net_ip *)cp)->ip_sum = ~changes;
- }
-
- return (cp);
-
- bad:
- comp->flags |= SLF_TOSS;
- return ((u_char *) 0);
- }
-
- /*
- A.4 Initialization
-
- This routine initializes the state structure for both the transmit and
- receive halves of some serial line. It must be called each time the
- line is brought up.
- */
- int
- sl_compress_init(compptr)
- struct slcompress **compptr;
- {
- register u_int i;
- struct slcompress *comp = *compptr;
- register struct cstate *tstate;
-
- if (comp==NULL)
- {
- if (comp = (struct slcompress *)malloc(sizeof(struct slcompress)), comp==NULL)
- return 0;
- }
- tstate = comp->tstate;
-
- /*
- * Clean out any junk left from the last time line was used.
- */
- bzero((char *) comp, sizeof(*comp));
- /*
- * Link the transmit states into a circular list.
- */
- for (i = MAX_STATES - 1; i > 0; --i) {
- tstate[i].cs_id = i;
- tstate[i].cs_next = &tstate[i - 1];
- }
- tstate[0].cs_next = &tstate[MAX_STATES - 1];
- tstate[0].cs_id = 0;
- comp->last_cs = &tstate[0];
- /*
- * Make sure we don't accidentally do CID compression
- * (assumes MAX_STATES < 255).
- */
- comp->last_recv = 255;
- comp->last_xmit = 255;
- *compptr = comp;
- return 1;
- }
- /*
-
- A.5 Berkeley Unix dependencies
-
- Note: The following is of interest only if you are trying to bring the
- sample code up on a system that is not derived from 4BSD (Berkeley
- Unix).
-
- The code uses the normal Berkeley Unix header files (from
- /usr/include/netinet) for definitions of the structure of IP and TCP
- headers. The structure tags tend to follow the protocol RFCs closely
- and should be obvious even if you do not have access to a 4BSD
- system./48/
-
- The macro BCOPY(src, dst, amt) is invoked to copy amt bytes from src to
- dst. In BSD, it translates into a call to bcopy. If you have the
- misfortune to be running System-V Unix, it can be translated into a call
- to memcpy. The macro OVBCOPY(src, dst, amt) is used to copy when src
- and dst overlap (i.e., when doing the 4-byte alignment copy). In the
- BSD kernel, it translates into a call to ovbcopy. Since AT&T botched
- the definition of memcpy, this should probably translate into a copy
- loop under System-V.
-
- The macro BCMP(src, dst, amt) is invoked to compare amt bytes of src and
- dst for equality. In BSD, it translates into a call to bcmp. In
- System-V, it can be translated into a call to memcmp or you can write a
- routine to do the compare. The routine should return zero if all bytes
- of src and dst are equal and non-zero otherwise.
-
- The routine ntohl(dat) converts (4 byte) long dat from network byte
- order to host byte order. On a reasonable cpu this can be the no-op
- macro:
- #define ntohl(dat) (dat)
-
- On a Vax or IBM PC (or anything with Intel byte order), you will have to
- define a macro or routine to rearrange bytes.
-
- The routine ntohs(dat) is like ntohl but converts (2 byte) shorts
- instead of longs. The routines htonl(dat) and htons(dat) do the inverse
- transform (host to network byte order) for longs and shorts.
-
-
- B Compatibility with past mistakes
-
- When combined with the modern PPP serial line protocol[9], the use of
- header compression is automatic and invisible to the user.
- Unfortunately, many sites have existing users of the SLIP described in
- [12] which doesn't allow for different protocol types to distinguish
- header compressed packets from IP packets or for version numbers or an
- option exchange that could be used to automatically negotiate header
- compression.
-
- The author has used the following tricks to allow header compressed SLIP
- to interoperate with the existing servers and clients. Note that these
- are hacks for compatibility with past mistakes and should be offensive
- to any right thinking person. They are offered solely to ease the pain
- of running SLIP while users wait patiently for vendors to release PPP.
-
-
- B.1 Living without a framing `type' byte
-
- The bizarre packet type numbers in sec. A.1 were chosen to allow a
- `packet type' to be sent on lines where it is undesirable or impossible
- to add an explicit type byte. Note that the first byte of an IP packet
- always contains `4' (the IP protocol version) in the top four bits. And
- that the most significant bit of the first byte of the compressed header
- is ignored. Using the packet types in sec. A.1, the type can be encoded
- in the most significant bits of the outgoing packet using the code
-
-
- p->dat[0] |= sl_compress_tcp(p, comp);
-
- and decoded on the receive side by
-
- if (p->dat[0] & 0x80)
- type = TYPE_COMPRESSED_TCP;
- else if (p->dat[0] >= 0x70) {
- type = TYPE_UNCOMPRESSED_TCP;
- p->dat[0] &=~ 0x30;
- } else
- type = TYPE_IP;
- status = sl_uncompress_tcp(p, type, comp);
-
-
- */
-
- /*
-
- B.2 Backwards compatible SLIP servers
-
- The SLIP described in [12] doesn't include any mechanism that could be
- used to automatically negotiate header compression. It would be nice to
- allow users of this SLIP to use header compression but, when users of
- the two SLIP varients share a common server, it would be annoying and
- difficult to manually configure both ends of each connection to enable
- compression. The following procedure can be used to avoid manual
- configuration.
-
- Since there are two types of dial-in clients (those that implement
- compression and those that don't) but one server for both types, it's
- clear that the server will be reconfiguring for each new client session
- but clients change configuration seldom if ever. If manual
- configuration has to be done, it should be done on the side that changes
- infrequently --- the client. This suggests that the server should
- somehow learn from the client whether to use header compression.
- Assuming symmetry (i.e., if compression is used at all it should be used
- both directions) the server can use the receipt of a compressed packet
- from some client to indicate that it can send compressed packets to that
- client. This leads to the following algorithm:
-
- There are two bits per line to control header compression: allowed and
- on. If on is set, compressed packets are sent, otherwise not. If
- allowed is set, compressed packets can be received and, if an
- UNCOMPRESSED_TCP packet arrives when on is clear, on will be set./49/
- If a compressed packet arrives when allowed is clear, it will be
- ignored.
-
- Clients are configured with both bits set (allowed is always set if on
- is set) and the server starts each session with allowed set and on
- clear. The first compressed packet from the client (which must be a
- UNCOMPRESSED_TCP packet) turns on compression for the server.
-
- */
-