149340Sbostic /*- 249340Sbostic * Copyright (c) 1989 The Regents of the University of California. 349340Sbostic * All rights reserved. 449340Sbostic * 549340Sbostic * %sccs.include.redist.c% 649340Sbostic * 7*56529Sbostic * @(#)slcompress.c 7.8 (Berkeley) 10/11/92 849340Sbostic */ 949340Sbostic 1038361Ssam /* 1138361Ssam * Routines to compress and uncompess tcp packets (for transmission 1238361Ssam * over low speed serial lines. 1338361Ssam * 1449340Sbostic * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: 1549340Sbostic * - Initial distribution. 1639948Ssam * 1749340Sbostic * static char rcsid[] = 1849340Sbostic * "$Header: slcompress.c,v 1.19 89/12/31 08:52:59 van Exp $"; 1938361Ssam */ 2038361Ssam 2138361Ssam #include <sys/param.h> 2238361Ssam #include <sys/mbuf.h> 23*56529Sbostic 2438361Ssam #include <netinet/in.h> 2538361Ssam #include <netinet/in_systm.h> 2638361Ssam #include <netinet/ip.h> 2738361Ssam #include <netinet/tcp.h> 2838361Ssam 29*56529Sbostic #include <net/slcompress.h> 3038361Ssam 3139948Ssam #ifndef SL_NO_STATS 3238374Skarels #define INCR(counter) ++comp->counter; 3338374Skarels #else 3438374Skarels #define INCR(counter) 3538374Skarels #endif 3638361Ssam 3738361Ssam #define BCMP(p1, p2, n) bcmp((char *)(p1), (char *)(p2), (int)(n)) 3838361Ssam #define BCOPY(p1, p2, n) bcopy((char *)(p1), (char *)(p2), (int)(n)) 3939948Ssam #ifndef KERNEL 4039948Ssam #define ovbcopy bcopy 4139948Ssam #endif 4238361Ssam 4338361Ssam 4438361Ssam void 4538361Ssam sl_compress_init(comp) 4638361Ssam struct slcompress *comp; 4738361Ssam { 4838361Ssam register u_int i; 4938361Ssam register struct cstate *tstate = comp->tstate; 5038361Ssam 5138361Ssam bzero((char *)comp, sizeof(*comp)); 5238361Ssam for (i = MAX_STATES - 1; i > 0; --i) { 5338361Ssam tstate[i].cs_id = i; 5438361Ssam tstate[i].cs_next = &tstate[i - 1]; 5538361Ssam } 5638361Ssam tstate[0].cs_next = &tstate[MAX_STATES - 1]; 5738361Ssam tstate[0].cs_id = 0; 5838361Ssam comp->last_cs = &tstate[0]; 5938361Ssam comp->last_recv = 255; 6038361Ssam comp->last_xmit = 255; 6138361Ssam } 6238361Ssam 6338361Ssam 6438361Ssam /* ENCODE encodes a number that is known to be non-zero. ENCODEZ 6538361Ssam * checks for zero (since zero has to be encoded in the long, 3 byte 6638361Ssam * form). 6738361Ssam */ 6838361Ssam #define ENCODE(n) { \ 6938361Ssam if ((u_short)(n) >= 256) { \ 7038361Ssam *cp++ = 0; \ 7138361Ssam cp[1] = (n); \ 7238361Ssam cp[0] = (n) >> 8; \ 7338361Ssam cp += 2; \ 7438361Ssam } else { \ 7538361Ssam *cp++ = (n); \ 7638361Ssam } \ 7738361Ssam } 7838361Ssam #define ENCODEZ(n) { \ 7938361Ssam if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ 8038361Ssam *cp++ = 0; \ 8138361Ssam cp[1] = (n); \ 8238361Ssam cp[0] = (n) >> 8; \ 8338361Ssam cp += 2; \ 8438361Ssam } else { \ 8538361Ssam *cp++ = (n); \ 8638361Ssam } \ 8738361Ssam } 8838361Ssam 8938361Ssam #define DECODEL(f) { \ 9038361Ssam if (*cp == 0) {\ 9138361Ssam (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \ 9238361Ssam cp += 3; \ 9338361Ssam } else { \ 9438361Ssam (f) = htonl(ntohl(f) + (u_long)*cp++); \ 9538361Ssam } \ 9638361Ssam } 9738361Ssam 9838361Ssam #define DECODES(f) { \ 9938361Ssam if (*cp == 0) {\ 10038361Ssam (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \ 10138361Ssam cp += 3; \ 10238361Ssam } else { \ 10338361Ssam (f) = htons(ntohs(f) + (u_long)*cp++); \ 10438361Ssam } \ 10538361Ssam } 10638361Ssam 10739948Ssam #define DECODEU(f) { \ 10839948Ssam if (*cp == 0) {\ 10939948Ssam (f) = htons((cp[1] << 8) | cp[2]); \ 11039948Ssam cp += 3; \ 11139948Ssam } else { \ 11239948Ssam (f) = htons((u_long)*cp++); \ 11339948Ssam } \ 11439948Ssam } 11538361Ssam 11639948Ssam 11738361Ssam u_char 11839948Ssam sl_compress_tcp(m, ip, comp, compress_cid) 11938361Ssam struct mbuf *m; 12038361Ssam register struct ip *ip; 12138361Ssam struct slcompress *comp; 12239948Ssam int compress_cid; 12338361Ssam { 12438361Ssam register struct cstate *cs = comp->last_cs->cs_next; 12538361Ssam register u_int hlen = ip->ip_hl; 12638361Ssam register struct tcphdr *oth; 12738361Ssam register struct tcphdr *th; 12838361Ssam register u_int deltaS, deltaA; 12938361Ssam register u_int changes = 0; 13038361Ssam u_char new_seq[16]; 13138361Ssam register u_char *cp = new_seq; 13238361Ssam 13338361Ssam /* 13439948Ssam * Bail if this is an IP fragment or if the TCP packet isn't 13539948Ssam * `compressible' (i.e., ACK isn't set or some other control bit is 13639948Ssam * set). (We assume that the caller has already made sure the 13739948Ssam * packet is IP proto TCP). 13838361Ssam */ 13939948Ssam if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40) 14038361Ssam return (TYPE_IP); 14138361Ssam 14238361Ssam th = (struct tcphdr *)&((int *)ip)[hlen]; 14338361Ssam if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK) 14438361Ssam return (TYPE_IP); 14539948Ssam /* 14639948Ssam * Packet is compressible -- we're going to send either a 14739948Ssam * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need 14839948Ssam * to locate (or create) the connection state. Special case the 14939948Ssam * most recently used connection since it's most likely to be used 15039948Ssam * again & we don't have to do any reordering if it's used. 15139948Ssam */ 15238374Skarels INCR(sls_packets) 15338374Skarels if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr || 15438374Skarels ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr || 15538374Skarels *(int *)th != ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) { 15638361Ssam /* 15738361Ssam * Wasn't the first -- search for it. 15838361Ssam * 15938361Ssam * States are kept in a circularly linked list with 16039948Ssam * last_cs pointing to the end of the list. The 16138361Ssam * list is kept in lru order by moving a state to the 16238361Ssam * head of the list whenever it is referenced. Since 16338361Ssam * the list is short and, empirically, the connection 16438361Ssam * we want is almost always near the front, we locate 16538361Ssam * states via linear search. If we don't find a state 16639948Ssam * for the datagram, the oldest state is (re-)used. 16738361Ssam */ 16838361Ssam register struct cstate *lcs; 16939948Ssam register struct cstate *lastcs = comp->last_cs; 17038361Ssam 17138361Ssam do { 17238361Ssam lcs = cs; cs = cs->cs_next; 17338374Skarels INCR(sls_searches) 17439948Ssam if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr 17539948Ssam && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr 17639948Ssam && *(int *)th == ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) 17738361Ssam goto found; 17839948Ssam } while (cs != lastcs); 17938361Ssam 18038361Ssam /* 18139948Ssam * Didn't find it -- re-use oldest cstate. Send an 18239948Ssam * uncompressed packet that tells the other side what 18339948Ssam * connection number we're using for this conversation. 18439948Ssam * Note that since the state list is circular, the oldest 18539948Ssam * state points to the newest and we only need to set 18639948Ssam * last_cs to update the lru linkage. 18738361Ssam */ 18839948Ssam INCR(sls_misses) 18938361Ssam comp->last_cs = lcs; 19038361Ssam hlen += th->th_off; 19138361Ssam hlen <<= 2; 19238361Ssam goto uncompressed; 19338361Ssam 19438361Ssam found: 19538361Ssam /* 19638361Ssam * Found it -- move to the front on the connection list. 19738361Ssam */ 19839948Ssam if (cs == lastcs) 19938361Ssam comp->last_cs = lcs; 20038361Ssam else { 20138361Ssam lcs->cs_next = cs->cs_next; 20239948Ssam cs->cs_next = lastcs->cs_next; 20339948Ssam lastcs->cs_next = cs; 20438361Ssam } 20538361Ssam } 20638361Ssam 20738361Ssam /* 20839948Ssam * Make sure that only what we expect to change changed. The first 20939948Ssam * line of the `if' checks the IP protocol version, header length & 21039948Ssam * type of service. The 2nd line checks the "Don't fragment" bit. 21139948Ssam * The 3rd line checks the time-to-live and protocol (the protocol 21239948Ssam * check is unnecessary but costless). The 4th line checks the TCP 21339948Ssam * header length. The 5th line checks IP options, if any. The 6th 21439948Ssam * line checks TCP options, if any. If any of these things are 21539948Ssam * different between the previous & current datagram, we send the 21639948Ssam * current datagram `uncompressed'. 21738361Ssam */ 21838361Ssam oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen]; 21938361Ssam deltaS = hlen; 22038361Ssam hlen += th->th_off; 22138361Ssam hlen <<= 2; 22238361Ssam 22338361Ssam if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] || 22439948Ssam ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] || 22538361Ssam ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] || 22638361Ssam th->th_off != oth->th_off || 22738361Ssam (deltaS > 5 && 22838361Ssam BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) || 22938361Ssam (th->th_off > 5 && 23038361Ssam BCMP(th + 1, oth + 1, (th->th_off - 5) << 2))) 23138361Ssam goto uncompressed; 23238361Ssam 23338361Ssam /* 23438361Ssam * Figure out which of the changing fields changed. The 23538361Ssam * receiver expects changes in the order: urgent, window, 23638361Ssam * ack, seq (the order minimizes the number of temporaries 23738361Ssam * needed in this section of code). 23838361Ssam */ 23938361Ssam if (th->th_flags & TH_URG) { 24038361Ssam deltaS = ntohs(th->th_urp); 24138361Ssam ENCODEZ(deltaS); 24238361Ssam changes |= NEW_U; 24338361Ssam } else if (th->th_urp != oth->th_urp) 24438361Ssam /* argh! URG not set but urp changed -- a sensible 24538361Ssam * implementation should never do this but RFC793 24638361Ssam * doesn't prohibit the change so we have to deal 24739948Ssam * with it. */ 24838361Ssam goto uncompressed; 24938361Ssam 25038361Ssam if (deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) { 25138361Ssam ENCODE(deltaS); 25238361Ssam changes |= NEW_W; 25338361Ssam } 25438361Ssam 25538361Ssam if (deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack)) { 25638361Ssam if (deltaA > 0xffff) 25738361Ssam goto uncompressed; 25838361Ssam ENCODE(deltaA); 25938361Ssam changes |= NEW_A; 26038361Ssam } 26138361Ssam 26238361Ssam if (deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq)) { 26338361Ssam if (deltaS > 0xffff) 26438361Ssam goto uncompressed; 26538361Ssam ENCODE(deltaS); 26638361Ssam changes |= NEW_S; 26738361Ssam } 26838361Ssam 26938361Ssam switch(changes) { 27038361Ssam 27138361Ssam case 0: 27238361Ssam /* 27339948Ssam * Nothing changed. If this packet contains data and the 27439948Ssam * last one didn't, this is probably a data packet following 27539948Ssam * an ack (normal on an interactive connection) and we send 27639948Ssam * it compressed. Otherwise it's probably a retransmit, 27739948Ssam * retransmitted ack or window probe. Send it uncompressed 27839948Ssam * in case the other side missed the compressed version. 27938361Ssam */ 28039948Ssam if (ip->ip_len != cs->cs_ip.ip_len && 28139948Ssam ntohs(cs->cs_ip.ip_len) == hlen) 28239948Ssam break; 28338361Ssam 28439948Ssam /* (fall through) */ 28539948Ssam 28638361Ssam case SPECIAL_I: 28738361Ssam case SPECIAL_D: 28838361Ssam /* 28938361Ssam * actual changes match one of our special case encodings -- 29038361Ssam * send packet uncompressed. 29138361Ssam */ 29238361Ssam goto uncompressed; 29338361Ssam 29438361Ssam case NEW_S|NEW_A: 29538361Ssam if (deltaS == deltaA && 29638361Ssam deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { 29738361Ssam /* special case for echoed terminal traffic */ 29838361Ssam changes = SPECIAL_I; 29938361Ssam cp = new_seq; 30038361Ssam } 30138361Ssam break; 30238361Ssam 30338361Ssam case NEW_S: 30438361Ssam if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { 30538361Ssam /* special case for data xfer */ 30638361Ssam changes = SPECIAL_D; 30738361Ssam cp = new_seq; 30838361Ssam } 30938361Ssam break; 31038361Ssam } 31138361Ssam 31238361Ssam deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id); 31338361Ssam if (deltaS != 1) { 31438361Ssam ENCODEZ(deltaS); 31538361Ssam changes |= NEW_I; 31638361Ssam } 31738361Ssam if (th->th_flags & TH_PUSH) 31838361Ssam changes |= TCP_PUSH_BIT; 31938361Ssam /* 32038361Ssam * Grab the cksum before we overwrite it below. Then update our 32138361Ssam * state with this packet's header. 32238361Ssam */ 32338361Ssam deltaA = ntohs(th->th_sum); 32438361Ssam BCOPY(ip, &cs->cs_ip, hlen); 32538361Ssam 32638361Ssam /* 32738361Ssam * We want to use the original packet as our compressed packet. 32838361Ssam * (cp - new_seq) is the number of bytes we need for compressed 32938361Ssam * sequence numbers. In addition we need one byte for the change 33038361Ssam * mask, one for the connection id and two for the tcp checksum. 33138361Ssam * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how 33238361Ssam * many bytes of the original packet to toss so subtract the two to 33338361Ssam * get the new packet size. 33438361Ssam */ 33538361Ssam deltaS = cp - new_seq; 33638361Ssam cp = (u_char *)ip; 33739948Ssam if (compress_cid == 0 || comp->last_xmit != cs->cs_id) { 33838361Ssam comp->last_xmit = cs->cs_id; 33938361Ssam hlen -= deltaS + 4; 34038374Skarels cp += hlen; 34138374Skarels *cp++ = changes | NEW_C; 34238361Ssam *cp++ = cs->cs_id; 34338361Ssam } else { 34438361Ssam hlen -= deltaS + 3; 34538374Skarels cp += hlen; 34638374Skarels *cp++ = changes; 34738361Ssam } 34839948Ssam m->m_len -= hlen; 34939948Ssam m->m_data += hlen; 35038361Ssam *cp++ = deltaA >> 8; 35138361Ssam *cp++ = deltaA; 35238361Ssam BCOPY(new_seq, cp, deltaS); 35338374Skarels INCR(sls_compressed) 35438361Ssam return (TYPE_COMPRESSED_TCP); 35538361Ssam 35638361Ssam /* 35738361Ssam * Update connection state cs & send uncompressed packet ('uncompressed' 35838361Ssam * means a regular ip/tcp packet but with the 'conversation id' we hope 35938361Ssam * to use on future compressed packets in the protocol field). 36038361Ssam */ 36138361Ssam uncompressed: 36238361Ssam BCOPY(ip, &cs->cs_ip, hlen); 36338361Ssam ip->ip_p = cs->cs_id; 36438361Ssam comp->last_xmit = cs->cs_id; 36538361Ssam return (TYPE_UNCOMPRESSED_TCP); 36638361Ssam } 36738361Ssam 36838361Ssam 36938374Skarels int 37038374Skarels sl_uncompress_tcp(bufp, len, type, comp) 37138374Skarels u_char **bufp; 37238374Skarels int len; 37338374Skarels u_int type; 37438361Ssam struct slcompress *comp; 37538361Ssam { 37638361Ssam register u_char *cp; 37738361Ssam register u_int hlen, changes; 37838361Ssam register struct tcphdr *th; 37938361Ssam register struct cstate *cs; 38038361Ssam register struct ip *ip; 38138361Ssam 38238361Ssam switch (type) { 38338361Ssam 38438361Ssam case TYPE_UNCOMPRESSED_TCP: 38538374Skarels ip = (struct ip *) *bufp; 38639948Ssam if (ip->ip_p >= MAX_STATES) 38739948Ssam goto bad; 38838361Ssam cs = &comp->rstate[comp->last_recv = ip->ip_p]; 38938361Ssam comp->flags &=~ SLF_TOSS; 39038361Ssam ip->ip_p = IPPROTO_TCP; 39138361Ssam hlen = ip->ip_hl; 39238361Ssam hlen += ((struct tcphdr *)&((int *)ip)[hlen])->th_off; 39338361Ssam hlen <<= 2; 39438361Ssam BCOPY(ip, &cs->cs_ip, hlen); 39538361Ssam cs->cs_ip.ip_sum = 0; 39638361Ssam cs->cs_hlen = hlen; 39738374Skarels INCR(sls_uncompressedin) 39838374Skarels return (len); 39938361Ssam 40038374Skarels default: 40139948Ssam goto bad; 40238361Ssam 40338361Ssam case TYPE_COMPRESSED_TCP: 40438361Ssam break; 40538361Ssam } 40638361Ssam /* We've got a compressed packet. */ 40738374Skarels INCR(sls_compressedin) 40838374Skarels cp = *bufp; 40938361Ssam changes = *cp++; 41038361Ssam if (changes & NEW_C) { 41138361Ssam /* Make sure the state index is in range, then grab the state. 41238361Ssam * If we have a good state index, clear the 'discard' flag. */ 41339948Ssam if (*cp >= MAX_STATES) 41439948Ssam goto bad; 41539948Ssam 41638361Ssam comp->flags &=~ SLF_TOSS; 41738361Ssam comp->last_recv = *cp++; 41838361Ssam } else { 41938361Ssam /* this packet has an implicit state index. If we've 42038361Ssam * had a line error since the last time we got an 42138361Ssam * explicit state index, we have to toss the packet. */ 42238374Skarels if (comp->flags & SLF_TOSS) { 42338374Skarels INCR(sls_tossed) 42438374Skarels return (0); 42538374Skarels } 42638361Ssam } 42738361Ssam cs = &comp->rstate[comp->last_recv]; 42838361Ssam hlen = cs->cs_ip.ip_hl << 2; 42938361Ssam th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen]; 43038361Ssam th->th_sum = htons((*cp << 8) | cp[1]); 43138361Ssam cp += 2; 43238361Ssam if (changes & TCP_PUSH_BIT) 43338361Ssam th->th_flags |= TH_PUSH; 43438361Ssam else 43538361Ssam th->th_flags &=~ TH_PUSH; 43638361Ssam 43738361Ssam switch (changes & SPECIALS_MASK) { 43838361Ssam case SPECIAL_I: 43938361Ssam { 44038361Ssam register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; 44138361Ssam th->th_ack = htonl(ntohl(th->th_ack) + i); 44238361Ssam th->th_seq = htonl(ntohl(th->th_seq) + i); 44338361Ssam } 44438361Ssam break; 44538361Ssam 44638361Ssam case SPECIAL_D: 44738361Ssam th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) 44838361Ssam - cs->cs_hlen); 44938361Ssam break; 45038361Ssam 45138361Ssam default: 45238361Ssam if (changes & NEW_U) { 45338361Ssam th->th_flags |= TH_URG; 45439948Ssam DECODEU(th->th_urp) 45538361Ssam } else 45638361Ssam th->th_flags &=~ TH_URG; 45738361Ssam if (changes & NEW_W) 45838361Ssam DECODES(th->th_win) 45938361Ssam if (changes & NEW_A) 46038361Ssam DECODEL(th->th_ack) 46138361Ssam if (changes & NEW_S) 46238361Ssam DECODEL(th->th_seq) 46338361Ssam break; 46438361Ssam } 46538361Ssam if (changes & NEW_I) { 46638361Ssam DECODES(cs->cs_ip.ip_id) 46738361Ssam } else 46838361Ssam cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1); 46938361Ssam 47038361Ssam /* 47138361Ssam * At this point, cp points to the first byte of data in the 47238374Skarels * packet. If we're not aligned on a 4-byte boundary, copy the 47338374Skarels * data down so the ip & tcp headers will be aligned. Then back up 47438374Skarels * cp by the tcp/ip header length to make room for the reconstructed 47538374Skarels * header (we assume the packet we were handed has enough space to 47639948Ssam * prepend 128 bytes of header). Adjust the length to account for 47738374Skarels * the new header & fill in the IP total length. 47838361Ssam */ 47938374Skarels len -= (cp - *bufp); 48039948Ssam if (len < 0) 48138374Skarels /* we must have dropped some characters (crc should detect 48238374Skarels * this but the old slip framing won't) */ 48339948Ssam goto bad; 48439948Ssam 48538379Ssam if ((int)cp & 3) { 48638379Ssam if (len > 0) 48739948Ssam (void) ovbcopy(cp, (caddr_t)((int)cp &~ 3), len); 48838374Skarels cp = (u_char *)((int)cp &~ 3); 48938374Skarels } 49038374Skarels cp -= cs->cs_hlen; 49138374Skarels len += cs->cs_hlen; 49238374Skarels cs->cs_ip.ip_len = htons(len); 49338374Skarels BCOPY(&cs->cs_ip, cp, cs->cs_hlen); 49438374Skarels *bufp = cp; 49538361Ssam 49638374Skarels /* recompute the ip header checksum */ 49738374Skarels { 49838374Skarels register u_short *bp = (u_short *)cp; 49938374Skarels for (changes = 0; hlen > 0; hlen -= 2) 50038374Skarels changes += *bp++; 50138374Skarels changes = (changes & 0xffff) + (changes >> 16); 50238374Skarels changes = (changes & 0xffff) + (changes >> 16); 50338374Skarels ((struct ip *)cp)->ip_sum = ~ changes; 50438374Skarels } 50538374Skarels return (len); 50639948Ssam bad: 50739948Ssam comp->flags |= SLF_TOSS; 50839948Ssam INCR(sls_errorin) 50939948Ssam return (0); 51038361Ssam } 511