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