1df8bae1dSRodney W. Grimes /*- 2df8bae1dSRodney W. Grimes * Copyright (c) 1989, 1993, 1994 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * 5df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 6df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 7df8bae1dSRodney W. Grimes * are met: 8df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 9df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 10df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 11df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 12df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 13df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 14df8bae1dSRodney W. Grimes * must display the following acknowledgement: 15df8bae1dSRodney W. Grimes * This product includes software developed by the University of 16df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 17df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 18df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 19df8bae1dSRodney W. Grimes * without specific prior written permission. 20df8bae1dSRodney W. Grimes * 21df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31df8bae1dSRodney W. Grimes * SUCH DAMAGE. 32df8bae1dSRodney W. Grimes * 33df8bae1dSRodney W. Grimes * @(#)slcompress.c 8.2 (Berkeley) 4/16/94 34c3aac50fSPeter Wemm * $FreeBSD$ 35df8bae1dSRodney W. Grimes */ 36df8bae1dSRodney W. Grimes 37df8bae1dSRodney W. Grimes /* 38df8bae1dSRodney W. Grimes * Routines to compress and uncompess tcp packets (for transmission 39df8bae1dSRodney W. Grimes * over low speed serial lines. 40df8bae1dSRodney W. Grimes * 41df8bae1dSRodney W. Grimes * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: 42df8bae1dSRodney W. Grimes * - Initial distribution. 43df8bae1dSRodney W. Grimes * 44df8bae1dSRodney W. Grimes */ 45df8bae1dSRodney W. Grimes 46df8bae1dSRodney W. Grimes #include <sys/param.h> 47df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 482d4b190bSPeter Wemm #include <sys/systm.h> 49df8bae1dSRodney W. Grimes 50df8bae1dSRodney W. Grimes #include <netinet/in.h> 51df8bae1dSRodney W. Grimes #include <netinet/in_systm.h> 52df8bae1dSRodney W. Grimes #include <netinet/ip.h> 53df8bae1dSRodney W. Grimes #include <netinet/tcp.h> 54df8bae1dSRodney W. Grimes 55df8bae1dSRodney W. Grimes #include <net/slcompress.h> 56df8bae1dSRodney W. Grimes 57df8bae1dSRodney W. Grimes #ifndef SL_NO_STATS 58df8bae1dSRodney W. Grimes #define INCR(counter) ++comp->counter; 59df8bae1dSRodney W. Grimes #else 60df8bae1dSRodney W. Grimes #define INCR(counter) 61df8bae1dSRodney W. Grimes #endif 62df8bae1dSRodney W. Grimes 637283c975SDag-Erling Smørgrav #define BCMP(p1, p2, n) bcmp((void *)(p1), (void *)(p2), (int)(n)) 647283c975SDag-Erling Smørgrav #define BCOPY(p1, p2, n) bcopy((void *)(p1), (void *)(p2), (int)(n)) 65df8bae1dSRodney W. Grimes 66df8bae1dSRodney W. Grimes void 6771b9cf73SPeter Wemm sl_compress_init(comp, max_state) 68df8bae1dSRodney W. Grimes struct slcompress *comp; 6971b9cf73SPeter Wemm int max_state; 70df8bae1dSRodney W. Grimes { 71df8bae1dSRodney W. Grimes register u_int i; 72df8bae1dSRodney W. Grimes register struct cstate *tstate = comp->tstate; 73df8bae1dSRodney W. Grimes 742d4b190bSPeter Wemm if (max_state == -1) { 7571b9cf73SPeter Wemm max_state = MAX_STATES - 1; 76df8bae1dSRodney W. Grimes bzero((char *)comp, sizeof(*comp)); 772d4b190bSPeter Wemm } else { 782d4b190bSPeter Wemm /* Don't reset statistics */ 792d4b190bSPeter Wemm bzero((char *)comp->tstate, sizeof(comp->tstate)); 802d4b190bSPeter Wemm bzero((char *)comp->rstate, sizeof(comp->rstate)); 812d4b190bSPeter Wemm } 8271b9cf73SPeter Wemm for (i = max_state; i > 0; --i) { 83df8bae1dSRodney W. Grimes tstate[i].cs_id = i; 84df8bae1dSRodney W. Grimes tstate[i].cs_next = &tstate[i - 1]; 85df8bae1dSRodney W. Grimes } 8671b9cf73SPeter Wemm tstate[0].cs_next = &tstate[max_state]; 87df8bae1dSRodney W. Grimes tstate[0].cs_id = 0; 88df8bae1dSRodney W. Grimes comp->last_cs = &tstate[0]; 89df8bae1dSRodney W. Grimes comp->last_recv = 255; 90df8bae1dSRodney W. Grimes comp->last_xmit = 255; 91df8bae1dSRodney W. Grimes comp->flags = SLF_TOSS; 92df8bae1dSRodney W. Grimes } 93df8bae1dSRodney W. Grimes 94df8bae1dSRodney W. Grimes 95df8bae1dSRodney W. Grimes /* ENCODE encodes a number that is known to be non-zero. ENCODEZ 96df8bae1dSRodney W. Grimes * checks for zero (since zero has to be encoded in the long, 3 byte 97df8bae1dSRodney W. Grimes * form). 98df8bae1dSRodney W. Grimes */ 99df8bae1dSRodney W. Grimes #define ENCODE(n) { \ 1002d4b190bSPeter Wemm if ((u_int16_t)(n) >= 256) { \ 101df8bae1dSRodney W. Grimes *cp++ = 0; \ 102df8bae1dSRodney W. Grimes cp[1] = (n); \ 103df8bae1dSRodney W. Grimes cp[0] = (n) >> 8; \ 104df8bae1dSRodney W. Grimes cp += 2; \ 105df8bae1dSRodney W. Grimes } else { \ 106df8bae1dSRodney W. Grimes *cp++ = (n); \ 107df8bae1dSRodney W. Grimes } \ 108df8bae1dSRodney W. Grimes } 109df8bae1dSRodney W. Grimes #define ENCODEZ(n) { \ 1102d4b190bSPeter Wemm if ((u_int16_t)(n) >= 256 || (u_int16_t)(n) == 0) { \ 111df8bae1dSRodney W. Grimes *cp++ = 0; \ 112df8bae1dSRodney W. Grimes cp[1] = (n); \ 113df8bae1dSRodney W. Grimes cp[0] = (n) >> 8; \ 114df8bae1dSRodney W. Grimes cp += 2; \ 115df8bae1dSRodney W. Grimes } else { \ 116df8bae1dSRodney W. Grimes *cp++ = (n); \ 117df8bae1dSRodney W. Grimes } \ 118df8bae1dSRodney W. Grimes } 119df8bae1dSRodney W. Grimes 120df8bae1dSRodney W. Grimes #define DECODEL(f) { \ 121df8bae1dSRodney W. Grimes if (*cp == 0) {\ 122df8bae1dSRodney W. Grimes (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \ 123df8bae1dSRodney W. Grimes cp += 3; \ 124df8bae1dSRodney W. Grimes } else { \ 1252d4b190bSPeter Wemm (f) = htonl(ntohl(f) + (u_int32_t)*cp++); \ 126df8bae1dSRodney W. Grimes } \ 127df8bae1dSRodney W. Grimes } 128df8bae1dSRodney W. Grimes 129df8bae1dSRodney W. Grimes #define DECODES(f) { \ 130df8bae1dSRodney W. Grimes if (*cp == 0) {\ 131df8bae1dSRodney W. Grimes (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \ 132df8bae1dSRodney W. Grimes cp += 3; \ 133df8bae1dSRodney W. Grimes } else { \ 1342d4b190bSPeter Wemm (f) = htons(ntohs(f) + (u_int32_t)*cp++); \ 135df8bae1dSRodney W. Grimes } \ 136df8bae1dSRodney W. Grimes } 137df8bae1dSRodney W. Grimes 138df8bae1dSRodney W. Grimes #define DECODEU(f) { \ 139df8bae1dSRodney W. Grimes if (*cp == 0) {\ 140df8bae1dSRodney W. Grimes (f) = htons((cp[1] << 8) | cp[2]); \ 141df8bae1dSRodney W. Grimes cp += 3; \ 142df8bae1dSRodney W. Grimes } else { \ 1432d4b190bSPeter Wemm (f) = htons((u_int32_t)*cp++); \ 144df8bae1dSRodney W. Grimes } \ 145df8bae1dSRodney W. Grimes } 146df8bae1dSRodney W. Grimes 1473893348eSArchie Cobbs /* 148da8c951dSArchie Cobbs * Attempt to compress an outgoing TCP packet and return the type of 149da8c951dSArchie Cobbs * the result. The caller must have already verified that the protocol 150da8c951dSArchie Cobbs * is TCP. The first mbuf must contain the complete IP and TCP headers, 151da8c951dSArchie Cobbs * and "ip" must be == mtod(m, struct ip *). "comp" supplies the 152da8c951dSArchie Cobbs * compression state, and "compress_cid" tells us whether it is OK 153da8c951dSArchie Cobbs * to leave out the CID field when feasible. 154da8c951dSArchie Cobbs * 155da8c951dSArchie Cobbs * The caller is responsible for adjusting m->m_pkthdr.len upon return, 156da8c951dSArchie Cobbs * if m is an M_PKTHDR mbuf. 1573893348eSArchie Cobbs */ 158df8bae1dSRodney W. Grimes u_int 159df8bae1dSRodney W. Grimes sl_compress_tcp(m, ip, comp, compress_cid) 160df8bae1dSRodney W. Grimes struct mbuf *m; 161df8bae1dSRodney W. Grimes register struct ip *ip; 162df8bae1dSRodney W. Grimes struct slcompress *comp; 163df8bae1dSRodney W. Grimes int compress_cid; 164df8bae1dSRodney W. Grimes { 165df8bae1dSRodney W. Grimes register struct cstate *cs = comp->last_cs->cs_next; 166df8bae1dSRodney W. Grimes register u_int hlen = ip->ip_hl; 167df8bae1dSRodney W. Grimes register struct tcphdr *oth; 168df8bae1dSRodney W. Grimes register struct tcphdr *th; 169df8bae1dSRodney W. Grimes register u_int deltaS, deltaA; 170df8bae1dSRodney W. Grimes register u_int changes = 0; 171df8bae1dSRodney W. Grimes u_char new_seq[16]; 172df8bae1dSRodney W. Grimes register u_char *cp = new_seq; 173df8bae1dSRodney W. Grimes 174df8bae1dSRodney W. Grimes /* 175df8bae1dSRodney W. Grimes * Bail if this is an IP fragment or if the TCP packet isn't 176df8bae1dSRodney W. Grimes * `compressible' (i.e., ACK isn't set or some other control bit is 177df8bae1dSRodney W. Grimes * set). (We assume that the caller has already made sure the 178df8bae1dSRodney W. Grimes * packet is IP proto TCP). 179df8bae1dSRodney W. Grimes */ 180df8bae1dSRodney W. Grimes if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40) 181df8bae1dSRodney W. Grimes return (TYPE_IP); 182df8bae1dSRodney W. Grimes 1832d4b190bSPeter Wemm th = (struct tcphdr *)&((int32_t *)ip)[hlen]; 184df8bae1dSRodney W. Grimes if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK) 185df8bae1dSRodney W. Grimes return (TYPE_IP); 186df8bae1dSRodney W. Grimes /* 187df8bae1dSRodney W. Grimes * Packet is compressible -- we're going to send either a 188df8bae1dSRodney W. Grimes * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need 189df8bae1dSRodney W. Grimes * to locate (or create) the connection state. Special case the 190df8bae1dSRodney W. Grimes * most recently used connection since it's most likely to be used 191df8bae1dSRodney W. Grimes * again & we don't have to do any reordering if it's used. 192df8bae1dSRodney W. Grimes */ 193df8bae1dSRodney W. Grimes INCR(sls_packets) 194df8bae1dSRodney W. Grimes if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr || 195df8bae1dSRodney W. Grimes ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr || 1962d4b190bSPeter Wemm *(int32_t *)th != ((int32_t *)&cs->cs_ip)[cs->cs_ip.ip_hl]) { 197df8bae1dSRodney W. Grimes /* 198df8bae1dSRodney W. Grimes * Wasn't the first -- search for it. 199df8bae1dSRodney W. Grimes * 200df8bae1dSRodney W. Grimes * States are kept in a circularly linked list with 201df8bae1dSRodney W. Grimes * last_cs pointing to the end of the list. The 202df8bae1dSRodney W. Grimes * list is kept in lru order by moving a state to the 203df8bae1dSRodney W. Grimes * head of the list whenever it is referenced. Since 204df8bae1dSRodney W. Grimes * the list is short and, empirically, the connection 205df8bae1dSRodney W. Grimes * we want is almost always near the front, we locate 206df8bae1dSRodney W. Grimes * states via linear search. If we don't find a state 207df8bae1dSRodney W. Grimes * for the datagram, the oldest state is (re-)used. 208df8bae1dSRodney W. Grimes */ 209df8bae1dSRodney W. Grimes register struct cstate *lcs; 210df8bae1dSRodney W. Grimes register struct cstate *lastcs = comp->last_cs; 211df8bae1dSRodney W. Grimes 212df8bae1dSRodney W. Grimes do { 213df8bae1dSRodney W. Grimes lcs = cs; cs = cs->cs_next; 214df8bae1dSRodney W. Grimes INCR(sls_searches) 215df8bae1dSRodney W. Grimes if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr 216df8bae1dSRodney W. Grimes && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr 2172d4b190bSPeter Wemm && *(int32_t *)th == 2182d4b190bSPeter Wemm ((int32_t *)&cs->cs_ip)[cs->cs_ip.ip_hl]) 219df8bae1dSRodney W. Grimes goto found; 220df8bae1dSRodney W. Grimes } while (cs != lastcs); 221df8bae1dSRodney W. Grimes 222df8bae1dSRodney W. Grimes /* 223df8bae1dSRodney W. Grimes * Didn't find it -- re-use oldest cstate. Send an 224df8bae1dSRodney W. Grimes * uncompressed packet that tells the other side what 225df8bae1dSRodney W. Grimes * connection number we're using for this conversation. 226df8bae1dSRodney W. Grimes * Note that since the state list is circular, the oldest 227df8bae1dSRodney W. Grimes * state points to the newest and we only need to set 228df8bae1dSRodney W. Grimes * last_cs to update the lru linkage. 229df8bae1dSRodney W. Grimes */ 230df8bae1dSRodney W. Grimes INCR(sls_misses) 231df8bae1dSRodney W. Grimes comp->last_cs = lcs; 232df8bae1dSRodney W. Grimes hlen += th->th_off; 233df8bae1dSRodney W. Grimes hlen <<= 2; 2343bb3b046SBrian Somers if (hlen > m->m_len) 2353bb3b046SBrian Somers return TYPE_IP; 236df8bae1dSRodney W. Grimes goto uncompressed; 237df8bae1dSRodney W. Grimes 238df8bae1dSRodney W. Grimes found: 239df8bae1dSRodney W. Grimes /* 240df8bae1dSRodney W. Grimes * Found it -- move to the front on the connection list. 241df8bae1dSRodney W. Grimes */ 242df8bae1dSRodney W. Grimes if (cs == lastcs) 243df8bae1dSRodney W. Grimes comp->last_cs = lcs; 244df8bae1dSRodney W. Grimes else { 245df8bae1dSRodney W. Grimes lcs->cs_next = cs->cs_next; 246df8bae1dSRodney W. Grimes cs->cs_next = lastcs->cs_next; 247df8bae1dSRodney W. Grimes lastcs->cs_next = cs; 248df8bae1dSRodney W. Grimes } 249df8bae1dSRodney W. Grimes } 250df8bae1dSRodney W. Grimes 251df8bae1dSRodney W. Grimes /* 252df8bae1dSRodney W. Grimes * Make sure that only what we expect to change changed. The first 253df8bae1dSRodney W. Grimes * line of the `if' checks the IP protocol version, header length & 254df8bae1dSRodney W. Grimes * type of service. The 2nd line checks the "Don't fragment" bit. 255df8bae1dSRodney W. Grimes * The 3rd line checks the time-to-live and protocol (the protocol 256df8bae1dSRodney W. Grimes * check is unnecessary but costless). The 4th line checks the TCP 257df8bae1dSRodney W. Grimes * header length. The 5th line checks IP options, if any. The 6th 258df8bae1dSRodney W. Grimes * line checks TCP options, if any. If any of these things are 259df8bae1dSRodney W. Grimes * different between the previous & current datagram, we send the 260df8bae1dSRodney W. Grimes * current datagram `uncompressed'. 261df8bae1dSRodney W. Grimes */ 2622d4b190bSPeter Wemm oth = (struct tcphdr *)&((int32_t *)&cs->cs_ip)[hlen]; 263df8bae1dSRodney W. Grimes deltaS = hlen; 264df8bae1dSRodney W. Grimes hlen += th->th_off; 265df8bae1dSRodney W. Grimes hlen <<= 2; 2663bb3b046SBrian Somers if (hlen > m->m_len) 2673bb3b046SBrian Somers return TYPE_IP; 268df8bae1dSRodney W. Grimes 2692d4b190bSPeter Wemm if (((u_int16_t *)ip)[0] != ((u_int16_t *)&cs->cs_ip)[0] || 2702d4b190bSPeter Wemm ((u_int16_t *)ip)[3] != ((u_int16_t *)&cs->cs_ip)[3] || 2712d4b190bSPeter Wemm ((u_int16_t *)ip)[4] != ((u_int16_t *)&cs->cs_ip)[4] || 272df8bae1dSRodney W. Grimes th->th_off != oth->th_off || 273df8bae1dSRodney W. Grimes (deltaS > 5 && 274df8bae1dSRodney W. Grimes BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) || 275df8bae1dSRodney W. Grimes (th->th_off > 5 && 276df8bae1dSRodney W. Grimes BCMP(th + 1, oth + 1, (th->th_off - 5) << 2))) 277df8bae1dSRodney W. Grimes goto uncompressed; 278df8bae1dSRodney W. Grimes 279df8bae1dSRodney W. Grimes /* 280df8bae1dSRodney W. Grimes * Figure out which of the changing fields changed. The 281df8bae1dSRodney W. Grimes * receiver expects changes in the order: urgent, window, 282df8bae1dSRodney W. Grimes * ack, seq (the order minimizes the number of temporaries 283df8bae1dSRodney W. Grimes * needed in this section of code). 284df8bae1dSRodney W. Grimes */ 285df8bae1dSRodney W. Grimes if (th->th_flags & TH_URG) { 286df8bae1dSRodney W. Grimes deltaS = ntohs(th->th_urp); 287df8bae1dSRodney W. Grimes ENCODEZ(deltaS); 288df8bae1dSRodney W. Grimes changes |= NEW_U; 289df8bae1dSRodney W. Grimes } else if (th->th_urp != oth->th_urp) 290df8bae1dSRodney W. Grimes /* argh! URG not set but urp changed -- a sensible 291df8bae1dSRodney W. Grimes * implementation should never do this but RFC793 292df8bae1dSRodney W. Grimes * doesn't prohibit the change so we have to deal 293df8bae1dSRodney W. Grimes * with it. */ 294df8bae1dSRodney W. Grimes goto uncompressed; 295df8bae1dSRodney W. Grimes 2962d4b190bSPeter Wemm deltaS = (u_int16_t)(ntohs(th->th_win) - ntohs(oth->th_win)); 297df440948SPoul-Henning Kamp if (deltaS) { 298df8bae1dSRodney W. Grimes ENCODE(deltaS); 299df8bae1dSRodney W. Grimes changes |= NEW_W; 300df8bae1dSRodney W. Grimes } 301df8bae1dSRodney W. Grimes 302df440948SPoul-Henning Kamp deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack); 303df440948SPoul-Henning Kamp if (deltaA) { 304df8bae1dSRodney W. Grimes if (deltaA > 0xffff) 305df8bae1dSRodney W. Grimes goto uncompressed; 306df8bae1dSRodney W. Grimes ENCODE(deltaA); 307df8bae1dSRodney W. Grimes changes |= NEW_A; 308df8bae1dSRodney W. Grimes } 309df8bae1dSRodney W. Grimes 310df440948SPoul-Henning Kamp deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq); 311df440948SPoul-Henning Kamp if (deltaS) { 312df8bae1dSRodney W. Grimes if (deltaS > 0xffff) 313df8bae1dSRodney W. Grimes goto uncompressed; 314df8bae1dSRodney W. Grimes ENCODE(deltaS); 315df8bae1dSRodney W. Grimes changes |= NEW_S; 316df8bae1dSRodney W. Grimes } 317df8bae1dSRodney W. Grimes 318df8bae1dSRodney W. Grimes switch(changes) { 319df8bae1dSRodney W. Grimes 320df8bae1dSRodney W. Grimes case 0: 321df8bae1dSRodney W. Grimes /* 322df8bae1dSRodney W. Grimes * Nothing changed. If this packet contains data and the 323df8bae1dSRodney W. Grimes * last one didn't, this is probably a data packet following 324df8bae1dSRodney W. Grimes * an ack (normal on an interactive connection) and we send 325df8bae1dSRodney W. Grimes * it compressed. Otherwise it's probably a retransmit, 326df8bae1dSRodney W. Grimes * retransmitted ack or window probe. Send it uncompressed 327df8bae1dSRodney W. Grimes * in case the other side missed the compressed version. 328df8bae1dSRodney W. Grimes */ 329df8bae1dSRodney W. Grimes if (ip->ip_len != cs->cs_ip.ip_len && 330df8bae1dSRodney W. Grimes ntohs(cs->cs_ip.ip_len) == hlen) 331df8bae1dSRodney W. Grimes break; 332df8bae1dSRodney W. Grimes 33393b0017fSPhilippe Charnier /* FALLTHROUGH */ 334df8bae1dSRodney W. Grimes 335df8bae1dSRodney W. Grimes case SPECIAL_I: 336df8bae1dSRodney W. Grimes case SPECIAL_D: 337df8bae1dSRodney W. Grimes /* 338df8bae1dSRodney W. Grimes * actual changes match one of our special case encodings -- 339df8bae1dSRodney W. Grimes * send packet uncompressed. 340df8bae1dSRodney W. Grimes */ 341df8bae1dSRodney W. Grimes goto uncompressed; 342df8bae1dSRodney W. Grimes 343df8bae1dSRodney W. Grimes case NEW_S|NEW_A: 344df8bae1dSRodney W. Grimes if (deltaS == deltaA && 345df8bae1dSRodney W. Grimes deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { 346df8bae1dSRodney W. Grimes /* special case for echoed terminal traffic */ 347df8bae1dSRodney W. Grimes changes = SPECIAL_I; 348df8bae1dSRodney W. Grimes cp = new_seq; 349df8bae1dSRodney W. Grimes } 350df8bae1dSRodney W. Grimes break; 351df8bae1dSRodney W. Grimes 352df8bae1dSRodney W. Grimes case NEW_S: 353df8bae1dSRodney W. Grimes if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { 354df8bae1dSRodney W. Grimes /* special case for data xfer */ 355df8bae1dSRodney W. Grimes changes = SPECIAL_D; 356df8bae1dSRodney W. Grimes cp = new_seq; 357df8bae1dSRodney W. Grimes } 358df8bae1dSRodney W. Grimes break; 359df8bae1dSRodney W. Grimes } 360df8bae1dSRodney W. Grimes 361df8bae1dSRodney W. Grimes deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id); 362df8bae1dSRodney W. Grimes if (deltaS != 1) { 363df8bae1dSRodney W. Grimes ENCODEZ(deltaS); 364df8bae1dSRodney W. Grimes changes |= NEW_I; 365df8bae1dSRodney W. Grimes } 366df8bae1dSRodney W. Grimes if (th->th_flags & TH_PUSH) 367df8bae1dSRodney W. Grimes changes |= TCP_PUSH_BIT; 368df8bae1dSRodney W. Grimes /* 369df8bae1dSRodney W. Grimes * Grab the cksum before we overwrite it below. Then update our 370df8bae1dSRodney W. Grimes * state with this packet's header. 371df8bae1dSRodney W. Grimes */ 372df8bae1dSRodney W. Grimes deltaA = ntohs(th->th_sum); 373df8bae1dSRodney W. Grimes BCOPY(ip, &cs->cs_ip, hlen); 374df8bae1dSRodney W. Grimes 375df8bae1dSRodney W. Grimes /* 376df8bae1dSRodney W. Grimes * We want to use the original packet as our compressed packet. 377df8bae1dSRodney W. Grimes * (cp - new_seq) is the number of bytes we need for compressed 378df8bae1dSRodney W. Grimes * sequence numbers. In addition we need one byte for the change 379df8bae1dSRodney W. Grimes * mask, one for the connection id and two for the tcp checksum. 380df8bae1dSRodney W. Grimes * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how 381df8bae1dSRodney W. Grimes * many bytes of the original packet to toss so subtract the two to 382df8bae1dSRodney W. Grimes * get the new packet size. 383df8bae1dSRodney W. Grimes */ 384df8bae1dSRodney W. Grimes deltaS = cp - new_seq; 385df8bae1dSRodney W. Grimes cp = (u_char *)ip; 386df8bae1dSRodney W. Grimes if (compress_cid == 0 || comp->last_xmit != cs->cs_id) { 387df8bae1dSRodney W. Grimes comp->last_xmit = cs->cs_id; 388df8bae1dSRodney W. Grimes hlen -= deltaS + 4; 389df8bae1dSRodney W. Grimes cp += hlen; 390df8bae1dSRodney W. Grimes *cp++ = changes | NEW_C; 391df8bae1dSRodney W. Grimes *cp++ = cs->cs_id; 392df8bae1dSRodney W. Grimes } else { 393df8bae1dSRodney W. Grimes hlen -= deltaS + 3; 394df8bae1dSRodney W. Grimes cp += hlen; 395df8bae1dSRodney W. Grimes *cp++ = changes; 396df8bae1dSRodney W. Grimes } 397df8bae1dSRodney W. Grimes m->m_len -= hlen; 398df8bae1dSRodney W. Grimes m->m_data += hlen; 399df8bae1dSRodney W. Grimes *cp++ = deltaA >> 8; 400df8bae1dSRodney W. Grimes *cp++ = deltaA; 401df8bae1dSRodney W. Grimes BCOPY(new_seq, cp, deltaS); 402df8bae1dSRodney W. Grimes INCR(sls_compressed) 403df8bae1dSRodney W. Grimes return (TYPE_COMPRESSED_TCP); 404df8bae1dSRodney W. Grimes 405df8bae1dSRodney W. Grimes /* 406df8bae1dSRodney W. Grimes * Update connection state cs & send uncompressed packet ('uncompressed' 407df8bae1dSRodney W. Grimes * means a regular ip/tcp packet but with the 'conversation id' we hope 408df8bae1dSRodney W. Grimes * to use on future compressed packets in the protocol field). 409df8bae1dSRodney W. Grimes */ 410df8bae1dSRodney W. Grimes uncompressed: 411df8bae1dSRodney W. Grimes BCOPY(ip, &cs->cs_ip, hlen); 412df8bae1dSRodney W. Grimes ip->ip_p = cs->cs_id; 413df8bae1dSRodney W. Grimes comp->last_xmit = cs->cs_id; 414df8bae1dSRodney W. Grimes return (TYPE_UNCOMPRESSED_TCP); 415df8bae1dSRodney W. Grimes } 416df8bae1dSRodney W. Grimes 417df8bae1dSRodney W. Grimes 418df8bae1dSRodney W. Grimes int 419df8bae1dSRodney W. Grimes sl_uncompress_tcp(bufp, len, type, comp) 420df8bae1dSRodney W. Grimes u_char **bufp; 421df8bae1dSRodney W. Grimes int len; 422df8bae1dSRodney W. Grimes u_int type; 423df8bae1dSRodney W. Grimes struct slcompress *comp; 424df8bae1dSRodney W. Grimes { 42571b9cf73SPeter Wemm u_char *hdr, *cp; 42671b9cf73SPeter Wemm int hlen, vjlen; 42771b9cf73SPeter Wemm 42871b9cf73SPeter Wemm cp = bufp? *bufp: NULL; 42971b9cf73SPeter Wemm vjlen = sl_uncompress_tcp_core(cp, len, len, type, comp, &hdr, &hlen); 43071b9cf73SPeter Wemm if (vjlen < 0) 43171b9cf73SPeter Wemm return (0); /* error */ 43271b9cf73SPeter Wemm if (vjlen == 0) 43371b9cf73SPeter Wemm return (len); /* was uncompressed already */ 43471b9cf73SPeter Wemm 43571b9cf73SPeter Wemm cp += vjlen; 43671b9cf73SPeter Wemm len -= vjlen; 43771b9cf73SPeter Wemm 43871b9cf73SPeter Wemm /* 43971b9cf73SPeter Wemm * At this point, cp points to the first byte of data in the 44071b9cf73SPeter Wemm * packet. If we're not aligned on a 4-byte boundary, copy the 44171b9cf73SPeter Wemm * data down so the ip & tcp headers will be aligned. Then back up 44271b9cf73SPeter Wemm * cp by the tcp/ip header length to make room for the reconstructed 44371b9cf73SPeter Wemm * header (we assume the packet we were handed has enough space to 44471b9cf73SPeter Wemm * prepend 128 bytes of header). 44571b9cf73SPeter Wemm */ 446a23d65bfSBruce Evans if ((intptr_t)cp & 3) { 44771b9cf73SPeter Wemm if (len > 0) 4487283c975SDag-Erling Smørgrav BCOPY(cp, ((intptr_t)cp &~ 3), len); 449a23d65bfSBruce Evans cp = (u_char *)((intptr_t)cp &~ 3); 45071b9cf73SPeter Wemm } 45171b9cf73SPeter Wemm cp -= hlen; 45271b9cf73SPeter Wemm len += hlen; 45371b9cf73SPeter Wemm BCOPY(hdr, cp, hlen); 45471b9cf73SPeter Wemm 45571b9cf73SPeter Wemm *bufp = cp; 45671b9cf73SPeter Wemm return (len); 45771b9cf73SPeter Wemm } 45871b9cf73SPeter Wemm 45971b9cf73SPeter Wemm /* 46071b9cf73SPeter Wemm * Uncompress a packet of total length total_len. The first buflen 46171b9cf73SPeter Wemm * bytes are at buf; this must include the entire (compressed or 46271b9cf73SPeter Wemm * uncompressed) TCP/IP header. This procedure returns the length 46371b9cf73SPeter Wemm * of the VJ header, with a pointer to the uncompressed IP header 46471b9cf73SPeter Wemm * in *hdrp and its length in *hlenp. 46571b9cf73SPeter Wemm */ 46671b9cf73SPeter Wemm int 46771b9cf73SPeter Wemm sl_uncompress_tcp_core(buf, buflen, total_len, type, comp, hdrp, hlenp) 46871b9cf73SPeter Wemm u_char *buf; 46971b9cf73SPeter Wemm int buflen, total_len; 47071b9cf73SPeter Wemm u_int type; 47171b9cf73SPeter Wemm struct slcompress *comp; 47271b9cf73SPeter Wemm u_char **hdrp; 47371b9cf73SPeter Wemm u_int *hlenp; 47471b9cf73SPeter Wemm { 475df8bae1dSRodney W. Grimes register u_char *cp; 476df8bae1dSRodney W. Grimes register u_int hlen, changes; 477df8bae1dSRodney W. Grimes register struct tcphdr *th; 478df8bae1dSRodney W. Grimes register struct cstate *cs; 479df8bae1dSRodney W. Grimes register struct ip *ip; 4802d4b190bSPeter Wemm register u_int16_t *bp; 48171b9cf73SPeter Wemm register u_int vjlen; 482df8bae1dSRodney W. Grimes 483df8bae1dSRodney W. Grimes switch (type) { 484df8bae1dSRodney W. Grimes 485df8bae1dSRodney W. Grimes case TYPE_UNCOMPRESSED_TCP: 48671b9cf73SPeter Wemm ip = (struct ip *) buf; 487df8bae1dSRodney W. Grimes if (ip->ip_p >= MAX_STATES) 488df8bae1dSRodney W. Grimes goto bad; 489df8bae1dSRodney W. Grimes cs = &comp->rstate[comp->last_recv = ip->ip_p]; 490df8bae1dSRodney W. Grimes comp->flags &=~ SLF_TOSS; 491df8bae1dSRodney W. Grimes ip->ip_p = IPPROTO_TCP; 49206fc5af9SDavid Greenman /* 49306fc5af9SDavid Greenman * Calculate the size of the TCP/IP header and make sure that 49406fc5af9SDavid Greenman * we don't overflow the space we have available for it. 49506fc5af9SDavid Greenman */ 49606fc5af9SDavid Greenman hlen = ip->ip_hl << 2; 49706fc5af9SDavid Greenman if (hlen + sizeof(struct tcphdr) > buflen) 49806fc5af9SDavid Greenman goto bad; 49906fc5af9SDavid Greenman hlen += ((struct tcphdr *)&((char *)ip)[hlen])->th_off << 2; 5002d4b190bSPeter Wemm if (hlen > MAX_HDR || hlen > buflen) 50106fc5af9SDavid Greenman goto bad; 502df8bae1dSRodney W. Grimes BCOPY(ip, &cs->cs_ip, hlen); 503df8bae1dSRodney W. Grimes cs->cs_hlen = hlen; 504df8bae1dSRodney W. Grimes INCR(sls_uncompressedin) 50571b9cf73SPeter Wemm *hdrp = (u_char *) &cs->cs_ip; 50671b9cf73SPeter Wemm *hlenp = hlen; 50771b9cf73SPeter Wemm return (0); 508df8bae1dSRodney W. Grimes 509df8bae1dSRodney W. Grimes default: 510df8bae1dSRodney W. Grimes goto bad; 511df8bae1dSRodney W. Grimes 512df8bae1dSRodney W. Grimes case TYPE_COMPRESSED_TCP: 513df8bae1dSRodney W. Grimes break; 514df8bae1dSRodney W. Grimes } 515df8bae1dSRodney W. Grimes /* We've got a compressed packet. */ 516df8bae1dSRodney W. Grimes INCR(sls_compressedin) 51771b9cf73SPeter Wemm cp = buf; 518df8bae1dSRodney W. Grimes changes = *cp++; 519df8bae1dSRodney W. Grimes if (changes & NEW_C) { 520df8bae1dSRodney W. Grimes /* Make sure the state index is in range, then grab the state. 521df8bae1dSRodney W. Grimes * If we have a good state index, clear the 'discard' flag. */ 522df8bae1dSRodney W. Grimes if (*cp >= MAX_STATES) 523df8bae1dSRodney W. Grimes goto bad; 524df8bae1dSRodney W. Grimes 525df8bae1dSRodney W. Grimes comp->flags &=~ SLF_TOSS; 526df8bae1dSRodney W. Grimes comp->last_recv = *cp++; 527df8bae1dSRodney W. Grimes } else { 528df8bae1dSRodney W. Grimes /* this packet has an implicit state index. If we've 529df8bae1dSRodney W. Grimes * had a line error since the last time we got an 530df8bae1dSRodney W. Grimes * explicit state index, we have to toss the packet. */ 531df8bae1dSRodney W. Grimes if (comp->flags & SLF_TOSS) { 532df8bae1dSRodney W. Grimes INCR(sls_tossed) 53371b9cf73SPeter Wemm return (-1); 534df8bae1dSRodney W. Grimes } 535df8bae1dSRodney W. Grimes } 536df8bae1dSRodney W. Grimes cs = &comp->rstate[comp->last_recv]; 537df8bae1dSRodney W. Grimes hlen = cs->cs_ip.ip_hl << 2; 538df8bae1dSRodney W. Grimes th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen]; 539df8bae1dSRodney W. Grimes th->th_sum = htons((*cp << 8) | cp[1]); 540df8bae1dSRodney W. Grimes cp += 2; 541df8bae1dSRodney W. Grimes if (changes & TCP_PUSH_BIT) 542df8bae1dSRodney W. Grimes th->th_flags |= TH_PUSH; 543df8bae1dSRodney W. Grimes else 544df8bae1dSRodney W. Grimes th->th_flags &=~ TH_PUSH; 545df8bae1dSRodney W. Grimes 546df8bae1dSRodney W. Grimes switch (changes & SPECIALS_MASK) { 547df8bae1dSRodney W. Grimes case SPECIAL_I: 548df8bae1dSRodney W. Grimes { 549df8bae1dSRodney W. Grimes register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; 550df8bae1dSRodney W. Grimes th->th_ack = htonl(ntohl(th->th_ack) + i); 551df8bae1dSRodney W. Grimes th->th_seq = htonl(ntohl(th->th_seq) + i); 552df8bae1dSRodney W. Grimes } 553df8bae1dSRodney W. Grimes break; 554df8bae1dSRodney W. Grimes 555df8bae1dSRodney W. Grimes case SPECIAL_D: 556df8bae1dSRodney W. Grimes th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) 557df8bae1dSRodney W. Grimes - cs->cs_hlen); 558df8bae1dSRodney W. Grimes break; 559df8bae1dSRodney W. Grimes 560df8bae1dSRodney W. Grimes default: 561df8bae1dSRodney W. Grimes if (changes & NEW_U) { 562df8bae1dSRodney W. Grimes th->th_flags |= TH_URG; 563df8bae1dSRodney W. Grimes DECODEU(th->th_urp) 564df8bae1dSRodney W. Grimes } else 565df8bae1dSRodney W. Grimes th->th_flags &=~ TH_URG; 566df8bae1dSRodney W. Grimes if (changes & NEW_W) 567df8bae1dSRodney W. Grimes DECODES(th->th_win) 568df8bae1dSRodney W. Grimes if (changes & NEW_A) 569df8bae1dSRodney W. Grimes DECODEL(th->th_ack) 570df8bae1dSRodney W. Grimes if (changes & NEW_S) 571df8bae1dSRodney W. Grimes DECODEL(th->th_seq) 572df8bae1dSRodney W. Grimes break; 573df8bae1dSRodney W. Grimes } 574df8bae1dSRodney W. Grimes if (changes & NEW_I) { 575df8bae1dSRodney W. Grimes DECODES(cs->cs_ip.ip_id) 576df8bae1dSRodney W. Grimes } else 577df8bae1dSRodney W. Grimes cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1); 578df8bae1dSRodney W. Grimes 579df8bae1dSRodney W. Grimes /* 580df8bae1dSRodney W. Grimes * At this point, cp points to the first byte of data in the 58171b9cf73SPeter Wemm * packet. Fill in the IP total length and update the IP 58271b9cf73SPeter Wemm * header checksum. 583df8bae1dSRodney W. Grimes */ 58471b9cf73SPeter Wemm vjlen = cp - buf; 58571b9cf73SPeter Wemm buflen -= vjlen; 58671b9cf73SPeter Wemm if (buflen < 0) 587df8bae1dSRodney W. Grimes /* we must have dropped some characters (crc should detect 588df8bae1dSRodney W. Grimes * this but the old slip framing won't) */ 589df8bae1dSRodney W. Grimes goto bad; 590df8bae1dSRodney W. Grimes 59171b9cf73SPeter Wemm total_len += cs->cs_hlen - vjlen; 59271b9cf73SPeter Wemm cs->cs_ip.ip_len = htons(total_len); 593df8bae1dSRodney W. Grimes 594df8bae1dSRodney W. Grimes /* recompute the ip header checksum */ 5952d4b190bSPeter Wemm bp = (u_int16_t *) &cs->cs_ip; 59671b9cf73SPeter Wemm cs->cs_ip.ip_sum = 0; 597df8bae1dSRodney W. Grimes for (changes = 0; hlen > 0; hlen -= 2) 598df8bae1dSRodney W. Grimes changes += *bp++; 599df8bae1dSRodney W. Grimes changes = (changes & 0xffff) + (changes >> 16); 600df8bae1dSRodney W. Grimes changes = (changes & 0xffff) + (changes >> 16); 60171b9cf73SPeter Wemm cs->cs_ip.ip_sum = ~ changes; 60271b9cf73SPeter Wemm 60371b9cf73SPeter Wemm *hdrp = (u_char *) &cs->cs_ip; 60471b9cf73SPeter Wemm *hlenp = cs->cs_hlen; 60571b9cf73SPeter Wemm return vjlen; 60671b9cf73SPeter Wemm 607df8bae1dSRodney W. Grimes bad: 608df8bae1dSRodney W. Grimes comp->flags |= SLF_TOSS; 609df8bae1dSRodney W. Grimes INCR(sls_errorin) 61071b9cf73SPeter Wemm return (-1); 611df8bae1dSRodney W. Grimes } 612