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