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