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 3471b9cf73SPeter Wemm * $Id: slcompress.c,v 1.5 1995/05/30 08:08:33 rgrimes Exp $ 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> 47f23b4c91SGarrett Wollman #include <sys/systm.h> 48df8bae1dSRodney W. Grimes #include <sys/mbuf.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 63df8bae1dSRodney W. Grimes #define BCMP(p1, p2, n) bcmp((char *)(p1), (char *)(p2), (int)(n)) 64df8bae1dSRodney W. Grimes #define BCOPY(p1, p2, n) bcopy((char *)(p1), (char *)(p2), (int)(n)) 65df8bae1dSRodney W. Grimes #ifndef KERNEL 66df8bae1dSRodney W. Grimes #define ovbcopy bcopy 67df8bae1dSRodney W. Grimes #endif 68df8bae1dSRodney W. Grimes 69df8bae1dSRodney W. Grimes void 7071b9cf73SPeter Wemm sl_compress_init(comp, max_state) 71df8bae1dSRodney W. Grimes struct slcompress *comp; 7271b9cf73SPeter Wemm int max_state; 73df8bae1dSRodney W. Grimes { 74df8bae1dSRodney W. Grimes register u_int i; 75df8bae1dSRodney W. Grimes register struct cstate *tstate = comp->tstate; 76df8bae1dSRodney W. Grimes 7771b9cf73SPeter Wemm if (max_state == -1) 7871b9cf73SPeter Wemm max_state = MAX_STATES - 1; 79df8bae1dSRodney W. Grimes bzero((char *)comp, sizeof(*comp)); 8071b9cf73SPeter Wemm for (i = max_state; i > 0; --i) { 81df8bae1dSRodney W. Grimes tstate[i].cs_id = i; 82df8bae1dSRodney W. Grimes tstate[i].cs_next = &tstate[i - 1]; 83df8bae1dSRodney W. Grimes } 8471b9cf73SPeter Wemm tstate[0].cs_next = &tstate[max_state]; 85df8bae1dSRodney W. Grimes tstate[0].cs_id = 0; 86df8bae1dSRodney W. Grimes comp->last_cs = &tstate[0]; 87df8bae1dSRodney W. Grimes comp->last_recv = 255; 88df8bae1dSRodney W. Grimes comp->last_xmit = 255; 89df8bae1dSRodney W. Grimes comp->flags = SLF_TOSS; 90df8bae1dSRodney W. Grimes } 91df8bae1dSRodney W. Grimes 92df8bae1dSRodney W. Grimes 93df8bae1dSRodney W. Grimes /* ENCODE encodes a number that is known to be non-zero. ENCODEZ 94df8bae1dSRodney W. Grimes * checks for zero (since zero has to be encoded in the long, 3 byte 95df8bae1dSRodney W. Grimes * form). 96df8bae1dSRodney W. Grimes */ 97df8bae1dSRodney W. Grimes #define ENCODE(n) { \ 98df8bae1dSRodney W. Grimes if ((u_short)(n) >= 256) { \ 99df8bae1dSRodney W. Grimes *cp++ = 0; \ 100df8bae1dSRodney W. Grimes cp[1] = (n); \ 101df8bae1dSRodney W. Grimes cp[0] = (n) >> 8; \ 102df8bae1dSRodney W. Grimes cp += 2; \ 103df8bae1dSRodney W. Grimes } else { \ 104df8bae1dSRodney W. Grimes *cp++ = (n); \ 105df8bae1dSRodney W. Grimes } \ 106df8bae1dSRodney W. Grimes } 107df8bae1dSRodney W. Grimes #define ENCODEZ(n) { \ 108df8bae1dSRodney W. Grimes if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ 109df8bae1dSRodney W. Grimes *cp++ = 0; \ 110df8bae1dSRodney W. Grimes cp[1] = (n); \ 111df8bae1dSRodney W. Grimes cp[0] = (n) >> 8; \ 112df8bae1dSRodney W. Grimes cp += 2; \ 113df8bae1dSRodney W. Grimes } else { \ 114df8bae1dSRodney W. Grimes *cp++ = (n); \ 115df8bae1dSRodney W. Grimes } \ 116df8bae1dSRodney W. Grimes } 117df8bae1dSRodney W. Grimes 118df8bae1dSRodney W. Grimes #define DECODEL(f) { \ 119df8bae1dSRodney W. Grimes if (*cp == 0) {\ 120df8bae1dSRodney W. Grimes (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \ 121df8bae1dSRodney W. Grimes cp += 3; \ 122df8bae1dSRodney W. Grimes } else { \ 123df8bae1dSRodney W. Grimes (f) = htonl(ntohl(f) + (u_long)*cp++); \ 124df8bae1dSRodney W. Grimes } \ 125df8bae1dSRodney W. Grimes } 126df8bae1dSRodney W. Grimes 127df8bae1dSRodney W. Grimes #define DECODES(f) { \ 128df8bae1dSRodney W. Grimes if (*cp == 0) {\ 129df8bae1dSRodney W. Grimes (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \ 130df8bae1dSRodney W. Grimes cp += 3; \ 131df8bae1dSRodney W. Grimes } else { \ 132df8bae1dSRodney W. Grimes (f) = htons(ntohs(f) + (u_long)*cp++); \ 133df8bae1dSRodney W. Grimes } \ 134df8bae1dSRodney W. Grimes } 135df8bae1dSRodney W. Grimes 136df8bae1dSRodney W. Grimes #define DECODEU(f) { \ 137df8bae1dSRodney W. Grimes if (*cp == 0) {\ 138df8bae1dSRodney W. Grimes (f) = htons((cp[1] << 8) | cp[2]); \ 139df8bae1dSRodney W. Grimes cp += 3; \ 140df8bae1dSRodney W. Grimes } else { \ 141df8bae1dSRodney W. Grimes (f) = htons((u_long)*cp++); \ 142df8bae1dSRodney W. Grimes } \ 143df8bae1dSRodney W. Grimes } 144df8bae1dSRodney W. Grimes 145df8bae1dSRodney W. Grimes u_int 146df8bae1dSRodney W. Grimes sl_compress_tcp(m, ip, comp, compress_cid) 147df8bae1dSRodney W. Grimes struct mbuf *m; 148df8bae1dSRodney W. Grimes register struct ip *ip; 149df8bae1dSRodney W. Grimes struct slcompress *comp; 150df8bae1dSRodney W. Grimes int compress_cid; 151df8bae1dSRodney W. Grimes { 152df8bae1dSRodney W. Grimes register struct cstate *cs = comp->last_cs->cs_next; 153df8bae1dSRodney W. Grimes register u_int hlen = ip->ip_hl; 154df8bae1dSRodney W. Grimes register struct tcphdr *oth; 155df8bae1dSRodney W. Grimes register struct tcphdr *th; 156df8bae1dSRodney W. Grimes register u_int deltaS, deltaA; 157df8bae1dSRodney W. Grimes register u_int changes = 0; 158df8bae1dSRodney W. Grimes u_char new_seq[16]; 159df8bae1dSRodney W. Grimes register u_char *cp = new_seq; 160df8bae1dSRodney W. Grimes 161df8bae1dSRodney W. Grimes /* 162df8bae1dSRodney W. Grimes * Bail if this is an IP fragment or if the TCP packet isn't 163df8bae1dSRodney W. Grimes * `compressible' (i.e., ACK isn't set or some other control bit is 164df8bae1dSRodney W. Grimes * set). (We assume that the caller has already made sure the 165df8bae1dSRodney W. Grimes * packet is IP proto TCP). 166df8bae1dSRodney W. Grimes */ 167df8bae1dSRodney W. Grimes if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40) 168df8bae1dSRodney W. Grimes return (TYPE_IP); 169df8bae1dSRodney W. Grimes 170df8bae1dSRodney W. Grimes th = (struct tcphdr *)&((int *)ip)[hlen]; 171df8bae1dSRodney W. Grimes if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK) 172df8bae1dSRodney W. Grimes return (TYPE_IP); 173df8bae1dSRodney W. Grimes /* 174df8bae1dSRodney W. Grimes * Packet is compressible -- we're going to send either a 175df8bae1dSRodney W. Grimes * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need 176df8bae1dSRodney W. Grimes * to locate (or create) the connection state. Special case the 177df8bae1dSRodney W. Grimes * most recently used connection since it's most likely to be used 178df8bae1dSRodney W. Grimes * again & we don't have to do any reordering if it's used. 179df8bae1dSRodney W. Grimes */ 180df8bae1dSRodney W. Grimes INCR(sls_packets) 181df8bae1dSRodney W. Grimes if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr || 182df8bae1dSRodney W. Grimes ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr || 183df8bae1dSRodney W. Grimes *(int *)th != ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) { 184df8bae1dSRodney W. Grimes /* 185df8bae1dSRodney W. Grimes * Wasn't the first -- search for it. 186df8bae1dSRodney W. Grimes * 187df8bae1dSRodney W. Grimes * States are kept in a circularly linked list with 188df8bae1dSRodney W. Grimes * last_cs pointing to the end of the list. The 189df8bae1dSRodney W. Grimes * list is kept in lru order by moving a state to the 190df8bae1dSRodney W. Grimes * head of the list whenever it is referenced. Since 191df8bae1dSRodney W. Grimes * the list is short and, empirically, the connection 192df8bae1dSRodney W. Grimes * we want is almost always near the front, we locate 193df8bae1dSRodney W. Grimes * states via linear search. If we don't find a state 194df8bae1dSRodney W. Grimes * for the datagram, the oldest state is (re-)used. 195df8bae1dSRodney W. Grimes */ 196df8bae1dSRodney W. Grimes register struct cstate *lcs; 197df8bae1dSRodney W. Grimes register struct cstate *lastcs = comp->last_cs; 198df8bae1dSRodney W. Grimes 199df8bae1dSRodney W. Grimes do { 200df8bae1dSRodney W. Grimes lcs = cs; cs = cs->cs_next; 201df8bae1dSRodney W. Grimes INCR(sls_searches) 202df8bae1dSRodney W. Grimes if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr 203df8bae1dSRodney W. Grimes && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr 204df8bae1dSRodney W. Grimes && *(int *)th == ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) 205df8bae1dSRodney W. Grimes goto found; 206df8bae1dSRodney W. Grimes } while (cs != lastcs); 207df8bae1dSRodney W. Grimes 208df8bae1dSRodney W. Grimes /* 209df8bae1dSRodney W. Grimes * Didn't find it -- re-use oldest cstate. Send an 210df8bae1dSRodney W. Grimes * uncompressed packet that tells the other side what 211df8bae1dSRodney W. Grimes * connection number we're using for this conversation. 212df8bae1dSRodney W. Grimes * Note that since the state list is circular, the oldest 213df8bae1dSRodney W. Grimes * state points to the newest and we only need to set 214df8bae1dSRodney W. Grimes * last_cs to update the lru linkage. 215df8bae1dSRodney W. Grimes */ 216df8bae1dSRodney W. Grimes INCR(sls_misses) 217df8bae1dSRodney W. Grimes comp->last_cs = lcs; 218df8bae1dSRodney W. Grimes hlen += th->th_off; 219df8bae1dSRodney W. Grimes hlen <<= 2; 220df8bae1dSRodney W. Grimes goto uncompressed; 221df8bae1dSRodney W. Grimes 222df8bae1dSRodney W. Grimes found: 223df8bae1dSRodney W. Grimes /* 224df8bae1dSRodney W. Grimes * Found it -- move to the front on the connection list. 225df8bae1dSRodney W. Grimes */ 226df8bae1dSRodney W. Grimes if (cs == lastcs) 227df8bae1dSRodney W. Grimes comp->last_cs = lcs; 228df8bae1dSRodney W. Grimes else { 229df8bae1dSRodney W. Grimes lcs->cs_next = cs->cs_next; 230df8bae1dSRodney W. Grimes cs->cs_next = lastcs->cs_next; 231df8bae1dSRodney W. Grimes lastcs->cs_next = cs; 232df8bae1dSRodney W. Grimes } 233df8bae1dSRodney W. Grimes } 234df8bae1dSRodney W. Grimes 235df8bae1dSRodney W. Grimes /* 236df8bae1dSRodney W. Grimes * Make sure that only what we expect to change changed. The first 237df8bae1dSRodney W. Grimes * line of the `if' checks the IP protocol version, header length & 238df8bae1dSRodney W. Grimes * type of service. The 2nd line checks the "Don't fragment" bit. 239df8bae1dSRodney W. Grimes * The 3rd line checks the time-to-live and protocol (the protocol 240df8bae1dSRodney W. Grimes * check is unnecessary but costless). The 4th line checks the TCP 241df8bae1dSRodney W. Grimes * header length. The 5th line checks IP options, if any. The 6th 242df8bae1dSRodney W. Grimes * line checks TCP options, if any. If any of these things are 243df8bae1dSRodney W. Grimes * different between the previous & current datagram, we send the 244df8bae1dSRodney W. Grimes * current datagram `uncompressed'. 245df8bae1dSRodney W. Grimes */ 246df8bae1dSRodney W. Grimes oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen]; 247df8bae1dSRodney W. Grimes deltaS = hlen; 248df8bae1dSRodney W. Grimes hlen += th->th_off; 249df8bae1dSRodney W. Grimes hlen <<= 2; 250df8bae1dSRodney W. Grimes 251df8bae1dSRodney W. Grimes if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] || 252df8bae1dSRodney W. Grimes ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] || 253df8bae1dSRodney W. Grimes ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] || 254df8bae1dSRodney W. Grimes th->th_off != oth->th_off || 255df8bae1dSRodney W. Grimes (deltaS > 5 && 256df8bae1dSRodney W. Grimes BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) || 257df8bae1dSRodney W. Grimes (th->th_off > 5 && 258df8bae1dSRodney W. Grimes BCMP(th + 1, oth + 1, (th->th_off - 5) << 2))) 259df8bae1dSRodney W. Grimes goto uncompressed; 260df8bae1dSRodney W. Grimes 261df8bae1dSRodney W. Grimes /* 262df8bae1dSRodney W. Grimes * Figure out which of the changing fields changed. The 263df8bae1dSRodney W. Grimes * receiver expects changes in the order: urgent, window, 264df8bae1dSRodney W. Grimes * ack, seq (the order minimizes the number of temporaries 265df8bae1dSRodney W. Grimes * needed in this section of code). 266df8bae1dSRodney W. Grimes */ 267df8bae1dSRodney W. Grimes if (th->th_flags & TH_URG) { 268df8bae1dSRodney W. Grimes deltaS = ntohs(th->th_urp); 269df8bae1dSRodney W. Grimes ENCODEZ(deltaS); 270df8bae1dSRodney W. Grimes changes |= NEW_U; 271df8bae1dSRodney W. Grimes } else if (th->th_urp != oth->th_urp) 272df8bae1dSRodney W. Grimes /* argh! URG not set but urp changed -- a sensible 273df8bae1dSRodney W. Grimes * implementation should never do this but RFC793 274df8bae1dSRodney W. Grimes * doesn't prohibit the change so we have to deal 275df8bae1dSRodney W. Grimes * with it. */ 276df8bae1dSRodney W. Grimes goto uncompressed; 277df8bae1dSRodney W. Grimes 278df440948SPoul-Henning Kamp deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win)); 279df440948SPoul-Henning Kamp if (deltaS) { 280df8bae1dSRodney W. Grimes ENCODE(deltaS); 281df8bae1dSRodney W. Grimes changes |= NEW_W; 282df8bae1dSRodney W. Grimes } 283df8bae1dSRodney W. Grimes 284df440948SPoul-Henning Kamp deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack); 285df440948SPoul-Henning Kamp if (deltaA) { 286df8bae1dSRodney W. Grimes if (deltaA > 0xffff) 287df8bae1dSRodney W. Grimes goto uncompressed; 288df8bae1dSRodney W. Grimes ENCODE(deltaA); 289df8bae1dSRodney W. Grimes changes |= NEW_A; 290df8bae1dSRodney W. Grimes } 291df8bae1dSRodney W. Grimes 292df440948SPoul-Henning Kamp deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq); 293df440948SPoul-Henning Kamp if (deltaS) { 294df8bae1dSRodney W. Grimes if (deltaS > 0xffff) 295df8bae1dSRodney W. Grimes goto uncompressed; 296df8bae1dSRodney W. Grimes ENCODE(deltaS); 297df8bae1dSRodney W. Grimes changes |= NEW_S; 298df8bae1dSRodney W. Grimes } 299df8bae1dSRodney W. Grimes 300df8bae1dSRodney W. Grimes switch(changes) { 301df8bae1dSRodney W. Grimes 302df8bae1dSRodney W. Grimes case 0: 303df8bae1dSRodney W. Grimes /* 304df8bae1dSRodney W. Grimes * Nothing changed. If this packet contains data and the 305df8bae1dSRodney W. Grimes * last one didn't, this is probably a data packet following 306df8bae1dSRodney W. Grimes * an ack (normal on an interactive connection) and we send 307df8bae1dSRodney W. Grimes * it compressed. Otherwise it's probably a retransmit, 308df8bae1dSRodney W. Grimes * retransmitted ack or window probe. Send it uncompressed 309df8bae1dSRodney W. Grimes * in case the other side missed the compressed version. 310df8bae1dSRodney W. Grimes */ 311df8bae1dSRodney W. Grimes if (ip->ip_len != cs->cs_ip.ip_len && 312df8bae1dSRodney W. Grimes ntohs(cs->cs_ip.ip_len) == hlen) 313df8bae1dSRodney W. Grimes break; 314df8bae1dSRodney W. Grimes 315df8bae1dSRodney W. Grimes /* (fall through) */ 316df8bae1dSRodney W. Grimes 317df8bae1dSRodney W. Grimes case SPECIAL_I: 318df8bae1dSRodney W. Grimes case SPECIAL_D: 319df8bae1dSRodney W. Grimes /* 320df8bae1dSRodney W. Grimes * actual changes match one of our special case encodings -- 321df8bae1dSRodney W. Grimes * send packet uncompressed. 322df8bae1dSRodney W. Grimes */ 323df8bae1dSRodney W. Grimes goto uncompressed; 324df8bae1dSRodney W. Grimes 325df8bae1dSRodney W. Grimes case NEW_S|NEW_A: 326df8bae1dSRodney W. Grimes if (deltaS == deltaA && 327df8bae1dSRodney W. Grimes deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { 328df8bae1dSRodney W. Grimes /* special case for echoed terminal traffic */ 329df8bae1dSRodney W. Grimes changes = SPECIAL_I; 330df8bae1dSRodney W. Grimes cp = new_seq; 331df8bae1dSRodney W. Grimes } 332df8bae1dSRodney W. Grimes break; 333df8bae1dSRodney W. Grimes 334df8bae1dSRodney W. Grimes case NEW_S: 335df8bae1dSRodney W. Grimes if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { 336df8bae1dSRodney W. Grimes /* special case for data xfer */ 337df8bae1dSRodney W. Grimes changes = SPECIAL_D; 338df8bae1dSRodney W. Grimes cp = new_seq; 339df8bae1dSRodney W. Grimes } 340df8bae1dSRodney W. Grimes break; 341df8bae1dSRodney W. Grimes } 342df8bae1dSRodney W. Grimes 343df8bae1dSRodney W. Grimes deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id); 344df8bae1dSRodney W. Grimes if (deltaS != 1) { 345df8bae1dSRodney W. Grimes ENCODEZ(deltaS); 346df8bae1dSRodney W. Grimes changes |= NEW_I; 347df8bae1dSRodney W. Grimes } 348df8bae1dSRodney W. Grimes if (th->th_flags & TH_PUSH) 349df8bae1dSRodney W. Grimes changes |= TCP_PUSH_BIT; 350df8bae1dSRodney W. Grimes /* 351df8bae1dSRodney W. Grimes * Grab the cksum before we overwrite it below. Then update our 352df8bae1dSRodney W. Grimes * state with this packet's header. 353df8bae1dSRodney W. Grimes */ 354df8bae1dSRodney W. Grimes deltaA = ntohs(th->th_sum); 355df8bae1dSRodney W. Grimes BCOPY(ip, &cs->cs_ip, hlen); 356df8bae1dSRodney W. Grimes 357df8bae1dSRodney W. Grimes /* 358df8bae1dSRodney W. Grimes * We want to use the original packet as our compressed packet. 359df8bae1dSRodney W. Grimes * (cp - new_seq) is the number of bytes we need for compressed 360df8bae1dSRodney W. Grimes * sequence numbers. In addition we need one byte for the change 361df8bae1dSRodney W. Grimes * mask, one for the connection id and two for the tcp checksum. 362df8bae1dSRodney W. Grimes * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how 363df8bae1dSRodney W. Grimes * many bytes of the original packet to toss so subtract the two to 364df8bae1dSRodney W. Grimes * get the new packet size. 365df8bae1dSRodney W. Grimes */ 366df8bae1dSRodney W. Grimes deltaS = cp - new_seq; 367df8bae1dSRodney W. Grimes cp = (u_char *)ip; 368df8bae1dSRodney W. Grimes if (compress_cid == 0 || comp->last_xmit != cs->cs_id) { 369df8bae1dSRodney W. Grimes comp->last_xmit = cs->cs_id; 370df8bae1dSRodney W. Grimes hlen -= deltaS + 4; 371df8bae1dSRodney W. Grimes cp += hlen; 372df8bae1dSRodney W. Grimes *cp++ = changes | NEW_C; 373df8bae1dSRodney W. Grimes *cp++ = cs->cs_id; 374df8bae1dSRodney W. Grimes } else { 375df8bae1dSRodney W. Grimes hlen -= deltaS + 3; 376df8bae1dSRodney W. Grimes cp += hlen; 377df8bae1dSRodney W. Grimes *cp++ = changes; 378df8bae1dSRodney W. Grimes } 379df8bae1dSRodney W. Grimes m->m_len -= hlen; 380df8bae1dSRodney W. Grimes m->m_data += hlen; 381df8bae1dSRodney W. Grimes *cp++ = deltaA >> 8; 382df8bae1dSRodney W. Grimes *cp++ = deltaA; 383df8bae1dSRodney W. Grimes BCOPY(new_seq, cp, deltaS); 384df8bae1dSRodney W. Grimes INCR(sls_compressed) 385df8bae1dSRodney W. Grimes return (TYPE_COMPRESSED_TCP); 386df8bae1dSRodney W. Grimes 387df8bae1dSRodney W. Grimes /* 388df8bae1dSRodney W. Grimes * Update connection state cs & send uncompressed packet ('uncompressed' 389df8bae1dSRodney W. Grimes * means a regular ip/tcp packet but with the 'conversation id' we hope 390df8bae1dSRodney W. Grimes * to use on future compressed packets in the protocol field). 391df8bae1dSRodney W. Grimes */ 392df8bae1dSRodney W. Grimes uncompressed: 393df8bae1dSRodney W. Grimes BCOPY(ip, &cs->cs_ip, hlen); 394df8bae1dSRodney W. Grimes ip->ip_p = cs->cs_id; 395df8bae1dSRodney W. Grimes comp->last_xmit = cs->cs_id; 396df8bae1dSRodney W. Grimes return (TYPE_UNCOMPRESSED_TCP); 397df8bae1dSRodney W. Grimes } 398df8bae1dSRodney W. Grimes 399df8bae1dSRodney W. Grimes 400df8bae1dSRodney W. Grimes int 401df8bae1dSRodney W. Grimes sl_uncompress_tcp(bufp, len, type, comp) 402df8bae1dSRodney W. Grimes u_char **bufp; 403df8bae1dSRodney W. Grimes int len; 404df8bae1dSRodney W. Grimes u_int type; 405df8bae1dSRodney W. Grimes struct slcompress *comp; 406df8bae1dSRodney W. Grimes { 40771b9cf73SPeter Wemm u_char *hdr, *cp; 40871b9cf73SPeter Wemm int hlen, vjlen; 40971b9cf73SPeter Wemm 41071b9cf73SPeter Wemm cp = bufp? *bufp: NULL; 41171b9cf73SPeter Wemm vjlen = sl_uncompress_tcp_core(cp, len, len, type, comp, &hdr, &hlen); 41271b9cf73SPeter Wemm if (vjlen < 0) 41371b9cf73SPeter Wemm return (0); /* error */ 41471b9cf73SPeter Wemm if (vjlen == 0) 41571b9cf73SPeter Wemm return (len); /* was uncompressed already */ 41671b9cf73SPeter Wemm 41771b9cf73SPeter Wemm cp += vjlen; 41871b9cf73SPeter Wemm len -= vjlen; 41971b9cf73SPeter Wemm 42071b9cf73SPeter Wemm /* 42171b9cf73SPeter Wemm * At this point, cp points to the first byte of data in the 42271b9cf73SPeter Wemm * packet. If we're not aligned on a 4-byte boundary, copy the 42371b9cf73SPeter Wemm * data down so the ip & tcp headers will be aligned. Then back up 42471b9cf73SPeter Wemm * cp by the tcp/ip header length to make room for the reconstructed 42571b9cf73SPeter Wemm * header (we assume the packet we were handed has enough space to 42671b9cf73SPeter Wemm * prepend 128 bytes of header). 42771b9cf73SPeter Wemm */ 42871b9cf73SPeter Wemm if ((int)cp & 3) { 42971b9cf73SPeter Wemm if (len > 0) 43071b9cf73SPeter Wemm (void) ovbcopy(cp, (caddr_t)((int)cp &~ 3), len); 43171b9cf73SPeter Wemm cp = (u_char *)((int)cp &~ 3); 43271b9cf73SPeter Wemm } 43371b9cf73SPeter Wemm cp -= hlen; 43471b9cf73SPeter Wemm len += hlen; 43571b9cf73SPeter Wemm BCOPY(hdr, cp, hlen); 43671b9cf73SPeter Wemm 43771b9cf73SPeter Wemm *bufp = cp; 43871b9cf73SPeter Wemm return (len); 43971b9cf73SPeter Wemm } 44071b9cf73SPeter Wemm 44171b9cf73SPeter Wemm /* 44271b9cf73SPeter Wemm * Uncompress a packet of total length total_len. The first buflen 44371b9cf73SPeter Wemm * bytes are at buf; this must include the entire (compressed or 44471b9cf73SPeter Wemm * uncompressed) TCP/IP header. This procedure returns the length 44571b9cf73SPeter Wemm * of the VJ header, with a pointer to the uncompressed IP header 44671b9cf73SPeter Wemm * in *hdrp and its length in *hlenp. 44771b9cf73SPeter Wemm */ 44871b9cf73SPeter Wemm int 44971b9cf73SPeter Wemm sl_uncompress_tcp_core(buf, buflen, total_len, type, comp, hdrp, hlenp) 45071b9cf73SPeter Wemm u_char *buf; 45171b9cf73SPeter Wemm int buflen, total_len; 45271b9cf73SPeter Wemm u_int type; 45371b9cf73SPeter Wemm struct slcompress *comp; 45471b9cf73SPeter Wemm u_char **hdrp; 45571b9cf73SPeter Wemm u_int *hlenp; 45671b9cf73SPeter Wemm { 457df8bae1dSRodney W. Grimes register u_char *cp; 458df8bae1dSRodney W. Grimes register u_int hlen, changes; 459df8bae1dSRodney W. Grimes register struct tcphdr *th; 460df8bae1dSRodney W. Grimes register struct cstate *cs; 461df8bae1dSRodney W. Grimes register struct ip *ip; 46271b9cf73SPeter Wemm register u_short *bp; 46371b9cf73SPeter Wemm register u_int vjlen; 464df8bae1dSRodney W. Grimes 465df8bae1dSRodney W. Grimes switch (type) { 466df8bae1dSRodney W. Grimes 467df8bae1dSRodney W. Grimes case TYPE_UNCOMPRESSED_TCP: 46871b9cf73SPeter Wemm ip = (struct ip *) buf; 469df8bae1dSRodney W. Grimes if (ip->ip_p >= MAX_STATES) 470df8bae1dSRodney W. Grimes goto bad; 471df8bae1dSRodney W. Grimes cs = &comp->rstate[comp->last_recv = ip->ip_p]; 472df8bae1dSRodney W. Grimes comp->flags &=~ SLF_TOSS; 473df8bae1dSRodney W. Grimes ip->ip_p = IPPROTO_TCP; 474df8bae1dSRodney W. Grimes hlen = ip->ip_hl; 475df8bae1dSRodney W. Grimes hlen += ((struct tcphdr *)&((int *)ip)[hlen])->th_off; 476df8bae1dSRodney W. Grimes hlen <<= 2; 477df8bae1dSRodney W. Grimes BCOPY(ip, &cs->cs_ip, hlen); 478df8bae1dSRodney W. Grimes cs->cs_hlen = hlen; 479df8bae1dSRodney W. Grimes INCR(sls_uncompressedin) 48071b9cf73SPeter Wemm *hdrp = (u_char *) &cs->cs_ip; 48171b9cf73SPeter Wemm *hlenp = hlen; 48271b9cf73SPeter Wemm return (0); 483df8bae1dSRodney W. Grimes 484df8bae1dSRodney W. Grimes default: 485df8bae1dSRodney W. Grimes goto bad; 486df8bae1dSRodney W. Grimes 487df8bae1dSRodney W. Grimes case TYPE_COMPRESSED_TCP: 488df8bae1dSRodney W. Grimes break; 489df8bae1dSRodney W. Grimes } 490df8bae1dSRodney W. Grimes /* We've got a compressed packet. */ 491df8bae1dSRodney W. Grimes INCR(sls_compressedin) 49271b9cf73SPeter Wemm cp = buf; 493df8bae1dSRodney W. Grimes changes = *cp++; 494df8bae1dSRodney W. Grimes if (changes & NEW_C) { 495df8bae1dSRodney W. Grimes /* Make sure the state index is in range, then grab the state. 496df8bae1dSRodney W. Grimes * If we have a good state index, clear the 'discard' flag. */ 497df8bae1dSRodney W. Grimes if (*cp >= MAX_STATES) 498df8bae1dSRodney W. Grimes goto bad; 499df8bae1dSRodney W. Grimes 500df8bae1dSRodney W. Grimes comp->flags &=~ SLF_TOSS; 501df8bae1dSRodney W. Grimes comp->last_recv = *cp++; 502df8bae1dSRodney W. Grimes } else { 503df8bae1dSRodney W. Grimes /* this packet has an implicit state index. If we've 504df8bae1dSRodney W. Grimes * had a line error since the last time we got an 505df8bae1dSRodney W. Grimes * explicit state index, we have to toss the packet. */ 506df8bae1dSRodney W. Grimes if (comp->flags & SLF_TOSS) { 507df8bae1dSRodney W. Grimes INCR(sls_tossed) 50871b9cf73SPeter Wemm return (-1); 509df8bae1dSRodney W. Grimes } 510df8bae1dSRodney W. Grimes } 511df8bae1dSRodney W. Grimes cs = &comp->rstate[comp->last_recv]; 512df8bae1dSRodney W. Grimes hlen = cs->cs_ip.ip_hl << 2; 513df8bae1dSRodney W. Grimes th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen]; 514df8bae1dSRodney W. Grimes th->th_sum = htons((*cp << 8) | cp[1]); 515df8bae1dSRodney W. Grimes cp += 2; 516df8bae1dSRodney W. Grimes if (changes & TCP_PUSH_BIT) 517df8bae1dSRodney W. Grimes th->th_flags |= TH_PUSH; 518df8bae1dSRodney W. Grimes else 519df8bae1dSRodney W. Grimes th->th_flags &=~ TH_PUSH; 520df8bae1dSRodney W. Grimes 521df8bae1dSRodney W. Grimes switch (changes & SPECIALS_MASK) { 522df8bae1dSRodney W. Grimes case SPECIAL_I: 523df8bae1dSRodney W. Grimes { 524df8bae1dSRodney W. Grimes register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; 525df8bae1dSRodney W. Grimes th->th_ack = htonl(ntohl(th->th_ack) + i); 526df8bae1dSRodney W. Grimes th->th_seq = htonl(ntohl(th->th_seq) + i); 527df8bae1dSRodney W. Grimes } 528df8bae1dSRodney W. Grimes break; 529df8bae1dSRodney W. Grimes 530df8bae1dSRodney W. Grimes case SPECIAL_D: 531df8bae1dSRodney W. Grimes th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) 532df8bae1dSRodney W. Grimes - cs->cs_hlen); 533df8bae1dSRodney W. Grimes break; 534df8bae1dSRodney W. Grimes 535df8bae1dSRodney W. Grimes default: 536df8bae1dSRodney W. Grimes if (changes & NEW_U) { 537df8bae1dSRodney W. Grimes th->th_flags |= TH_URG; 538df8bae1dSRodney W. Grimes DECODEU(th->th_urp) 539df8bae1dSRodney W. Grimes } else 540df8bae1dSRodney W. Grimes th->th_flags &=~ TH_URG; 541df8bae1dSRodney W. Grimes if (changes & NEW_W) 542df8bae1dSRodney W. Grimes DECODES(th->th_win) 543df8bae1dSRodney W. Grimes if (changes & NEW_A) 544df8bae1dSRodney W. Grimes DECODEL(th->th_ack) 545df8bae1dSRodney W. Grimes if (changes & NEW_S) 546df8bae1dSRodney W. Grimes DECODEL(th->th_seq) 547df8bae1dSRodney W. Grimes break; 548df8bae1dSRodney W. Grimes } 549df8bae1dSRodney W. Grimes if (changes & NEW_I) { 550df8bae1dSRodney W. Grimes DECODES(cs->cs_ip.ip_id) 551df8bae1dSRodney W. Grimes } else 552df8bae1dSRodney W. Grimes cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1); 553df8bae1dSRodney W. Grimes 554df8bae1dSRodney W. Grimes /* 555df8bae1dSRodney W. Grimes * At this point, cp points to the first byte of data in the 55671b9cf73SPeter Wemm * packet. Fill in the IP total length and update the IP 55771b9cf73SPeter Wemm * header checksum. 558df8bae1dSRodney W. Grimes */ 55971b9cf73SPeter Wemm vjlen = cp - buf; 56071b9cf73SPeter Wemm buflen -= vjlen; 56171b9cf73SPeter Wemm if (buflen < 0) 562df8bae1dSRodney W. Grimes /* we must have dropped some characters (crc should detect 563df8bae1dSRodney W. Grimes * this but the old slip framing won't) */ 564df8bae1dSRodney W. Grimes goto bad; 565df8bae1dSRodney W. Grimes 56671b9cf73SPeter Wemm total_len += cs->cs_hlen - vjlen; 56771b9cf73SPeter Wemm cs->cs_ip.ip_len = htons(total_len); 568df8bae1dSRodney W. Grimes 569df8bae1dSRodney W. Grimes /* recompute the ip header checksum */ 57071b9cf73SPeter Wemm bp = (u_short *) &cs->cs_ip; 57171b9cf73SPeter Wemm cs->cs_ip.ip_sum = 0; 572df8bae1dSRodney W. Grimes for (changes = 0; hlen > 0; hlen -= 2) 573df8bae1dSRodney W. Grimes changes += *bp++; 574df8bae1dSRodney W. Grimes changes = (changes & 0xffff) + (changes >> 16); 575df8bae1dSRodney W. Grimes changes = (changes & 0xffff) + (changes >> 16); 57671b9cf73SPeter Wemm cs->cs_ip.ip_sum = ~ changes; 57771b9cf73SPeter Wemm 57871b9cf73SPeter Wemm *hdrp = (u_char *) &cs->cs_ip; 57971b9cf73SPeter Wemm *hlenp = cs->cs_hlen; 58071b9cf73SPeter Wemm return vjlen; 58171b9cf73SPeter Wemm 582df8bae1dSRodney W. Grimes bad: 583df8bae1dSRodney W. Grimes comp->flags |= SLF_TOSS; 584df8bae1dSRodney W. Grimes INCR(sls_errorin) 58571b9cf73SPeter Wemm return (-1); 586df8bae1dSRodney W. Grimes } 587