138361Ssam /* 2*38379Ssam * @(#)slcompress.c 7.4 (Berkeley) 07/01/89 338374Skarels * 438361Ssam * THIS CODE IS NOT FOR DISTRIBUTION! 538361Ssam * KEEP YOUR GRUBBY HANDS OFF UNLESS AUTHORIZED BY VAN JACOBSON TO COPY! 638361Ssam * ASK SAM, MIKE, OR BILL ABOUT IT. 738374Skarels * 838361Ssam * Routines to compress and uncompess tcp packets (for transmission 938361Ssam * over low speed serial lines. 1038361Ssam * 1138361Ssam * Copyright (c) 1988, 1989 by Van Jacobson, Lawrence Berkeley Laboratory 1238361Ssam * All rights reserved. 1338361Ssam */ 1438361Ssam 1538374Skarels #include "sl.h" 1638374Skarels #if NSL > 0 1738361Ssam #ifndef lint 1838374Skarels static char rcsid[] = "$Header: slcompress.c,v 1.10 89/06/05 08:28:52 van Exp $"; 1938361Ssam #endif 2038361Ssam 2138361Ssam #include <sys/types.h> 2238361Ssam #include <sys/param.h> 2338361Ssam #include <sys/mbuf.h> 2438361Ssam #include <netinet/in.h> 2538361Ssam #include <netinet/in_systm.h> 2638361Ssam #include <netinet/ip.h> 2738361Ssam #include <netinet/tcp.h> 2838361Ssam 2938361Ssam #include "slcompress.h" 3038361Ssam 3138374Skarels #ifndef NO_SL_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)) 3938361Ssam 4038361Ssam 4138361Ssam void 4238361Ssam sl_compress_init(comp) 4338361Ssam struct slcompress *comp; 4438361Ssam { 4538361Ssam register u_int i; 4638361Ssam register struct cstate *tstate = comp->tstate; 4738361Ssam 4838361Ssam bzero((char *)comp, sizeof(*comp)); 4938361Ssam for (i = MAX_STATES - 1; i > 0; --i) { 5038361Ssam tstate[i].cs_id = i; 5138361Ssam tstate[i].cs_next = &tstate[i - 1]; 5238361Ssam } 5338361Ssam tstate[0].cs_next = &tstate[MAX_STATES - 1]; 5438361Ssam tstate[0].cs_id = 0; 5538361Ssam comp->last_cs = &tstate[0]; 5638361Ssam comp->last_recv = 255; 5738361Ssam comp->last_xmit = 255; 5838361Ssam } 5938361Ssam 6038361Ssam 6138361Ssam /* ENCODE encodes a number that is known to be non-zero. ENCODEZ 6238361Ssam * checks for zero (since zero has to be encoded in the long, 3 byte 6338361Ssam * form). 6438361Ssam */ 6538361Ssam #define ENCODE(n) { \ 6638361Ssam if ((u_short)(n) >= 256) { \ 6738361Ssam *cp++ = 0; \ 6838361Ssam cp[1] = (n); \ 6938361Ssam cp[0] = (n) >> 8; \ 7038361Ssam cp += 2; \ 7138361Ssam } else { \ 7238361Ssam *cp++ = (n); \ 7338361Ssam } \ 7438361Ssam } 7538361Ssam #define ENCODEZ(n) { \ 7638361Ssam if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ 7738361Ssam *cp++ = 0; \ 7838361Ssam cp[1] = (n); \ 7938361Ssam cp[0] = (n) >> 8; \ 8038361Ssam cp += 2; \ 8138361Ssam } else { \ 8238361Ssam *cp++ = (n); \ 8338361Ssam } \ 8438361Ssam } 8538361Ssam 8638361Ssam #define DECODEL(f) { \ 8738361Ssam if (*cp == 0) {\ 8838361Ssam (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \ 8938361Ssam cp += 3; \ 9038361Ssam } else { \ 9138361Ssam (f) = htonl(ntohl(f) + (u_long)*cp++); \ 9238361Ssam } \ 9338361Ssam } 9438361Ssam 9538361Ssam #define DECODES(f) { \ 9638361Ssam if (*cp == 0) {\ 9738361Ssam (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \ 9838361Ssam cp += 3; \ 9938361Ssam } else { \ 10038361Ssam (f) = htons(ntohs(f) + (u_long)*cp++); \ 10138361Ssam } \ 10238361Ssam } 10338361Ssam 10438361Ssam 10538361Ssam u_char 10638361Ssam sl_compress_tcp(m, ip, comp) 10738361Ssam struct mbuf *m; 10838361Ssam register struct ip *ip; 10938361Ssam struct slcompress *comp; 11038361Ssam { 11138361Ssam register struct cstate *cs = comp->last_cs->cs_next; 11238361Ssam register u_int hlen = ip->ip_hl; 11338361Ssam register struct tcphdr *oth; 11438361Ssam register struct tcphdr *th; 11538361Ssam register u_int deltaS, deltaA; 11638361Ssam register u_int changes = 0; 11738361Ssam u_char new_seq[16]; 11838361Ssam register u_char *cp = new_seq; 11938361Ssam 12038361Ssam /* 12138361Ssam * Bail if this is an ip fragment or if we don't have 12238361Ssam * a complete ip & tcp header in the first mbuf. Otherwise, 12338361Ssam * check flags to see if this is a packet we might compress 12438361Ssam * and, if so, try to locate the connection state. 12538361Ssam * special case the most recently used connection since 12638361Ssam * it's most likely to be used again & we don't have to 12738361Ssam * do any reordering if it's used. 12838361Ssam */ 12938361Ssam if ((ip->ip_off & 0x3fff) || m->m_len < 40) 13038361Ssam return (TYPE_IP); 13138361Ssam 13238361Ssam th = (struct tcphdr *)&((int *)ip)[hlen]; 13338361Ssam if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK) 13438361Ssam return (TYPE_IP); 13538361Ssam 13638374Skarels INCR(sls_packets) 13738374Skarels if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr || 13838374Skarels ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr || 13938374Skarels *(int *)th != ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) { 14038361Ssam /* 14138361Ssam * Wasn't the first -- search for it. 14238361Ssam * 14338361Ssam * States are kept in a circularly linked list with 14438361Ssam * first_cs pointing to the head of the list. The 14538361Ssam * list is kept in lru order by moving a state to the 14638361Ssam * head of the list whenever it is referenced. Since 14738361Ssam * the list is short and, empirically, the connection 14838361Ssam * we want is almost always near the front, we locate 14938361Ssam * states via linear search. If we don't find a state 15038361Ssam * for the datagram, the oldest state is used. 15138361Ssam */ 15238361Ssam register struct cstate *lcs; 15338361Ssam 15438361Ssam do { 15538361Ssam lcs = cs; cs = cs->cs_next; 15638374Skarels INCR(sls_searches) 15738361Ssam if (*(int *)th == ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl] 15838361Ssam && ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr 15938361Ssam && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr) 16038361Ssam goto found; 16138361Ssam } while (cs != comp->last_cs); 16238374Skarels INCR(sls_misses) 16338361Ssam 16438361Ssam /* 16538361Ssam * Didn't find it -- re-use oldest cstate. 16638361Ssam * Send an uncompressed packet that tells 16738361Ssam * the other side what connection number 16838361Ssam * we're using for this conversation. Note 16938361Ssam * that since the state list is circular, the 17038361Ssam * oldest state points to the newest and we only 17138361Ssam * need to set last_cs to update the lru linkage. 17238361Ssam */ 17338361Ssam comp->last_cs = lcs; 17438361Ssam hlen += th->th_off; 17538361Ssam hlen <<= 2; 17638361Ssam goto uncompressed; 17738361Ssam 17838361Ssam found: 17938361Ssam /* 18038361Ssam * Found it -- move to the front on the connection list. 18138361Ssam */ 18238361Ssam if (comp->last_cs == cs) 18338361Ssam comp->last_cs = lcs; 18438361Ssam else { 18538361Ssam lcs->cs_next = cs->cs_next; 18638361Ssam cs->cs_next = comp->last_cs->cs_next; 18738361Ssam comp->last_cs->cs_next = cs; 18838361Ssam } 18938361Ssam } 19038361Ssam 19138361Ssam /* 19238361Ssam * Make sure that only what we expect to change changed. 19338361Ssam */ 19438361Ssam oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen]; 19538361Ssam deltaS = hlen; 19638361Ssam hlen += th->th_off; 19738361Ssam hlen <<= 2; 19838361Ssam 19938361Ssam if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] || 20038361Ssam ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] || 20138361Ssam th->th_off != oth->th_off || 20238361Ssam (deltaS > 5 && 20338361Ssam BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) || 20438361Ssam (th->th_off > 5 && 20538361Ssam BCMP(th + 1, oth + 1, (th->th_off - 5) << 2))) 20638361Ssam goto uncompressed; 20738361Ssam 20838361Ssam /* 20938361Ssam * Figure out which of the changing fields changed. The 21038361Ssam * receiver expects changes in the order: urgent, window, 21138361Ssam * ack, seq (the order minimizes the number of temporaries 21238361Ssam * needed in this section of code). 21338361Ssam */ 21438361Ssam if (th->th_flags & TH_URG) { 21538361Ssam deltaS = ntohs(th->th_urp); 21638361Ssam ENCODEZ(deltaS); 21738361Ssam changes |= NEW_U; 21838361Ssam } else if (th->th_urp != oth->th_urp) 21938361Ssam /* argh! URG not set but urp changed -- a sensible 22038361Ssam * implementation should never do this but RFC793 22138361Ssam * doesn't prohibit the change so we have to deal 22238361Ssam * with it. */ 22338361Ssam goto uncompressed; 22438361Ssam 22538361Ssam if (deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) { 22638361Ssam ENCODE(deltaS); 22738361Ssam changes |= NEW_W; 22838361Ssam } 22938361Ssam 23038361Ssam if (deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack)) { 23138361Ssam if (deltaA > 0xffff) 23238361Ssam goto uncompressed; 23338361Ssam ENCODE(deltaA); 23438361Ssam changes |= NEW_A; 23538361Ssam } 23638361Ssam 23738361Ssam if (deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq)) { 23838361Ssam if (deltaS > 0xffff) 23938361Ssam goto uncompressed; 24038361Ssam ENCODE(deltaS); 24138361Ssam changes |= NEW_S; 24238361Ssam } 24338361Ssam 24438361Ssam switch(changes) { 24538361Ssam 24638361Ssam case 0: 24738361Ssam if (ip->ip_len != cs->cs_ip.ip_len && ntohs(ip->ip_len) != hlen) 24838361Ssam break; 24938361Ssam /* 25038361Ssam * Nothing changed and this packet looks like a duplicate 25138361Ssam * of the last or contains no data -- this is probably a 25238361Ssam * retransmitted ack or window probe. Send it 25338361Ssam * uncompressed in case the other side missed the 25438361Ssam * compressed version. 25538361Ssam * 25638361Ssam * (fall through) 25738361Ssam */ 25838361Ssam 25938361Ssam case SPECIAL_I: 26038361Ssam case SPECIAL_D: 26138361Ssam /* 26238361Ssam * actual changes match one of our special case encodings -- 26338361Ssam * send packet uncompressed. 26438361Ssam */ 26538361Ssam goto uncompressed; 26638361Ssam 26738361Ssam case NEW_S|NEW_A: 26838361Ssam if (deltaS == deltaA && 26938361Ssam deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { 27038361Ssam /* special case for echoed terminal traffic */ 27138361Ssam changes = SPECIAL_I; 27238361Ssam cp = new_seq; 27338361Ssam } 27438361Ssam break; 27538361Ssam 27638361Ssam case NEW_S: 27738361Ssam if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { 27838361Ssam /* special case for data xfer */ 27938361Ssam changes = SPECIAL_D; 28038361Ssam cp = new_seq; 28138361Ssam } 28238361Ssam break; 28338361Ssam } 28438361Ssam 28538361Ssam deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id); 28638361Ssam if (deltaS != 1) { 28738361Ssam ENCODEZ(deltaS); 28838361Ssam changes |= NEW_I; 28938361Ssam } 29038361Ssam if (th->th_flags & TH_PUSH) 29138361Ssam changes |= TCP_PUSH_BIT; 29238361Ssam /* 29338361Ssam * Grab the cksum before we overwrite it below. Then update our 29438361Ssam * state with this packet's header. 29538361Ssam */ 29638361Ssam deltaA = ntohs(th->th_sum); 29738361Ssam BCOPY(ip, &cs->cs_ip, hlen); 29838361Ssam 29938361Ssam /* 30038361Ssam * We want to use the original packet as our compressed packet. 30138361Ssam * (cp - new_seq) is the number of bytes we need for compressed 30238361Ssam * sequence numbers. In addition we need one byte for the change 30338361Ssam * mask, one for the connection id and two for the tcp checksum. 30438361Ssam * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how 30538361Ssam * many bytes of the original packet to toss so subtract the two to 30638361Ssam * get the new packet size. 30738361Ssam */ 30838361Ssam deltaS = cp - new_seq; 30938361Ssam cp = (u_char *)ip; 31038361Ssam if (comp->last_xmit != cs->cs_id) { 31138361Ssam comp->last_xmit = cs->cs_id; 31238361Ssam hlen -= deltaS + 4; 31338374Skarels cp += hlen; 31438374Skarels m->m_len -= hlen; 31538374Skarels m->m_data += hlen; 31638374Skarels *cp++ = changes | NEW_C; 31738361Ssam *cp++ = cs->cs_id; 31838361Ssam } else { 31938361Ssam hlen -= deltaS + 3; 32038374Skarels cp += hlen; 32138374Skarels m->m_len -= hlen; 32238374Skarels m->m_data += hlen; 32338374Skarels *cp++ = changes; 32438361Ssam } 32538361Ssam *cp++ = deltaA >> 8; 32638361Ssam *cp++ = deltaA; 32738361Ssam BCOPY(new_seq, cp, deltaS); 32838374Skarels INCR(sls_compressed) 32938361Ssam return (TYPE_COMPRESSED_TCP); 33038361Ssam 33138361Ssam /* 33238361Ssam * Update connection state cs & send uncompressed packet ('uncompressed' 33338361Ssam * means a regular ip/tcp packet but with the 'conversation id' we hope 33438361Ssam * to use on future compressed packets in the protocol field). 33538361Ssam */ 33638361Ssam uncompressed: 33738361Ssam BCOPY(ip, &cs->cs_ip, hlen); 33838361Ssam ip->ip_p = cs->cs_id; 33938361Ssam comp->last_xmit = cs->cs_id; 34038361Ssam return (TYPE_UNCOMPRESSED_TCP); 34138361Ssam } 34238361Ssam 34338361Ssam 34438374Skarels int 34538374Skarels sl_uncompress_tcp(bufp, len, type, comp) 34638374Skarels u_char **bufp; 34738374Skarels int len; 34838374Skarels u_int type; 34938361Ssam struct slcompress *comp; 35038361Ssam { 35138361Ssam register u_char *cp; 35238361Ssam register u_int hlen, changes; 35338361Ssam register struct tcphdr *th; 35438361Ssam register struct cstate *cs; 35538361Ssam register struct ip *ip; 35638361Ssam 35738361Ssam switch (type) { 35838361Ssam 35938361Ssam case TYPE_UNCOMPRESSED_TCP: 36038374Skarels ip = (struct ip *) *bufp; 36138374Skarels if (ip->ip_p >= MAX_STATES) { 36238374Skarels INCR(sls_errorin) 36338374Skarels return (0); 36438374Skarels } 36538361Ssam cs = &comp->rstate[comp->last_recv = ip->ip_p]; 36638361Ssam comp->flags &=~ SLF_TOSS; 36738361Ssam ip->ip_p = IPPROTO_TCP; 36838361Ssam hlen = ip->ip_hl; 36938361Ssam hlen += ((struct tcphdr *)&((int *)ip)[hlen])->th_off; 37038361Ssam hlen <<= 2; 37138361Ssam BCOPY(ip, &cs->cs_ip, hlen); 37238361Ssam cs->cs_ip.ip_sum = 0; 37338361Ssam cs->cs_hlen = hlen; 37438374Skarels INCR(sls_uncompressedin) 37538374Skarels return (len); 37638361Ssam 37738361Ssam case TYPE_ERROR: 37838361Ssam comp->flags |= SLF_TOSS; 37938374Skarels default: 38038374Skarels INCR(sls_errorin) 38138374Skarels return (0); 38238361Ssam 38338361Ssam case TYPE_COMPRESSED_TCP: 38438361Ssam break; 38538361Ssam } 38638361Ssam /* We've got a compressed packet. */ 38738374Skarels INCR(sls_compressedin) 38838374Skarels cp = *bufp; 38938361Ssam changes = *cp++; 39038361Ssam if (changes & NEW_C) { 39138361Ssam /* Make sure the state index is in range, then grab the state. 39238361Ssam * If we have a good state index, clear the 'discard' flag. */ 39338374Skarels if (*cp >= MAX_STATES) { 39438374Skarels INCR(sls_errorin) 39538374Skarels return (0); 39638374Skarels } 39738361Ssam comp->flags &=~ SLF_TOSS; 39838361Ssam comp->last_recv = *cp++; 39938361Ssam } else { 40038361Ssam /* this packet has an implicit state index. If we've 40138361Ssam * had a line error since the last time we got an 40238361Ssam * explicit state index, we have to toss the packet. */ 40338374Skarels if (comp->flags & SLF_TOSS) { 40438374Skarels INCR(sls_tossed) 40538374Skarels return (0); 40638374Skarels } 40738361Ssam } 40838361Ssam cs = &comp->rstate[comp->last_recv]; 40938361Ssam hlen = cs->cs_ip.ip_hl << 2; 41038361Ssam th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen]; 41138361Ssam th->th_sum = htons((*cp << 8) | cp[1]); 41238361Ssam cp += 2; 41338361Ssam if (changes & TCP_PUSH_BIT) 41438361Ssam th->th_flags |= TH_PUSH; 41538361Ssam else 41638361Ssam th->th_flags &=~ TH_PUSH; 41738361Ssam 41838361Ssam switch (changes & SPECIALS_MASK) { 41938361Ssam case SPECIAL_I: 42038361Ssam { 42138361Ssam register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; 42238361Ssam th->th_ack = htonl(ntohl(th->th_ack) + i); 42338361Ssam th->th_seq = htonl(ntohl(th->th_seq) + i); 42438361Ssam } 42538361Ssam break; 42638361Ssam 42738361Ssam case SPECIAL_D: 42838361Ssam th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) 42938361Ssam - cs->cs_hlen); 43038361Ssam break; 43138361Ssam 43238361Ssam default: 43338361Ssam if (changes & NEW_U) { 43438361Ssam th->th_flags |= TH_URG; 43538361Ssam DECODES(th->th_urp) 43638361Ssam } else 43738361Ssam th->th_flags &=~ TH_URG; 43838361Ssam if (changes & NEW_W) 43938361Ssam DECODES(th->th_win) 44038361Ssam if (changes & NEW_A) 44138361Ssam DECODEL(th->th_ack) 44238361Ssam if (changes & NEW_S) 44338361Ssam DECODEL(th->th_seq) 44438361Ssam break; 44538361Ssam } 44638361Ssam if (changes & NEW_I) { 44738361Ssam DECODES(cs->cs_ip.ip_id) 44838361Ssam } else 44938361Ssam cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1); 45038361Ssam 45138361Ssam /* 45238361Ssam * At this point, cp points to the first byte of data in the 45338374Skarels * packet. If we're not aligned on a 4-byte boundary, copy the 45438374Skarels * data down so the ip & tcp headers will be aligned. Then back up 45538374Skarels * cp by the tcp/ip header length to make room for the reconstructed 45638374Skarels * header (we assume the packet we were handed has enough space to 45738374Skarels * prepend 128 bytes of header). Adjust the lenth to account for 45838374Skarels * the new header & fill in the IP total length. 45938361Ssam */ 46038374Skarels len -= (cp - *bufp); 46138374Skarels if (len < 0) { 46238374Skarels /* we must have dropped some characters (crc should detect 46338374Skarels * this but the old slip framing won't) */ 46438374Skarels INCR(sls_errorin) 46538374Skarels return (0); 46638374Skarels } 467*38379Ssam if ((int)cp & 3) { 468*38379Ssam if (len > 0) 469*38379Ssam BCOPY(cp, (int)cp &~ 3, len); 47038374Skarels cp = (u_char *)((int)cp &~ 3); 47138374Skarels } 47238374Skarels cp -= cs->cs_hlen; 47338374Skarels len += cs->cs_hlen; 47438374Skarels cs->cs_ip.ip_len = htons(len); 47538374Skarels BCOPY(&cs->cs_ip, cp, cs->cs_hlen); 47638374Skarels *bufp = cp; 47738361Ssam 47838374Skarels /* recompute the ip header checksum */ 47938374Skarels { 48038374Skarels register u_short *bp = (u_short *)cp; 48138374Skarels for (changes = 0; hlen > 0; hlen -= 2) 48238374Skarels changes += *bp++; 48338374Skarels changes = (changes & 0xffff) + (changes >> 16); 48438374Skarels changes = (changes & 0xffff) + (changes >> 16); 48538374Skarels ((struct ip *)cp)->ip_sum = ~ changes; 48638374Skarels } 48738374Skarels return (len); 48838361Ssam } 48938374Skarels #endif 490