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