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