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