-/* slcompress.c 7.2 89/06/29 */
-
-/*
- * THIS CODE IS NOT FOR DISTRIBUTION!
- * KEEP YOUR GRUBBY HANDS OFF UNLESS AUTHORIZED BY VAN JACOBSON TO COPY!
- * ASK SAM, MIKE, OR BILL ABOUT IT.
+/*-
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ *
+ * @(#)slcompress.c 7.8 (Berkeley) %G%
*/
/*
* Routines to compress and uncompess tcp packets (for transmission
* over low speed serial lines.
*
- * Copyright (c) 1988, 1989 by Van Jacobson, Lawrence Berkeley Laboratory
- * All rights reserved.
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ *
+ * static char rcsid[] =
+ * "$Header: slcompress.c,v 1.19 89/12/31 08:52:59 van Exp $";
*/
-#ifndef lint
-static char rcsid[] = "$Header: slcompress.c,v 1.7 89/03/19 18:10:19 van Locked $";
-#endif
-
-#include <sys/types.h>
#include <sys/param.h>
#include <sys/mbuf.h>
+
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
-#include "slcompress.h"
+#include <net/slcompress.h>
-int sls_packets;
-int sls_searches;
-int sls_misses;
-int sls_compressed;
-int sls_ipin;
-int sls_uncompressedin;
-int sls_compressedin;
+#ifndef SL_NO_STATS
+#define INCR(counter) ++comp->counter;
+#else
+#define INCR(counter)
+#endif
#define BCMP(p1, p2, n) bcmp((char *)(p1), (char *)(p2), (int)(n))
#define BCOPY(p1, p2, n) bcopy((char *)(p1), (char *)(p2), (int)(n))
-
#ifndef KERNEL
-extern struct mbuf *m_get();
-#undef MGET
-#define MGET(m, w, t) ((m) = m_get((w), (t)))
+#define ovbcopy bcopy
#endif
-#if BSD>=198810
-#define m_off m_data
-#endif
void
sl_compress_init(comp)
} \
}
+#define DECODEU(f) { \
+ if (*cp == 0) {\
+ (f) = htons((cp[1] << 8) | cp[2]); \
+ cp += 3; \
+ } else { \
+ (f) = htons((u_long)*cp++); \
+ } \
+}
+
u_char
-sl_compress_tcp(m, ip, comp)
+sl_compress_tcp(m, ip, comp, compress_cid)
struct mbuf *m;
register struct ip *ip;
struct slcompress *comp;
+ int compress_cid;
{
register struct cstate *cs = comp->last_cs->cs_next;
register u_int hlen = ip->ip_hl;
register u_char *cp = new_seq;
/*
- * Bail if this is an ip fragment or if we don't have
- * a complete ip & tcp header in the first mbuf. Otherwise,
- * check flags to see if this is a packet we might compress
- * and, if so, try to locate the connection state.
- * since slip links tend to be end nodes, check the tcp ports
- * first since the inet addresses won't usually change.
- * 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.
+ * 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 & 0x3fff) || m->m_len < 40)
+ if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40)
return (TYPE_IP);
th = (struct tcphdr *)&((int *)ip)[hlen];
if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK)
return (TYPE_IP);
-
- ++sls_packets;
- if (*(int *)th != ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl] ||
- ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
- ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr) {
+ /*
+ * 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.
+ */
+ INCR(sls_packets)
+ if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
+ ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr ||
+ *(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
- * first_cs pointing to the head of the list. The
+ * 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 used.
+ * 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;
- ++sls_searches;
- if (*(int *)th == ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]
- && ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
- && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr)
+ INCR(sls_searches)
+ if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
+ && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr
+ && *(int *)th == ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl])
goto found;
- } while (cs != comp->last_cs);
- ++sls_misses;
+ } 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.
+ * 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.
*/
+ INCR(sls_misses)
comp->last_cs = lcs;
hlen += th->th_off;
hlen <<= 2;
/*
* Found it -- move to the front on the connection list.
*/
- if (comp->last_cs == cs)
+ if (cs == lastcs)
comp->last_cs = lcs;
else {
lcs->cs_next = cs->cs_next;
- cs->cs_next = comp->last_cs->cs_next;
- comp->last_cs->cs_next = cs;
+ cs->cs_next = lastcs->cs_next;
+ lastcs->cs_next = cs;
}
}
/*
- * Make sure that only what we expect to change changed.
+ * 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 tcphdr *)&((int *)&cs->cs_ip)[hlen];
deltaS = hlen;
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 &&
/* 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. */
+ * with it. */
goto uncompressed;
if (deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) {
switch(changes) {
case 0:
- if (ip->ip_len != cs->cs_ip.ip_len && ntohs(ip->ip_len) != hlen)
- break;
/*
- * Nothing changed and this packet looks like a duplicate
- * of the last or contains no data -- this is probably a
- * retransmitted ack or window probe. Send it
- * uncompressed in case the other side missed the
- * compressed version.
- *
- * (fall through)
+ * 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:
*/
deltaS = cp - new_seq;
cp = (u_char *)ip;
- if (comp->last_xmit != cs->cs_id) {
+ if (compress_cid == 0 || comp->last_xmit != cs->cs_id) {
comp->last_xmit = cs->cs_id;
hlen -= deltaS + 4;
- cp += hlen; m->m_len -= hlen; m->m_off += hlen;
- *cp++ = TYPE_COMPRESSED_TCP | changes | NEW_C;
+ cp += hlen;
+ *cp++ = changes | NEW_C;
*cp++ = cs->cs_id;
} else {
hlen -= deltaS + 3;
- cp += hlen; m->m_len -= hlen; m->m_off += hlen;
- *cp++ = TYPE_COMPRESSED_TCP | changes;
+ cp += hlen;
+ *cp++ = changes;
}
+ m->m_len -= hlen;
+ m->m_data += hlen;
*cp++ = deltaA >> 8;
*cp++ = deltaA;
BCOPY(new_seq, cp, deltaS);
- ++sls_compressed;
+ INCR(sls_compressed)
return (TYPE_COMPRESSED_TCP);
/*
BCOPY(ip, &cs->cs_ip, hlen);
ip->ip_p = cs->cs_id;
comp->last_xmit = cs->cs_id;
- ip->ip_v = (TYPE_UNCOMPRESSED_TCP>>4);
return (TYPE_UNCOMPRESSED_TCP);
}
-int uncdeb ;
-struct mbuf *
-sl_uncompress_tcp(m, type, comp)
- register struct mbuf *m;
- u_char type;
+int
+sl_uncompress_tcp(bufp, len, type, comp)
+ u_char **bufp;
+ int len;
+ u_int type;
struct slcompress *comp;
{
register u_char *cp;
register struct tcphdr *th;
register struct cstate *cs;
register struct ip *ip;
- register struct mbuf *m0;
switch (type) {
case TYPE_UNCOMPRESSED_TCP:
- ip = mtod(m, struct ip *);
+ ip = (struct ip *) *bufp;
if (ip->ip_p >= MAX_STATES)
goto bad;
- ip->ip_v = 4;
-
cs = &comp->rstate[comp->last_recv = ip->ip_p];
comp->flags &=~ SLF_TOSS;
ip->ip_p = IPPROTO_TCP;
BCOPY(ip, &cs->cs_ip, hlen);
cs->cs_ip.ip_sum = 0;
cs->cs_hlen = hlen;
- ++sls_uncompressedin;
- return (m);
+ INCR(sls_uncompressedin)
+ return (len);
default:
- if(type&TYPE_COMPRESSED_TCP) goto compre;
- ++sls_ipin;
- return (m);
-
- case TYPE_ERROR:
- comp->flags |= SLF_TOSS;
- return (m);
+ goto bad;
case TYPE_COMPRESSED_TCP:
-compre:
break;
}
/* We've got a compressed packet. */
- ++sls_compressedin;
- cp = mtod(m, u_char *);
+ INCR(sls_compressedin)
+ cp = *bufp;
changes = *cp++;
if (changes & NEW_C) {
/* Make sure the state index is in range, then grab the state.
/* 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)
- goto bad;
+ if (comp->flags & SLF_TOSS) {
+ INCR(sls_tossed)
+ return (0);
+ }
}
cs = &comp->rstate[comp->last_recv];
hlen = cs->cs_ip.ip_hl << 2;
default:
if (changes & NEW_U) {
th->th_flags |= TH_URG;
- DECODES(th->th_urp)
+ DECODEU(th->th_urp)
} else
th->th_flags &=~ TH_URG;
if (changes & NEW_W)
/*
* At this point, cp points to the first byte of data in the
- * packet (if any). Toss the compressed header from the
- * original packet, allocatate a new mbuf for the uncompressed
- * header (to make sure it's aligned correctly), then chain it
- * in front of the original. Set up the ip length & ip checksum then
- * return the rebuilt packet.
+ * 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 length to account for
+ * the new header & fill in the IP total length.
*/
- changes = cp - mtod(m, u_char *);
- m->m_off += changes; m->m_len -= changes;
- changes = cs->cs_hlen;
- for (m0 = m; m0; m0 = m0->m_next)
- changes += m0->m_len;
- cs->cs_ip.ip_len = htons(changes);
-
- /*MGET(m0, M_DONTWAIT, MT_DATA);*/
- MGETHDR(m0, M_DONTWAIT, MT_DATA); /* XXX! */
- if (! m0)
+ 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;
- m0->m_next = m;
- m0->m_pkthdr.rcvif = m->m_pkthdr.rcvif ; /* XXX! */
- m0->m_pkthdr.len = m->m_pkthdr.len; /* XXX! */
- m = m0;
- m->m_len = cs->cs_hlen;
- ip = mtod(m, struct ip *);
- BCOPY(&cs->cs_ip, ip, cs->cs_hlen);
-
- ip->ip_sum = in_cksum(m, hlen);
- return (m);
-
+ if ((int)cp & 3) {
+ if (len > 0)
+ (void) ovbcopy(cp, (caddr_t)((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);
+ *bufp = cp;
+
+ /* 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 ip *)cp)->ip_sum = ~ changes;
+ }
+ return (len);
bad:
- m_freem(m);
- return ((struct mbuf *)0);
+ comp->flags |= SLF_TOSS;
+ INCR(sls_errorin)
+ return (0);
}