source: rtems/cpukit/libnetworking/net/slcompress.c @ b25b88e7

4.104.115
Last change on this file since b25b88e7 was b25b88e7, checked in by Ralf Corsepius <ralf.corsepius@…>, on 03/28/10 at 05:50:29

Add HAVE_CONFIG_H support to let files receive configure defines.

  • Property mode set to 100644
File size: 16.5 KB
Line 
1/*-
2 * Copyright (c) 1989, 1993, 1994
3 *      The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 *      @(#)slcompress.c        8.2 (Berkeley) 4/16/94
30 * $FreeBSD: src/sys/net/slcompress.c,v 1.19 2004/04/07 20:46:12 imp Exp $
31 */
32
33/*
34 * Routines to compress and uncompess tcp packets (for transmission
35 * over low speed serial lines.
36 *
37 * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
38 *      - Initial distribution.
39 *
40 */
41
42#ifdef HAVE_CONFIG_H
43#include "config.h"
44#endif
45
46#include <stdint.h>
47
48#include <sys/param.h>
49#include <sys/mbuf.h>
50#include <sys/systm.h>
51
52#include <netinet/in.h>
53#include <netinet/in_systm.h>
54#include <netinet/ip.h>
55#include <netinet/tcp.h>
56
57#include <net/slcompress.h>
58
59#ifndef SL_NO_STATS
60#define INCR(counter) ++comp->counter;
61#else
62#define INCR(counter)
63#endif
64
65#define BCMP(p1, p2, n) bcmp((void *)(p1), (void *)(p2), (int)(n))
66#define BCOPY(p1, p2, n) bcopy((void *)(p1), (void *)(p2), (int)(n))
67
68void
69sl_compress_init(struct slcompress *comp, int max_state)
70{
71        register u_int i;
72        register struct cstate *tstate = comp->tstate;
73
74        if (max_state == -1) {
75                max_state = MAX_STATES - 1;
76                bzero((char *)comp, sizeof(*comp));
77        } else {
78                /* Don't reset statistics */
79                bzero((char *)comp->tstate, sizeof(comp->tstate));
80                bzero((char *)comp->rstate, sizeof(comp->rstate));
81        }
82        for (i = max_state; i > 0; --i) {
83                tstate[i].cs_id = i;
84                tstate[i].cs_next = &tstate[i - 1];
85        }
86        tstate[0].cs_next = &tstate[max_state];
87        tstate[0].cs_id = 0;
88        comp->last_cs = &tstate[0];
89        comp->last_recv = 255;
90        comp->last_xmit = 255;
91        comp->flags = SLF_TOSS;
92}
93
94
95/* ENCODE encodes a number that is known to be non-zero.  ENCODEZ
96 * checks for zero (since zero has to be encoded in the long, 3 byte
97 * form).
98 */
99#define ENCODE(n) { \
100        if ((u_int16_t)(n) >= 256) { \
101                *cp++ = 0; \
102                cp[1] = (n); \
103                cp[0] = (n) >> 8; \
104                cp += 2; \
105        } else { \
106                *cp++ = (n); \
107        } \
108}
109#define ENCODEZ(n) { \
110        if ((u_int16_t)(n) >= 256 || (u_int16_t)(n) == 0) { \
111                *cp++ = 0; \
112                cp[1] = (n); \
113                cp[0] = (n) >> 8; \
114                cp += 2; \
115        } else { \
116                *cp++ = (n); \
117        } \
118}
119
120#define DECODEL(f) { \
121        if (*cp == 0) {\
122                (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \
123                cp += 3; \
124        } else { \
125                (f) = htonl(ntohl(f) + (u_int32_t)*cp++); \
126        } \
127}
128
129#define DECODES(f) { \
130        if (*cp == 0) {\
131                (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \
132                cp += 3; \
133        } else { \
134                (f) = htons(ntohs(f) + (u_int32_t)*cp++); \
135        } \
136}
137
138#define DECODEU(f) { \
139        if (*cp == 0) {\
140                (f) = htons((cp[1] << 8) | cp[2]); \
141                cp += 3; \
142        } else { \
143                (f) = htons((u_int32_t)*cp++); \
144        } \
145}
146
147/*
148 * Attempt to compress an outgoing TCP packet and return the type of
149 * the result.  The caller must have already verified that the protocol
150 * is TCP.  The first mbuf must contain the complete IP and TCP headers,
151 * and "ip" must be == mtod(m, struct ip *).  "comp" supplies the
152 * compression state, and "compress_cid" tells us whether it is OK
153 * to leave out the CID field when feasible.
154 *
155 * The caller is responsible for adjusting m->m_pkthdr.len upon return,
156 * if m is an M_PKTHDR mbuf.
157 */
158u_int
159sl_compress_tcp(struct mbuf *m, struct ip *ip, struct slcompress *comp,
160        int compress_cid)
161{
162        register struct cstate *cs = comp->last_cs->cs_next;
163        register u_int hlen = ip->ip_hl;
164        register struct tcphdr *oth;
165        register struct tcphdr *th;
166        register u_int deltaS, deltaA;
167        register u_int changes = 0;
168        u_char new_seq[16];
169        register u_char *cp = new_seq;
170
171        /*
172         * Bail if this is an IP fragment or if the TCP packet isn't
173         * `compressible' (i.e., ACK isn't set or some other control bit is
174         * set).  (We assume that the caller has already made sure the
175         * packet is IP proto TCP).
176         */
177        if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40)
178                return (TYPE_IP);
179
180        th = (struct tcphdr *)&((int32_t *)ip)[hlen];
181        if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK)
182                return (TYPE_IP);
183        /*
184         * Packet is compressible -- we're going to send either a
185         * COMPRESSED_TCP or UNCOMPRESSED_TCP packet.  Either way we need
186         * to locate (or create) the connection state.  Special case the
187         * most recently used connection since it's most likely to be used
188         * again & we don't have to do any reordering if it's used.
189         */
190        INCR(sls_packets)
191        if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
192            ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr ||
193            *(int32_t *)th != ((int32_t *)&cs->cs_ip)[cs->cs_ip.ip_hl]) {
194                /*
195                 * Wasn't the first -- search for it.
196                 *
197                 * States are kept in a circularly linked list with
198                 * last_cs pointing to the end of the list.  The
199                 * list is kept in lru order by moving a state to the
200                 * head of the list whenever it is referenced.  Since
201                 * the list is short and, empirically, the connection
202                 * we want is almost always near the front, we locate
203                 * states via linear search.  If we don't find a state
204                 * for the datagram, the oldest state is (re-)used.
205                 */
206                register struct cstate *lcs;
207                register struct cstate *lastcs = comp->last_cs;
208
209                do {
210                        lcs = cs; cs = cs->cs_next;
211                        INCR(sls_searches)
212                        if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
213                            && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr
214                            && *(int32_t *)th ==
215                            ((int32_t *)&cs->cs_ip)[cs->cs_ip.ip_hl])
216                                goto found;
217                } while (cs != lastcs);
218
219                /*
220                 * Didn't find it -- re-use oldest cstate.  Send an
221                 * uncompressed packet that tells the other side what
222                 * connection number we're using for this conversation.
223                 * Note that since the state list is circular, the oldest
224                 * state points to the newest and we only need to set
225                 * last_cs to update the lru linkage.
226                 */
227                INCR(sls_misses)
228                comp->last_cs = lcs;
229                hlen += th->th_off;
230                hlen <<= 2;
231                if (hlen > m->m_len)
232                    return TYPE_IP;
233                goto uncompressed;
234
235        found:
236                /*
237                 * Found it -- move to the front on the connection list.
238                 */
239                if (cs == lastcs)
240                        comp->last_cs = lcs;
241                else {
242                        lcs->cs_next = cs->cs_next;
243                        cs->cs_next = lastcs->cs_next;
244                        lastcs->cs_next = cs;
245                }
246        }
247
248        /*
249         * Make sure that only what we expect to change changed. The first
250         * line of the `if' checks the IP protocol version, header length &
251         * type of service.  The 2nd line checks the "Don't fragment" bit.
252         * The 3rd line checks the time-to-live and protocol (the protocol
253         * check is unnecessary but costless).  The 4th line checks the TCP
254         * header length.  The 5th line checks IP options, if any.  The 6th
255         * line checks TCP options, if any.  If any of these things are
256         * different between the previous & current datagram, we send the
257         * current datagram `uncompressed'.
258         */
259        oth = (struct tcphdr *)&((int32_t *)&cs->cs_ip)[hlen];
260        deltaS = hlen;
261        hlen += th->th_off;
262        hlen <<= 2;
263        if (hlen > m->m_len)
264            return TYPE_IP;
265
266        if (((u_int16_t *)ip)[0] != ((u_int16_t *)&cs->cs_ip)[0] ||
267            ((u_int16_t *)ip)[3] != ((u_int16_t *)&cs->cs_ip)[3] ||
268            ((u_int16_t *)ip)[4] != ((u_int16_t *)&cs->cs_ip)[4] ||
269            th->th_off != oth->th_off ||
270            (deltaS > 5 &&
271             BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
272            (th->th_off > 5 &&
273             BCMP(th + 1, oth + 1, (th->th_off - 5) << 2)))
274                goto uncompressed;
275
276        /*
277         * Figure out which of the changing fields changed.  The
278         * receiver expects changes in the order: urgent, window,
279         * ack, seq (the order minimizes the number of temporaries
280         * needed in this section of code).
281         */
282        if (th->th_flags & TH_URG) {
283                deltaS = ntohs(th->th_urp);
284                ENCODEZ(deltaS);
285                changes |= NEW_U;
286        } else if (th->th_urp != oth->th_urp)
287                /* argh! URG not set but urp changed -- a sensible
288                 * implementation should never do this but RFC793
289                 * doesn't prohibit the change so we have to deal
290                 * with it. */
291                 goto uncompressed;
292
293        deltaS = (u_int16_t)(ntohs(th->th_win) - ntohs(oth->th_win));
294        if (deltaS) {
295                ENCODE(deltaS);
296                changes |= NEW_W;
297        }
298
299        deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack);
300        if (deltaA) {
301                if (deltaA > 0xffff)
302                        goto uncompressed;
303                ENCODE(deltaA);
304                changes |= NEW_A;
305        }
306
307        deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq);
308        if (deltaS) {
309                if (deltaS > 0xffff)
310                        goto uncompressed;
311                ENCODE(deltaS);
312                changes |= NEW_S;
313        }
314
315        switch(changes) {
316
317        case 0:
318                /*
319                 * Nothing changed. If this packet contains data and the
320                 * last one didn't, this is probably a data packet following
321                 * an ack (normal on an interactive connection) and we send
322                 * it compressed.  Otherwise it's probably a retransmit,
323                 * retransmitted ack or window probe.  Send it uncompressed
324                 * in case the other side missed the compressed version.
325                 */
326                if (ip->ip_len != cs->cs_ip.ip_len &&
327                    ntohs(cs->cs_ip.ip_len) == hlen)
328                        break;
329
330                /* FALLTHROUGH */
331
332        case SPECIAL_I:
333        case SPECIAL_D:
334                /*
335                 * actual changes match one of our special case encodings --
336                 * send packet uncompressed.
337                 */
338                goto uncompressed;
339
340        case NEW_S|NEW_A:
341                if (deltaS == deltaA &&
342                    deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
343                        /* special case for echoed terminal traffic */
344                        changes = SPECIAL_I;
345                        cp = new_seq;
346                }
347                break;
348
349        case NEW_S:
350                if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
351                        /* special case for data xfer */
352                        changes = SPECIAL_D;
353                        cp = new_seq;
354                }
355                break;
356        }
357
358        deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
359        if (deltaS != 1) {
360                ENCODEZ(deltaS);
361                changes |= NEW_I;
362        }
363        if (th->th_flags & TH_PUSH)
364                changes |= TCP_PUSH_BIT;
365        /*
366         * Grab the cksum before we overwrite it below.  Then update our
367         * state with this packet's header.
368         */
369        deltaA = ntohs(th->th_sum);
370        BCOPY(ip, &cs->cs_ip, hlen);
371
372        /*
373         * We want to use the original packet as our compressed packet.
374         * (cp - new_seq) is the number of bytes we need for compressed
375         * sequence numbers.  In addition we need one byte for the change
376         * mask, one for the connection id and two for the tcp checksum.
377         * So, (cp - new_seq) + 4 bytes of header are needed.  hlen is how
378         * many bytes of the original packet to toss so subtract the two to
379         * get the new packet size.
380         */
381        deltaS = cp - new_seq;
382        cp = (u_char *)ip;
383        if (compress_cid == 0 || comp->last_xmit != cs->cs_id) {
384                comp->last_xmit = cs->cs_id;
385                hlen -= deltaS + 4;
386                cp += hlen;
387                *cp++ = changes | NEW_C;
388                *cp++ = cs->cs_id;
389        } else {
390                hlen -= deltaS + 3;
391                cp += hlen;
392                *cp++ = changes;
393        }
394        m->m_len -= hlen;
395        m->m_data += hlen;
396        *cp++ = deltaA >> 8;
397        *cp++ = deltaA;
398        BCOPY(new_seq, cp, deltaS);
399        INCR(sls_compressed)
400        return (TYPE_COMPRESSED_TCP);
401
402        /*
403         * Update connection state cs & send uncompressed packet ('uncompressed'
404         * means a regular ip/tcp packet but with the 'conversation id' we hope
405         * to use on future compressed packets in the protocol field).
406         */
407uncompressed:
408        BCOPY(ip, &cs->cs_ip, hlen);
409        ip->ip_p = cs->cs_id;
410        comp->last_xmit = cs->cs_id;
411        return (TYPE_UNCOMPRESSED_TCP);
412}
413
414
415int
416sl_uncompress_tcp(u_char **bufp, int len, u_int type, struct slcompress *comp)
417{
418        u_char *hdr, *cp;
419        u_int hlen;
420        int vjlen;
421
422        cp = bufp? *bufp: NULL;
423        vjlen = sl_uncompress_tcp_core(cp, len, len, type, comp, &hdr, &hlen);
424        if (vjlen < 0)
425                return (0);     /* error */
426        if (vjlen == 0)
427                return (len);   /* was uncompressed already */
428
429        cp += vjlen;
430        len -= vjlen;
431
432        /*
433         * At this point, cp points to the first byte of data in the
434         * packet.  If we're not aligned on a 4-byte boundary, copy the
435         * data down so the ip & tcp headers will be aligned.  Then back up
436         * cp by the tcp/ip header length to make room for the reconstructed
437         * header (we assume the packet we were handed has enough space to
438         * prepend 128 bytes of header).
439         */
440        if ((intptr_t)cp & 3) {
441                if (len > 0)
442                        BCOPY(cp, ((intptr_t)cp &~ 3), len);
443                cp = (u_char *)((intptr_t)cp &~ 3);
444        }
445        cp -= hlen;
446        len += hlen;
447        BCOPY(hdr, cp, hlen);
448
449        *bufp = cp;
450        return (len);
451}
452
453/*
454 * Uncompress a packet of total length total_len.  The first buflen
455 * bytes are at buf; this must include the entire (compressed or
456 * uncompressed) TCP/IP header.  This procedure returns the length
457 * of the VJ header, with a pointer to the uncompressed IP header
458 * in *hdrp and its length in *hlenp.
459 */
460int
461sl_uncompress_tcp_core(u_char *buf, int buflen, int total_len,
462        u_int type, struct slcompress *comp,
463        u_char **hdrp, u_int *hlenp)
464{
465        register u_char *cp;
466        register uint32_t hlen, changes;
467        register struct tcphdr *th;
468        register struct cstate *cs;
469        register struct ip *ip;
470        register u_int16_t *bp;
471        register u_int vjlen;
472
473        switch (type) {
474
475        case TYPE_UNCOMPRESSED_TCP:
476                ip = (struct ip *) buf;
477                if (ip->ip_p >= MAX_STATES)
478                        goto bad;
479                cs = &comp->rstate[comp->last_recv = ip->ip_p];
480                comp->flags &=~ SLF_TOSS;
481                ip->ip_p = IPPROTO_TCP;
482                /*
483                 * Calculate the size of the TCP/IP header and make sure that
484                 * we don't overflow the space we have available for it.
485                 */
486                hlen = ip->ip_hl << 2;
487                if (hlen + sizeof(struct tcphdr) > buflen)
488                        goto bad;
489                hlen += ((struct tcphdr *)&((char *)ip)[hlen])->th_off << 2;
490                if (hlen > MAX_HDR || hlen > buflen)
491                        goto bad;
492                BCOPY(ip, &cs->cs_ip, hlen);
493                cs->cs_hlen = hlen;
494                INCR(sls_uncompressedin)
495                *hdrp = (u_char *) &cs->cs_ip;
496                *hlenp = hlen;
497                return (0);
498
499        default:
500                goto bad;
501
502        case TYPE_COMPRESSED_TCP:
503                break;
504        }
505        /* We've got a compressed packet. */
506        INCR(sls_compressedin)
507        cp = buf;
508        changes = *cp++;
509        if (changes & NEW_C) {
510                /* Make sure the state index is in range, then grab the state.
511                 * If we have a good state index, clear the 'discard' flag. */
512                if (*cp >= MAX_STATES)
513                        goto bad;
514
515                comp->flags &=~ SLF_TOSS;
516                comp->last_recv = *cp++;
517        } else {
518                /* this packet has an implicit state index.  If we've
519                 * had a line error since the last time we got an
520                 * explicit state index, we have to toss the packet. */
521                if (comp->flags & SLF_TOSS) {
522                        INCR(sls_tossed)
523                        return (-1);
524                }
525        }
526        cs = &comp->rstate[comp->last_recv];
527        hlen = cs->cs_ip.ip_hl << 2;
528        th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen];
529        th->th_sum = htons((*cp << 8) | cp[1]);
530        cp += 2;
531        if (changes & TCP_PUSH_BIT)
532                th->th_flags |= TH_PUSH;
533        else
534                th->th_flags &=~ TH_PUSH;
535
536        switch (changes & SPECIALS_MASK) {
537        case SPECIAL_I:
538                {
539                register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
540                th->th_ack = htonl(ntohl(th->th_ack) + i);
541                th->th_seq = htonl(ntohl(th->th_seq) + i);
542                }
543                break;
544
545        case SPECIAL_D:
546                th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
547                                   - cs->cs_hlen);
548                break;
549
550        default:
551                if (changes & NEW_U) {
552                        th->th_flags |= TH_URG;
553                        DECODEU(th->th_urp)
554                } else
555                        th->th_flags &=~ TH_URG;
556                if (changes & NEW_W)
557                        DECODES(th->th_win)
558                if (changes & NEW_A)
559                        DECODEL(th->th_ack)
560                if (changes & NEW_S)
561                        DECODEL(th->th_seq)
562                break;
563        }
564        if (changes & NEW_I) {
565                DECODES(cs->cs_ip.ip_id)
566        } else
567                cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
568
569        /*
570         * At this point, cp points to the first byte of data in the
571         * packet.  Fill in the IP total length and update the IP
572         * header checksum.
573         */
574        vjlen = cp - buf;
575        buflen -= vjlen;
576        if (buflen < 0)
577                /* we must have dropped some characters (crc should detect
578                 * this but the old slip framing won't) */
579                goto bad;
580
581        total_len += cs->cs_hlen - vjlen;
582        cs->cs_ip.ip_len = htons(total_len);
583
584        /* recompute the ip header checksum */
585        bp = (u_int16_t *) &cs->cs_ip;
586        cs->cs_ip.ip_sum = 0;
587                for (changes = 0; hlen > 0; hlen -= 2)
588                        changes += *bp++;
589                changes = (changes & 0xffff) + (changes >> 16);
590                changes = (changes & 0xffff) + (changes >> 16);
591        cs->cs_ip.ip_sum = ~ changes;
592
593        *hdrp = (u_char *) &cs->cs_ip;
594        *hlenp = cs->cs_hlen;
595        return vjlen;
596
597bad:
598        comp->flags |= SLF_TOSS;
599        INCR(sls_errorin)
600        return (-1);
601}
Note: See TracBrowser for help on using the repository browser.