BSD 4_3_Net_2 release
[unix-history] / usr / src / sys / net / slcompress.c
index a9945c2..81b3543 100644 (file)
@@ -1,24 +1,49 @@
-/*     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.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)slcompress.c        7.7 (Berkeley) 5/7/91
  */
 
 /*
  * Routines to compress and uncompess tcp packets (for transmission
  * over low speed serial lines.
  *
  */
 
 /*
  * 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 <sys/param.h>
 #include <sys/mbuf.h>
 #include <netinet/in.h>
@@ -28,26 +53,18 @@ static char rcsid[] = "$Header: slcompress.c,v 1.7 89/03/19 18:10:19 van Locked
 
 #include "slcompress.h"
 
 
 #include "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))
 
 #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
 #ifndef KERNEL
-extern struct mbuf *m_get();
-#undef MGET
-#define MGET(m, w, t) ((m) = m_get((w), (t)))
+#define ovbcopy bcopy
 #endif
 
 #endif
 
-#if BSD>=198810
-#define m_off m_data
-#endif
 
 void
 sl_compress_init(comp)
 
 void
 sl_compress_init(comp)
@@ -112,12 +129,22 @@ 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
 
 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;
        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 struct cstate *cs = comp->last_cs->cs_next;
        register u_int hlen = ip->ip_hl;
@@ -129,60 +156,61 @@ sl_compress_tcp(m, ip, comp)
        register u_char *cp = new_seq;
 
        /*
        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);
                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
                /*
                 * 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
                 * 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 *lcs;
+               register struct cstate *lastcs = comp->last_cs;
 
                do {
                        lcs = cs; cs = cs->cs_next;
 
                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;
                                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;
                comp->last_cs = lcs;
                hlen += th->th_off;
                hlen <<= 2;
@@ -192,17 +220,25 @@ sl_compress_tcp(m, ip, comp)
                /*
                 * Found it -- move to the front on the connection list.
                 */
                /*
                 * 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;
                        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;
         */
        oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen];
        deltaS = hlen;
@@ -210,6 +246,7 @@ sl_compress_tcp(m, ip, comp)
        hlen <<= 2;
 
        if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] ||
        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 &&
            ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] ||
            th->th_off != oth->th_off ||
            (deltaS > 5 &&
@@ -232,7 +269,7 @@ sl_compress_tcp(m, ip, comp)
                /* 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
                /* 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))) {
                 goto uncompressed;
 
        if (deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) {
@@ -257,17 +294,19 @@ sl_compress_tcp(m, ip, comp)
        switch(changes) {
 
        case 0:
        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:
 
        case SPECIAL_I:
        case SPECIAL_D:
@@ -320,21 +359,23 @@ sl_compress_tcp(m, ip, comp)
         */
        deltaS = cp - new_seq;
        cp = (u_char *)ip;
         */
        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;
                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++ = 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);
        *cp++ = deltaA >> 8;
        *cp++ = deltaA;
        BCOPY(new_seq, cp, deltaS);
-       ++sls_compressed;
+       INCR(sls_compressed)
        return (TYPE_COMPRESSED_TCP);
 
        /*
        return (TYPE_COMPRESSED_TCP);
 
        /*
@@ -346,16 +387,15 @@ uncompressed:
        BCOPY(ip, &cs->cs_ip, hlen);
        ip->ip_p = cs->cs_id;
        comp->last_xmit = cs->cs_id;
        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);
 }
 
        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;
        struct slcompress *comp;
 {
        register u_char *cp;
@@ -363,16 +403,13 @@ sl_uncompress_tcp(m, type, comp)
        register struct tcphdr *th;
        register struct cstate *cs;
        register struct ip *ip;
        register struct tcphdr *th;
        register struct cstate *cs;
        register struct ip *ip;
-       register struct mbuf *m0;
 
        switch (type) {
 
        case TYPE_UNCOMPRESSED_TCP:
 
        switch (type) {
 
        case TYPE_UNCOMPRESSED_TCP:
-               ip = mtod(m, struct ip *);
+               ip = (struct ip *) *bufp;
                if (ip->ip_p >= MAX_STATES)
                        goto bad;
                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;
                cs = &comp->rstate[comp->last_recv = ip->ip_p];
                comp->flags &=~ SLF_TOSS;
                ip->ip_p = IPPROTO_TCP;
@@ -382,25 +419,18 @@ sl_uncompress_tcp(m, type, comp)
                BCOPY(ip, &cs->cs_ip, hlen);
                cs->cs_ip.ip_sum = 0;
                cs->cs_hlen = hlen;
                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:
 
        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:
 
        case TYPE_COMPRESSED_TCP:
-compre:
                break;
        }
        /* We've got a compressed packet. */
                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.
        changes = *cp++;
        if (changes & NEW_C) {
                /* Make sure the state index is in range, then grab the state.
@@ -414,8 +444,10 @@ compre:
                /* 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. */
                /* 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;
        }
        cs = &comp->rstate[comp->last_recv];
        hlen = cs->cs_ip.ip_hl << 2;
@@ -444,7 +476,7 @@ compre:
        default:
                if (changes & NEW_U) {
                        th->th_flags |= TH_URG;
        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)
                } else
                        th->th_flags &=~ TH_URG;
                if (changes & NEW_W)
@@ -462,36 +494,42 @@ compre:
 
        /*
         * At this point, cp points to the first byte of data in the
 
        /*
         * 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;
 
                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:
 bad:
-       m_freem(m);
-       return ((struct mbuf *)0);
+       comp->flags |= SLF_TOSS;
+       INCR(sls_errorin)
+       return (0);
 }
 }