1*49340Sbostic /*- 2*49340Sbostic * Copyright (c) 1989 The Regents of the University of California. 3*49340Sbostic * All rights reserved. 4*49340Sbostic * 5*49340Sbostic * %sccs.include.redist.c% 6*49340Sbostic * 7*49340Sbostic * @(#)slcompress.c 7.7 (Berkeley) 05/07/91 8*49340Sbostic */ 9*49340Sbostic 1038361Ssam /* 1138361Ssam * Routines to compress and uncompess tcp packets (for transmission 1238361Ssam * over low speed serial lines. 1338361Ssam * 14*49340Sbostic * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: 15*49340Sbostic * - Initial distribution. 1639948Ssam * 17*49340Sbostic * static char rcsid[] = 18*49340Sbostic * "$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> 2338361Ssam #include <netinet/in.h> 2438361Ssam #include <netinet/in_systm.h> 2538361Ssam #include <netinet/ip.h> 2638361Ssam #include <netinet/tcp.h> 2738361Ssam 2838361Ssam #include "slcompress.h" 2938361Ssam 3039948Ssam #ifndef SL_NO_STATS 3138374Skarels #define INCR(counter) ++comp->counter; 3238374Skarels #else 3338374Skarels #define INCR(counter) 3438374Skarels #endif 3538361Ssam 3638361Ssam #define BCMP(p1, p2, n) bcmp((char *)(p1), (char *)(p2), (int)(n)) 3738361Ssam #define BCOPY(p1, p2, n) bcopy((char *)(p1), (char *)(p2), (int)(n)) 3839948Ssam #ifndef KERNEL 3939948Ssam #define ovbcopy bcopy 4039948Ssam #endif 4138361Ssam 4238361Ssam 4338361Ssam void 4438361Ssam sl_compress_init(comp) 4538361Ssam struct slcompress *comp; 4638361Ssam { 4738361Ssam register u_int i; 4838361Ssam register struct cstate *tstate = comp->tstate; 4938361Ssam 5038361Ssam bzero((char *)comp, sizeof(*comp)); 5138361Ssam for (i = MAX_STATES - 1; i > 0; --i) { 5238361Ssam tstate[i].cs_id = i; 5338361Ssam tstate[i].cs_next = &tstate[i - 1]; 5438361Ssam } 5538361Ssam tstate[0].cs_next = &tstate[MAX_STATES - 1]; 5638361Ssam tstate[0].cs_id = 0; 5738361Ssam comp->last_cs = &tstate[0]; 5838361Ssam comp->last_recv = 255; 5938361Ssam comp->last_xmit = 255; 6038361Ssam } 6138361Ssam 6238361Ssam 6338361Ssam /* ENCODE encodes a number that is known to be non-zero. ENCODEZ 6438361Ssam * checks for zero (since zero has to be encoded in the long, 3 byte 6538361Ssam * form). 6638361Ssam */ 6738361Ssam #define ENCODE(n) { \ 6838361Ssam if ((u_short)(n) >= 256) { \ 6938361Ssam *cp++ = 0; \ 7038361Ssam cp[1] = (n); \ 7138361Ssam cp[0] = (n) >> 8; \ 7238361Ssam cp += 2; \ 7338361Ssam } else { \ 7438361Ssam *cp++ = (n); \ 7538361Ssam } \ 7638361Ssam } 7738361Ssam #define ENCODEZ(n) { \ 7838361Ssam if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ 7938361Ssam *cp++ = 0; \ 8038361Ssam cp[1] = (n); \ 8138361Ssam cp[0] = (n) >> 8; \ 8238361Ssam cp += 2; \ 8338361Ssam } else { \ 8438361Ssam *cp++ = (n); \ 8538361Ssam } \ 8638361Ssam } 8738361Ssam 8838361Ssam #define DECODEL(f) { \ 8938361Ssam if (*cp == 0) {\ 9038361Ssam (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \ 9138361Ssam cp += 3; \ 9238361Ssam } else { \ 9338361Ssam (f) = htonl(ntohl(f) + (u_long)*cp++); \ 9438361Ssam } \ 9538361Ssam } 9638361Ssam 9738361Ssam #define DECODES(f) { \ 9838361Ssam if (*cp == 0) {\ 9938361Ssam (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \ 10038361Ssam cp += 3; \ 10138361Ssam } else { \ 10238361Ssam (f) = htons(ntohs(f) + (u_long)*cp++); \ 10338361Ssam } \ 10438361Ssam } 10538361Ssam 10639948Ssam #define DECODEU(f) { \ 10739948Ssam if (*cp == 0) {\ 10839948Ssam (f) = htons((cp[1] << 8) | cp[2]); \ 10939948Ssam cp += 3; \ 11039948Ssam } else { \ 11139948Ssam (f) = htons((u_long)*cp++); \ 11239948Ssam } \ 11339948Ssam } 11438361Ssam 11539948Ssam 11638361Ssam u_char 11739948Ssam sl_compress_tcp(m, ip, comp, compress_cid) 11838361Ssam struct mbuf *m; 11938361Ssam register struct ip *ip; 12038361Ssam struct slcompress *comp; 12139948Ssam int compress_cid; 12238361Ssam { 12338361Ssam register struct cstate *cs = comp->last_cs->cs_next; 12438361Ssam register u_int hlen = ip->ip_hl; 12538361Ssam register struct tcphdr *oth; 12638361Ssam register struct tcphdr *th; 12738361Ssam register u_int deltaS, deltaA; 12838361Ssam register u_int changes = 0; 12938361Ssam u_char new_seq[16]; 13038361Ssam register u_char *cp = new_seq; 13138361Ssam 13238361Ssam /* 13339948Ssam * Bail if this is an IP fragment or if the TCP packet isn't 13439948Ssam * `compressible' (i.e., ACK isn't set or some other control bit is 13539948Ssam * set). (We assume that the caller has already made sure the 13639948Ssam * packet is IP proto TCP). 13738361Ssam */ 13839948Ssam if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40) 13938361Ssam return (TYPE_IP); 14038361Ssam 14138361Ssam th = (struct tcphdr *)&((int *)ip)[hlen]; 14238361Ssam if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK) 14338361Ssam return (TYPE_IP); 14439948Ssam /* 14539948Ssam * Packet is compressible -- we're going to send either a 14639948Ssam * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need 14739948Ssam * to locate (or create) the connection state. Special case the 14839948Ssam * most recently used connection since it's most likely to be used 14939948Ssam * again & we don't have to do any reordering if it's used. 15039948Ssam */ 15138374Skarels INCR(sls_packets) 15238374Skarels if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr || 15338374Skarels ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr || 15438374Skarels *(int *)th != ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) { 15538361Ssam /* 15638361Ssam * Wasn't the first -- search for it. 15738361Ssam * 15838361Ssam * States are kept in a circularly linked list with 15939948Ssam * last_cs pointing to the end of the list. The 16038361Ssam * list is kept in lru order by moving a state to the 16138361Ssam * head of the list whenever it is referenced. Since 16238361Ssam * the list is short and, empirically, the connection 16338361Ssam * we want is almost always near the front, we locate 16438361Ssam * states via linear search. If we don't find a state 16539948Ssam * for the datagram, the oldest state is (re-)used. 16638361Ssam */ 16738361Ssam register struct cstate *lcs; 16839948Ssam register struct cstate *lastcs = comp->last_cs; 16938361Ssam 17038361Ssam do { 17138361Ssam lcs = cs; cs = cs->cs_next; 17238374Skarels INCR(sls_searches) 17339948Ssam if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr 17439948Ssam && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr 17539948Ssam && *(int *)th == ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) 17638361Ssam goto found; 17739948Ssam } while (cs != lastcs); 17838361Ssam 17938361Ssam /* 18039948Ssam * Didn't find it -- re-use oldest cstate. Send an 18139948Ssam * uncompressed packet that tells the other side what 18239948Ssam * connection number we're using for this conversation. 18339948Ssam * Note that since the state list is circular, the oldest 18439948Ssam * state points to the newest and we only need to set 18539948Ssam * last_cs to update the lru linkage. 18638361Ssam */ 18739948Ssam INCR(sls_misses) 18838361Ssam comp->last_cs = lcs; 18938361Ssam hlen += th->th_off; 19038361Ssam hlen <<= 2; 19138361Ssam goto uncompressed; 19238361Ssam 19338361Ssam found: 19438361Ssam /* 19538361Ssam * Found it -- move to the front on the connection list. 19638361Ssam */ 19739948Ssam if (cs == lastcs) 19838361Ssam comp->last_cs = lcs; 19938361Ssam else { 20038361Ssam lcs->cs_next = cs->cs_next; 20139948Ssam cs->cs_next = lastcs->cs_next; 20239948Ssam lastcs->cs_next = cs; 20338361Ssam } 20438361Ssam } 20538361Ssam 20638361Ssam /* 20739948Ssam * Make sure that only what we expect to change changed. The first 20839948Ssam * line of the `if' checks the IP protocol version, header length & 20939948Ssam * type of service. The 2nd line checks the "Don't fragment" bit. 21039948Ssam * The 3rd line checks the time-to-live and protocol (the protocol 21139948Ssam * check is unnecessary but costless). The 4th line checks the TCP 21239948Ssam * header length. The 5th line checks IP options, if any. The 6th 21339948Ssam * line checks TCP options, if any. If any of these things are 21439948Ssam * different between the previous & current datagram, we send the 21539948Ssam * current datagram `uncompressed'. 21638361Ssam */ 21738361Ssam oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen]; 21838361Ssam deltaS = hlen; 21938361Ssam hlen += th->th_off; 22038361Ssam hlen <<= 2; 22138361Ssam 22238361Ssam if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] || 22339948Ssam ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] || 22438361Ssam ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] || 22538361Ssam th->th_off != oth->th_off || 22638361Ssam (deltaS > 5 && 22738361Ssam BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) || 22838361Ssam (th->th_off > 5 && 22938361Ssam BCMP(th + 1, oth + 1, (th->th_off - 5) << 2))) 23038361Ssam goto uncompressed; 23138361Ssam 23238361Ssam /* 23338361Ssam * Figure out which of the changing fields changed. The 23438361Ssam * receiver expects changes in the order: urgent, window, 23538361Ssam * ack, seq (the order minimizes the number of temporaries 23638361Ssam * needed in this section of code). 23738361Ssam */ 23838361Ssam if (th->th_flags & TH_URG) { 23938361Ssam deltaS = ntohs(th->th_urp); 24038361Ssam ENCODEZ(deltaS); 24138361Ssam changes |= NEW_U; 24238361Ssam } else if (th->th_urp != oth->th_urp) 24338361Ssam /* argh! URG not set but urp changed -- a sensible 24438361Ssam * implementation should never do this but RFC793 24538361Ssam * doesn't prohibit the change so we have to deal 24639948Ssam * with it. */ 24738361Ssam goto uncompressed; 24838361Ssam 24938361Ssam if (deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) { 25038361Ssam ENCODE(deltaS); 25138361Ssam changes |= NEW_W; 25238361Ssam } 25338361Ssam 25438361Ssam if (deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack)) { 25538361Ssam if (deltaA > 0xffff) 25638361Ssam goto uncompressed; 25738361Ssam ENCODE(deltaA); 25838361Ssam changes |= NEW_A; 25938361Ssam } 26038361Ssam 26138361Ssam if (deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq)) { 26238361Ssam if (deltaS > 0xffff) 26338361Ssam goto uncompressed; 26438361Ssam ENCODE(deltaS); 26538361Ssam changes |= NEW_S; 26638361Ssam } 26738361Ssam 26838361Ssam switch(changes) { 26938361Ssam 27038361Ssam case 0: 27138361Ssam /* 27239948Ssam * Nothing changed. If this packet contains data and the 27339948Ssam * last one didn't, this is probably a data packet following 27439948Ssam * an ack (normal on an interactive connection) and we send 27539948Ssam * it compressed. Otherwise it's probably a retransmit, 27639948Ssam * retransmitted ack or window probe. Send it uncompressed 27739948Ssam * in case the other side missed the compressed version. 27838361Ssam */ 27939948Ssam if (ip->ip_len != cs->cs_ip.ip_len && 28039948Ssam ntohs(cs->cs_ip.ip_len) == hlen) 28139948Ssam break; 28238361Ssam 28339948Ssam /* (fall through) */ 28439948Ssam 28538361Ssam case SPECIAL_I: 28638361Ssam case SPECIAL_D: 28738361Ssam /* 28838361Ssam * actual changes match one of our special case encodings -- 28938361Ssam * send packet uncompressed. 29038361Ssam */ 29138361Ssam goto uncompressed; 29238361Ssam 29338361Ssam case NEW_S|NEW_A: 29438361Ssam if (deltaS == deltaA && 29538361Ssam deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { 29638361Ssam /* special case for echoed terminal traffic */ 29738361Ssam changes = SPECIAL_I; 29838361Ssam cp = new_seq; 29938361Ssam } 30038361Ssam break; 30138361Ssam 30238361Ssam case NEW_S: 30338361Ssam if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { 30438361Ssam /* special case for data xfer */ 30538361Ssam changes = SPECIAL_D; 30638361Ssam cp = new_seq; 30738361Ssam } 30838361Ssam break; 30938361Ssam } 31038361Ssam 31138361Ssam deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id); 31238361Ssam if (deltaS != 1) { 31338361Ssam ENCODEZ(deltaS); 31438361Ssam changes |= NEW_I; 31538361Ssam } 31638361Ssam if (th->th_flags & TH_PUSH) 31738361Ssam changes |= TCP_PUSH_BIT; 31838361Ssam /* 31938361Ssam * Grab the cksum before we overwrite it below. Then update our 32038361Ssam * state with this packet's header. 32138361Ssam */ 32238361Ssam deltaA = ntohs(th->th_sum); 32338361Ssam BCOPY(ip, &cs->cs_ip, hlen); 32438361Ssam 32538361Ssam /* 32638361Ssam * We want to use the original packet as our compressed packet. 32738361Ssam * (cp - new_seq) is the number of bytes we need for compressed 32838361Ssam * sequence numbers. In addition we need one byte for the change 32938361Ssam * mask, one for the connection id and two for the tcp checksum. 33038361Ssam * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how 33138361Ssam * many bytes of the original packet to toss so subtract the two to 33238361Ssam * get the new packet size. 33338361Ssam */ 33438361Ssam deltaS = cp - new_seq; 33538361Ssam cp = (u_char *)ip; 33639948Ssam if (compress_cid == 0 || comp->last_xmit != cs->cs_id) { 33738361Ssam comp->last_xmit = cs->cs_id; 33838361Ssam hlen -= deltaS + 4; 33938374Skarels cp += hlen; 34038374Skarels *cp++ = changes | NEW_C; 34138361Ssam *cp++ = cs->cs_id; 34238361Ssam } else { 34338361Ssam hlen -= deltaS + 3; 34438374Skarels cp += hlen; 34538374Skarels *cp++ = changes; 34638361Ssam } 34739948Ssam m->m_len -= hlen; 34839948Ssam m->m_data += hlen; 34938361Ssam *cp++ = deltaA >> 8; 35038361Ssam *cp++ = deltaA; 35138361Ssam BCOPY(new_seq, cp, deltaS); 35238374Skarels INCR(sls_compressed) 35338361Ssam return (TYPE_COMPRESSED_TCP); 35438361Ssam 35538361Ssam /* 35638361Ssam * Update connection state cs & send uncompressed packet ('uncompressed' 35738361Ssam * means a regular ip/tcp packet but with the 'conversation id' we hope 35838361Ssam * to use on future compressed packets in the protocol field). 35938361Ssam */ 36038361Ssam uncompressed: 36138361Ssam BCOPY(ip, &cs->cs_ip, hlen); 36238361Ssam ip->ip_p = cs->cs_id; 36338361Ssam comp->last_xmit = cs->cs_id; 36438361Ssam return (TYPE_UNCOMPRESSED_TCP); 36538361Ssam } 36638361Ssam 36738361Ssam 36838374Skarels int 36938374Skarels sl_uncompress_tcp(bufp, len, type, comp) 37038374Skarels u_char **bufp; 37138374Skarels int len; 37238374Skarels u_int type; 37338361Ssam struct slcompress *comp; 37438361Ssam { 37538361Ssam register u_char *cp; 37638361Ssam register u_int hlen, changes; 37738361Ssam register struct tcphdr *th; 37838361Ssam register struct cstate *cs; 37938361Ssam register struct ip *ip; 38038361Ssam 38138361Ssam switch (type) { 38238361Ssam 38338361Ssam case TYPE_UNCOMPRESSED_TCP: 38438374Skarels ip = (struct ip *) *bufp; 38539948Ssam if (ip->ip_p >= MAX_STATES) 38639948Ssam goto bad; 38738361Ssam cs = &comp->rstate[comp->last_recv = ip->ip_p]; 38838361Ssam comp->flags &=~ SLF_TOSS; 38938361Ssam ip->ip_p = IPPROTO_TCP; 39038361Ssam hlen = ip->ip_hl; 39138361Ssam hlen += ((struct tcphdr *)&((int *)ip)[hlen])->th_off; 39238361Ssam hlen <<= 2; 39338361Ssam BCOPY(ip, &cs->cs_ip, hlen); 39438361Ssam cs->cs_ip.ip_sum = 0; 39538361Ssam cs->cs_hlen = hlen; 39638374Skarels INCR(sls_uncompressedin) 39738374Skarels return (len); 39838361Ssam 39938374Skarels default: 40039948Ssam goto bad; 40138361Ssam 40238361Ssam case TYPE_COMPRESSED_TCP: 40338361Ssam break; 40438361Ssam } 40538361Ssam /* We've got a compressed packet. */ 40638374Skarels INCR(sls_compressedin) 40738374Skarels cp = *bufp; 40838361Ssam changes = *cp++; 40938361Ssam if (changes & NEW_C) { 41038361Ssam /* Make sure the state index is in range, then grab the state. 41138361Ssam * If we have a good state index, clear the 'discard' flag. */ 41239948Ssam if (*cp >= MAX_STATES) 41339948Ssam goto bad; 41439948Ssam 41538361Ssam comp->flags &=~ SLF_TOSS; 41638361Ssam comp->last_recv = *cp++; 41738361Ssam } else { 41838361Ssam /* this packet has an implicit state index. If we've 41938361Ssam * had a line error since the last time we got an 42038361Ssam * explicit state index, we have to toss the packet. */ 42138374Skarels if (comp->flags & SLF_TOSS) { 42238374Skarels INCR(sls_tossed) 42338374Skarels return (0); 42438374Skarels } 42538361Ssam } 42638361Ssam cs = &comp->rstate[comp->last_recv]; 42738361Ssam hlen = cs->cs_ip.ip_hl << 2; 42838361Ssam th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen]; 42938361Ssam th->th_sum = htons((*cp << 8) | cp[1]); 43038361Ssam cp += 2; 43138361Ssam if (changes & TCP_PUSH_BIT) 43238361Ssam th->th_flags |= TH_PUSH; 43338361Ssam else 43438361Ssam th->th_flags &=~ TH_PUSH; 43538361Ssam 43638361Ssam switch (changes & SPECIALS_MASK) { 43738361Ssam case SPECIAL_I: 43838361Ssam { 43938361Ssam register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; 44038361Ssam th->th_ack = htonl(ntohl(th->th_ack) + i); 44138361Ssam th->th_seq = htonl(ntohl(th->th_seq) + i); 44238361Ssam } 44338361Ssam break; 44438361Ssam 44538361Ssam case SPECIAL_D: 44638361Ssam th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) 44738361Ssam - cs->cs_hlen); 44838361Ssam break; 44938361Ssam 45038361Ssam default: 45138361Ssam if (changes & NEW_U) { 45238361Ssam th->th_flags |= TH_URG; 45339948Ssam DECODEU(th->th_urp) 45438361Ssam } else 45538361Ssam th->th_flags &=~ TH_URG; 45638361Ssam if (changes & NEW_W) 45738361Ssam DECODES(th->th_win) 45838361Ssam if (changes & NEW_A) 45938361Ssam DECODEL(th->th_ack) 46038361Ssam if (changes & NEW_S) 46138361Ssam DECODEL(th->th_seq) 46238361Ssam break; 46338361Ssam } 46438361Ssam if (changes & NEW_I) { 46538361Ssam DECODES(cs->cs_ip.ip_id) 46638361Ssam } else 46738361Ssam cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1); 46838361Ssam 46938361Ssam /* 47038361Ssam * At this point, cp points to the first byte of data in the 47138374Skarels * packet. If we're not aligned on a 4-byte boundary, copy the 47238374Skarels * data down so the ip & tcp headers will be aligned. Then back up 47338374Skarels * cp by the tcp/ip header length to make room for the reconstructed 47438374Skarels * header (we assume the packet we were handed has enough space to 47539948Ssam * prepend 128 bytes of header). Adjust the length to account for 47638374Skarels * the new header & fill in the IP total length. 47738361Ssam */ 47838374Skarels len -= (cp - *bufp); 47939948Ssam if (len < 0) 48038374Skarels /* we must have dropped some characters (crc should detect 48138374Skarels * this but the old slip framing won't) */ 48239948Ssam goto bad; 48339948Ssam 48438379Ssam if ((int)cp & 3) { 48538379Ssam if (len > 0) 48639948Ssam (void) ovbcopy(cp, (caddr_t)((int)cp &~ 3), len); 48738374Skarels cp = (u_char *)((int)cp &~ 3); 48838374Skarels } 48938374Skarels cp -= cs->cs_hlen; 49038374Skarels len += cs->cs_hlen; 49138374Skarels cs->cs_ip.ip_len = htons(len); 49238374Skarels BCOPY(&cs->cs_ip, cp, cs->cs_hlen); 49338374Skarels *bufp = cp; 49438361Ssam 49538374Skarels /* recompute the ip header checksum */ 49638374Skarels { 49738374Skarels register u_short *bp = (u_short *)cp; 49838374Skarels for (changes = 0; hlen > 0; hlen -= 2) 49938374Skarels changes += *bp++; 50038374Skarels changes = (changes & 0xffff) + (changes >> 16); 50138374Skarels changes = (changes & 0xffff) + (changes >> 16); 50238374Skarels ((struct ip *)cp)->ip_sum = ~ changes; 50338374Skarels } 50438374Skarels return (len); 50539948Ssam bad: 50639948Ssam comp->flags |= SLF_TOSS; 50739948Ssam INCR(sls_errorin) 50839948Ssam return (0); 50938361Ssam } 510