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 34df440948SPoul-Henning Kamp * $Id: slcompress.c,v 1.3 1994/08/18 22:35:22 wollman 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 70df8bae1dSRodney W. Grimes sl_compress_init(comp) 71df8bae1dSRodney W. Grimes struct slcompress *comp; 72df8bae1dSRodney W. Grimes { 73df8bae1dSRodney W. Grimes register u_int i; 74df8bae1dSRodney W. Grimes register struct cstate *tstate = comp->tstate; 75df8bae1dSRodney W. Grimes 76df8bae1dSRodney W. Grimes bzero((char *)comp, sizeof(*comp)); 77df8bae1dSRodney W. Grimes for (i = MAX_STATES - 1; i > 0; --i) { 78df8bae1dSRodney W. Grimes tstate[i].cs_id = i; 79df8bae1dSRodney W. Grimes tstate[i].cs_next = &tstate[i - 1]; 80df8bae1dSRodney W. Grimes } 81df8bae1dSRodney W. Grimes tstate[0].cs_next = &tstate[MAX_STATES - 1]; 82df8bae1dSRodney W. Grimes tstate[0].cs_id = 0; 83df8bae1dSRodney W. Grimes comp->last_cs = &tstate[0]; 84df8bae1dSRodney W. Grimes comp->last_recv = 255; 85df8bae1dSRodney W. Grimes comp->last_xmit = 255; 86df8bae1dSRodney W. Grimes comp->flags = SLF_TOSS; 87df8bae1dSRodney W. Grimes } 88df8bae1dSRodney W. Grimes 89df8bae1dSRodney W. Grimes 90df8bae1dSRodney W. Grimes /* ENCODE encodes a number that is known to be non-zero. ENCODEZ 91df8bae1dSRodney W. Grimes * checks for zero (since zero has to be encoded in the long, 3 byte 92df8bae1dSRodney W. Grimes * form). 93df8bae1dSRodney W. Grimes */ 94df8bae1dSRodney W. Grimes #define ENCODE(n) { \ 95df8bae1dSRodney W. Grimes if ((u_short)(n) >= 256) { \ 96df8bae1dSRodney W. Grimes *cp++ = 0; \ 97df8bae1dSRodney W. Grimes cp[1] = (n); \ 98df8bae1dSRodney W. Grimes cp[0] = (n) >> 8; \ 99df8bae1dSRodney W. Grimes cp += 2; \ 100df8bae1dSRodney W. Grimes } else { \ 101df8bae1dSRodney W. Grimes *cp++ = (n); \ 102df8bae1dSRodney W. Grimes } \ 103df8bae1dSRodney W. Grimes } 104df8bae1dSRodney W. Grimes #define ENCODEZ(n) { \ 105df8bae1dSRodney W. Grimes if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ 106df8bae1dSRodney W. Grimes *cp++ = 0; \ 107df8bae1dSRodney W. Grimes cp[1] = (n); \ 108df8bae1dSRodney W. Grimes cp[0] = (n) >> 8; \ 109df8bae1dSRodney W. Grimes cp += 2; \ 110df8bae1dSRodney W. Grimes } else { \ 111df8bae1dSRodney W. Grimes *cp++ = (n); \ 112df8bae1dSRodney W. Grimes } \ 113df8bae1dSRodney W. Grimes } 114df8bae1dSRodney W. Grimes 115df8bae1dSRodney W. Grimes #define DECODEL(f) { \ 116df8bae1dSRodney W. Grimes if (*cp == 0) {\ 117df8bae1dSRodney W. Grimes (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \ 118df8bae1dSRodney W. Grimes cp += 3; \ 119df8bae1dSRodney W. Grimes } else { \ 120df8bae1dSRodney W. Grimes (f) = htonl(ntohl(f) + (u_long)*cp++); \ 121df8bae1dSRodney W. Grimes } \ 122df8bae1dSRodney W. Grimes } 123df8bae1dSRodney W. Grimes 124df8bae1dSRodney W. Grimes #define DECODES(f) { \ 125df8bae1dSRodney W. Grimes if (*cp == 0) {\ 126df8bae1dSRodney W. Grimes (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \ 127df8bae1dSRodney W. Grimes cp += 3; \ 128df8bae1dSRodney W. Grimes } else { \ 129df8bae1dSRodney W. Grimes (f) = htons(ntohs(f) + (u_long)*cp++); \ 130df8bae1dSRodney W. Grimes } \ 131df8bae1dSRodney W. Grimes } 132df8bae1dSRodney W. Grimes 133df8bae1dSRodney W. Grimes #define DECODEU(f) { \ 134df8bae1dSRodney W. Grimes if (*cp == 0) {\ 135df8bae1dSRodney W. Grimes (f) = htons((cp[1] << 8) | cp[2]); \ 136df8bae1dSRodney W. Grimes cp += 3; \ 137df8bae1dSRodney W. Grimes } else { \ 138df8bae1dSRodney W. Grimes (f) = htons((u_long)*cp++); \ 139df8bae1dSRodney W. Grimes } \ 140df8bae1dSRodney W. Grimes } 141df8bae1dSRodney W. Grimes 142df8bae1dSRodney W. Grimes u_int 143df8bae1dSRodney W. Grimes sl_compress_tcp(m, ip, comp, compress_cid) 144df8bae1dSRodney W. Grimes struct mbuf *m; 145df8bae1dSRodney W. Grimes register struct ip *ip; 146df8bae1dSRodney W. Grimes struct slcompress *comp; 147df8bae1dSRodney W. Grimes int compress_cid; 148df8bae1dSRodney W. Grimes { 149df8bae1dSRodney W. Grimes register struct cstate *cs = comp->last_cs->cs_next; 150df8bae1dSRodney W. Grimes register u_int hlen = ip->ip_hl; 151df8bae1dSRodney W. Grimes register struct tcphdr *oth; 152df8bae1dSRodney W. Grimes register struct tcphdr *th; 153df8bae1dSRodney W. Grimes register u_int deltaS, deltaA; 154df8bae1dSRodney W. Grimes register u_int changes = 0; 155df8bae1dSRodney W. Grimes u_char new_seq[16]; 156df8bae1dSRodney W. Grimes register u_char *cp = new_seq; 157df8bae1dSRodney W. Grimes 158df8bae1dSRodney W. Grimes /* 159df8bae1dSRodney W. Grimes * Bail if this is an IP fragment or if the TCP packet isn't 160df8bae1dSRodney W. Grimes * `compressible' (i.e., ACK isn't set or some other control bit is 161df8bae1dSRodney W. Grimes * set). (We assume that the caller has already made sure the 162df8bae1dSRodney W. Grimes * packet is IP proto TCP). 163df8bae1dSRodney W. Grimes */ 164df8bae1dSRodney W. Grimes if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40) 165df8bae1dSRodney W. Grimes return (TYPE_IP); 166df8bae1dSRodney W. Grimes 167df8bae1dSRodney W. Grimes th = (struct tcphdr *)&((int *)ip)[hlen]; 168df8bae1dSRodney W. Grimes if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK) 169df8bae1dSRodney W. Grimes return (TYPE_IP); 170df8bae1dSRodney W. Grimes /* 171df8bae1dSRodney W. Grimes * Packet is compressible -- we're going to send either a 172df8bae1dSRodney W. Grimes * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need 173df8bae1dSRodney W. Grimes * to locate (or create) the connection state. Special case the 174df8bae1dSRodney W. Grimes * most recently used connection since it's most likely to be used 175df8bae1dSRodney W. Grimes * again & we don't have to do any reordering if it's used. 176df8bae1dSRodney W. Grimes */ 177df8bae1dSRodney W. Grimes INCR(sls_packets) 178df8bae1dSRodney W. Grimes if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr || 179df8bae1dSRodney W. Grimes ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr || 180df8bae1dSRodney W. Grimes *(int *)th != ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) { 181df8bae1dSRodney W. Grimes /* 182df8bae1dSRodney W. Grimes * Wasn't the first -- search for it. 183df8bae1dSRodney W. Grimes * 184df8bae1dSRodney W. Grimes * States are kept in a circularly linked list with 185df8bae1dSRodney W. Grimes * last_cs pointing to the end of the list. The 186df8bae1dSRodney W. Grimes * list is kept in lru order by moving a state to the 187df8bae1dSRodney W. Grimes * head of the list whenever it is referenced. Since 188df8bae1dSRodney W. Grimes * the list is short and, empirically, the connection 189df8bae1dSRodney W. Grimes * we want is almost always near the front, we locate 190df8bae1dSRodney W. Grimes * states via linear search. If we don't find a state 191df8bae1dSRodney W. Grimes * for the datagram, the oldest state is (re-)used. 192df8bae1dSRodney W. Grimes */ 193df8bae1dSRodney W. Grimes register struct cstate *lcs; 194df8bae1dSRodney W. Grimes register struct cstate *lastcs = comp->last_cs; 195df8bae1dSRodney W. Grimes 196df8bae1dSRodney W. Grimes do { 197df8bae1dSRodney W. Grimes lcs = cs; cs = cs->cs_next; 198df8bae1dSRodney W. Grimes INCR(sls_searches) 199df8bae1dSRodney W. Grimes if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr 200df8bae1dSRodney W. Grimes && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr 201df8bae1dSRodney W. Grimes && *(int *)th == ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) 202df8bae1dSRodney W. Grimes goto found; 203df8bae1dSRodney W. Grimes } while (cs != lastcs); 204df8bae1dSRodney W. Grimes 205df8bae1dSRodney W. Grimes /* 206df8bae1dSRodney W. Grimes * Didn't find it -- re-use oldest cstate. Send an 207df8bae1dSRodney W. Grimes * uncompressed packet that tells the other side what 208df8bae1dSRodney W. Grimes * connection number we're using for this conversation. 209df8bae1dSRodney W. Grimes * Note that since the state list is circular, the oldest 210df8bae1dSRodney W. Grimes * state points to the newest and we only need to set 211df8bae1dSRodney W. Grimes * last_cs to update the lru linkage. 212df8bae1dSRodney W. Grimes */ 213df8bae1dSRodney W. Grimes INCR(sls_misses) 214df8bae1dSRodney W. Grimes comp->last_cs = lcs; 215df8bae1dSRodney W. Grimes hlen += th->th_off; 216df8bae1dSRodney W. Grimes hlen <<= 2; 217df8bae1dSRodney W. Grimes goto uncompressed; 218df8bae1dSRodney W. Grimes 219df8bae1dSRodney W. Grimes found: 220df8bae1dSRodney W. Grimes /* 221df8bae1dSRodney W. Grimes * Found it -- move to the front on the connection list. 222df8bae1dSRodney W. Grimes */ 223df8bae1dSRodney W. Grimes if (cs == lastcs) 224df8bae1dSRodney W. Grimes comp->last_cs = lcs; 225df8bae1dSRodney W. Grimes else { 226df8bae1dSRodney W. Grimes lcs->cs_next = cs->cs_next; 227df8bae1dSRodney W. Grimes cs->cs_next = lastcs->cs_next; 228df8bae1dSRodney W. Grimes lastcs->cs_next = cs; 229df8bae1dSRodney W. Grimes } 230df8bae1dSRodney W. Grimes } 231df8bae1dSRodney W. Grimes 232df8bae1dSRodney W. Grimes /* 233df8bae1dSRodney W. Grimes * Make sure that only what we expect to change changed. The first 234df8bae1dSRodney W. Grimes * line of the `if' checks the IP protocol version, header length & 235df8bae1dSRodney W. Grimes * type of service. The 2nd line checks the "Don't fragment" bit. 236df8bae1dSRodney W. Grimes * The 3rd line checks the time-to-live and protocol (the protocol 237df8bae1dSRodney W. Grimes * check is unnecessary but costless). The 4th line checks the TCP 238df8bae1dSRodney W. Grimes * header length. The 5th line checks IP options, if any. The 6th 239df8bae1dSRodney W. Grimes * line checks TCP options, if any. If any of these things are 240df8bae1dSRodney W. Grimes * different between the previous & current datagram, we send the 241df8bae1dSRodney W. Grimes * current datagram `uncompressed'. 242df8bae1dSRodney W. Grimes */ 243df8bae1dSRodney W. Grimes oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen]; 244df8bae1dSRodney W. Grimes deltaS = hlen; 245df8bae1dSRodney W. Grimes hlen += th->th_off; 246df8bae1dSRodney W. Grimes hlen <<= 2; 247df8bae1dSRodney W. Grimes 248df8bae1dSRodney W. Grimes if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] || 249df8bae1dSRodney W. Grimes ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] || 250df8bae1dSRodney W. Grimes ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] || 251df8bae1dSRodney W. Grimes th->th_off != oth->th_off || 252df8bae1dSRodney W. Grimes (deltaS > 5 && 253df8bae1dSRodney W. Grimes BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) || 254df8bae1dSRodney W. Grimes (th->th_off > 5 && 255df8bae1dSRodney W. Grimes BCMP(th + 1, oth + 1, (th->th_off - 5) << 2))) 256df8bae1dSRodney W. Grimes goto uncompressed; 257df8bae1dSRodney W. Grimes 258df8bae1dSRodney W. Grimes /* 259df8bae1dSRodney W. Grimes * Figure out which of the changing fields changed. The 260df8bae1dSRodney W. Grimes * receiver expects changes in the order: urgent, window, 261df8bae1dSRodney W. Grimes * ack, seq (the order minimizes the number of temporaries 262df8bae1dSRodney W. Grimes * needed in this section of code). 263df8bae1dSRodney W. Grimes */ 264df8bae1dSRodney W. Grimes if (th->th_flags & TH_URG) { 265df8bae1dSRodney W. Grimes deltaS = ntohs(th->th_urp); 266df8bae1dSRodney W. Grimes ENCODEZ(deltaS); 267df8bae1dSRodney W. Grimes changes |= NEW_U; 268df8bae1dSRodney W. Grimes } else if (th->th_urp != oth->th_urp) 269df8bae1dSRodney W. Grimes /* argh! URG not set but urp changed -- a sensible 270df8bae1dSRodney W. Grimes * implementation should never do this but RFC793 271df8bae1dSRodney W. Grimes * doesn't prohibit the change so we have to deal 272df8bae1dSRodney W. Grimes * with it. */ 273df8bae1dSRodney W. Grimes goto uncompressed; 274df8bae1dSRodney W. Grimes 275df440948SPoul-Henning Kamp deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win)); 276df440948SPoul-Henning Kamp if (deltaS) { 277df8bae1dSRodney W. Grimes ENCODE(deltaS); 278df8bae1dSRodney W. Grimes changes |= NEW_W; 279df8bae1dSRodney W. Grimes } 280df8bae1dSRodney W. Grimes 281df440948SPoul-Henning Kamp deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack); 282df440948SPoul-Henning Kamp if (deltaA) { 283df8bae1dSRodney W. Grimes if (deltaA > 0xffff) 284df8bae1dSRodney W. Grimes goto uncompressed; 285df8bae1dSRodney W. Grimes ENCODE(deltaA); 286df8bae1dSRodney W. Grimes changes |= NEW_A; 287df8bae1dSRodney W. Grimes } 288df8bae1dSRodney W. Grimes 289df440948SPoul-Henning Kamp deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq); 290df440948SPoul-Henning Kamp if (deltaS) { 291df8bae1dSRodney W. Grimes if (deltaS > 0xffff) 292df8bae1dSRodney W. Grimes goto uncompressed; 293df8bae1dSRodney W. Grimes ENCODE(deltaS); 294df8bae1dSRodney W. Grimes changes |= NEW_S; 295df8bae1dSRodney W. Grimes } 296df8bae1dSRodney W. Grimes 297df8bae1dSRodney W. Grimes switch(changes) { 298df8bae1dSRodney W. Grimes 299df8bae1dSRodney W. Grimes case 0: 300df8bae1dSRodney W. Grimes /* 301df8bae1dSRodney W. Grimes * Nothing changed. If this packet contains data and the 302df8bae1dSRodney W. Grimes * last one didn't, this is probably a data packet following 303df8bae1dSRodney W. Grimes * an ack (normal on an interactive connection) and we send 304df8bae1dSRodney W. Grimes * it compressed. Otherwise it's probably a retransmit, 305df8bae1dSRodney W. Grimes * retransmitted ack or window probe. Send it uncompressed 306df8bae1dSRodney W. Grimes * in case the other side missed the compressed version. 307df8bae1dSRodney W. Grimes */ 308df8bae1dSRodney W. Grimes if (ip->ip_len != cs->cs_ip.ip_len && 309df8bae1dSRodney W. Grimes ntohs(cs->cs_ip.ip_len) == hlen) 310df8bae1dSRodney W. Grimes break; 311df8bae1dSRodney W. Grimes 312df8bae1dSRodney W. Grimes /* (fall through) */ 313df8bae1dSRodney W. Grimes 314df8bae1dSRodney W. Grimes case SPECIAL_I: 315df8bae1dSRodney W. Grimes case SPECIAL_D: 316df8bae1dSRodney W. Grimes /* 317df8bae1dSRodney W. Grimes * actual changes match one of our special case encodings -- 318df8bae1dSRodney W. Grimes * send packet uncompressed. 319df8bae1dSRodney W. Grimes */ 320df8bae1dSRodney W. Grimes goto uncompressed; 321df8bae1dSRodney W. Grimes 322df8bae1dSRodney W. Grimes case NEW_S|NEW_A: 323df8bae1dSRodney W. Grimes if (deltaS == deltaA && 324df8bae1dSRodney W. Grimes deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { 325df8bae1dSRodney W. Grimes /* special case for echoed terminal traffic */ 326df8bae1dSRodney W. Grimes changes = SPECIAL_I; 327df8bae1dSRodney W. Grimes cp = new_seq; 328df8bae1dSRodney W. Grimes } 329df8bae1dSRodney W. Grimes break; 330df8bae1dSRodney W. Grimes 331df8bae1dSRodney W. Grimes case NEW_S: 332df8bae1dSRodney W. Grimes if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { 333df8bae1dSRodney W. Grimes /* special case for data xfer */ 334df8bae1dSRodney W. Grimes changes = SPECIAL_D; 335df8bae1dSRodney W. Grimes cp = new_seq; 336df8bae1dSRodney W. Grimes } 337df8bae1dSRodney W. Grimes break; 338df8bae1dSRodney W. Grimes } 339df8bae1dSRodney W. Grimes 340df8bae1dSRodney W. Grimes deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id); 341df8bae1dSRodney W. Grimes if (deltaS != 1) { 342df8bae1dSRodney W. Grimes ENCODEZ(deltaS); 343df8bae1dSRodney W. Grimes changes |= NEW_I; 344df8bae1dSRodney W. Grimes } 345df8bae1dSRodney W. Grimes if (th->th_flags & TH_PUSH) 346df8bae1dSRodney W. Grimes changes |= TCP_PUSH_BIT; 347df8bae1dSRodney W. Grimes /* 348df8bae1dSRodney W. Grimes * Grab the cksum before we overwrite it below. Then update our 349df8bae1dSRodney W. Grimes * state with this packet's header. 350df8bae1dSRodney W. Grimes */ 351df8bae1dSRodney W. Grimes deltaA = ntohs(th->th_sum); 352df8bae1dSRodney W. Grimes BCOPY(ip, &cs->cs_ip, hlen); 353df8bae1dSRodney W. Grimes 354df8bae1dSRodney W. Grimes /* 355df8bae1dSRodney W. Grimes * We want to use the original packet as our compressed packet. 356df8bae1dSRodney W. Grimes * (cp - new_seq) is the number of bytes we need for compressed 357df8bae1dSRodney W. Grimes * sequence numbers. In addition we need one byte for the change 358df8bae1dSRodney W. Grimes * mask, one for the connection id and two for the tcp checksum. 359df8bae1dSRodney W. Grimes * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how 360df8bae1dSRodney W. Grimes * many bytes of the original packet to toss so subtract the two to 361df8bae1dSRodney W. Grimes * get the new packet size. 362df8bae1dSRodney W. Grimes */ 363df8bae1dSRodney W. Grimes deltaS = cp - new_seq; 364df8bae1dSRodney W. Grimes cp = (u_char *)ip; 365df8bae1dSRodney W. Grimes if (compress_cid == 0 || comp->last_xmit != cs->cs_id) { 366df8bae1dSRodney W. Grimes comp->last_xmit = cs->cs_id; 367df8bae1dSRodney W. Grimes hlen -= deltaS + 4; 368df8bae1dSRodney W. Grimes cp += hlen; 369df8bae1dSRodney W. Grimes *cp++ = changes | NEW_C; 370df8bae1dSRodney W. Grimes *cp++ = cs->cs_id; 371df8bae1dSRodney W. Grimes } else { 372df8bae1dSRodney W. Grimes hlen -= deltaS + 3; 373df8bae1dSRodney W. Grimes cp += hlen; 374df8bae1dSRodney W. Grimes *cp++ = changes; 375df8bae1dSRodney W. Grimes } 376df8bae1dSRodney W. Grimes m->m_len -= hlen; 377df8bae1dSRodney W. Grimes m->m_data += hlen; 378df8bae1dSRodney W. Grimes *cp++ = deltaA >> 8; 379df8bae1dSRodney W. Grimes *cp++ = deltaA; 380df8bae1dSRodney W. Grimes BCOPY(new_seq, cp, deltaS); 381df8bae1dSRodney W. Grimes INCR(sls_compressed) 382df8bae1dSRodney W. Grimes return (TYPE_COMPRESSED_TCP); 383df8bae1dSRodney W. Grimes 384df8bae1dSRodney W. Grimes /* 385df8bae1dSRodney W. Grimes * Update connection state cs & send uncompressed packet ('uncompressed' 386df8bae1dSRodney W. Grimes * means a regular ip/tcp packet but with the 'conversation id' we hope 387df8bae1dSRodney W. Grimes * to use on future compressed packets in the protocol field). 388df8bae1dSRodney W. Grimes */ 389df8bae1dSRodney W. Grimes uncompressed: 390df8bae1dSRodney W. Grimes BCOPY(ip, &cs->cs_ip, hlen); 391df8bae1dSRodney W. Grimes ip->ip_p = cs->cs_id; 392df8bae1dSRodney W. Grimes comp->last_xmit = cs->cs_id; 393df8bae1dSRodney W. Grimes return (TYPE_UNCOMPRESSED_TCP); 394df8bae1dSRodney W. Grimes } 395df8bae1dSRodney W. Grimes 396df8bae1dSRodney W. Grimes 397df8bae1dSRodney W. Grimes int 398df8bae1dSRodney W. Grimes sl_uncompress_tcp(bufp, len, type, comp) 399df8bae1dSRodney W. Grimes u_char **bufp; 400df8bae1dSRodney W. Grimes int len; 401df8bae1dSRodney W. Grimes u_int type; 402df8bae1dSRodney W. Grimes struct slcompress *comp; 403df8bae1dSRodney W. Grimes { 404df8bae1dSRodney W. Grimes register u_char *cp; 405df8bae1dSRodney W. Grimes register u_int hlen, changes; 406df8bae1dSRodney W. Grimes register struct tcphdr *th; 407df8bae1dSRodney W. Grimes register struct cstate *cs; 408df8bae1dSRodney W. Grimes register struct ip *ip; 409df8bae1dSRodney W. Grimes 410df8bae1dSRodney W. Grimes switch (type) { 411df8bae1dSRodney W. Grimes 412df8bae1dSRodney W. Grimes case TYPE_UNCOMPRESSED_TCP: 413df8bae1dSRodney W. Grimes ip = (struct ip *) *bufp; 414df8bae1dSRodney W. Grimes if (ip->ip_p >= MAX_STATES) 415df8bae1dSRodney W. Grimes goto bad; 416df8bae1dSRodney W. Grimes cs = &comp->rstate[comp->last_recv = ip->ip_p]; 417df8bae1dSRodney W. Grimes comp->flags &=~ SLF_TOSS; 418df8bae1dSRodney W. Grimes ip->ip_p = IPPROTO_TCP; 419df8bae1dSRodney W. Grimes hlen = ip->ip_hl; 420df8bae1dSRodney W. Grimes hlen += ((struct tcphdr *)&((int *)ip)[hlen])->th_off; 421df8bae1dSRodney W. Grimes hlen <<= 2; 422df8bae1dSRodney W. Grimes BCOPY(ip, &cs->cs_ip, hlen); 423df8bae1dSRodney W. Grimes cs->cs_ip.ip_sum = 0; 424df8bae1dSRodney W. Grimes cs->cs_hlen = hlen; 425df8bae1dSRodney W. Grimes INCR(sls_uncompressedin) 426df8bae1dSRodney W. Grimes return (len); 427df8bae1dSRodney W. Grimes 428df8bae1dSRodney W. Grimes default: 429df8bae1dSRodney W. Grimes goto bad; 430df8bae1dSRodney W. Grimes 431df8bae1dSRodney W. Grimes case TYPE_COMPRESSED_TCP: 432df8bae1dSRodney W. Grimes break; 433df8bae1dSRodney W. Grimes } 434df8bae1dSRodney W. Grimes /* We've got a compressed packet. */ 435df8bae1dSRodney W. Grimes INCR(sls_compressedin) 436df8bae1dSRodney W. Grimes cp = *bufp; 437df8bae1dSRodney W. Grimes changes = *cp++; 438df8bae1dSRodney W. Grimes if (changes & NEW_C) { 439df8bae1dSRodney W. Grimes /* Make sure the state index is in range, then grab the state. 440df8bae1dSRodney W. Grimes * If we have a good state index, clear the 'discard' flag. */ 441df8bae1dSRodney W. Grimes if (*cp >= MAX_STATES) 442df8bae1dSRodney W. Grimes goto bad; 443df8bae1dSRodney W. Grimes 444df8bae1dSRodney W. Grimes comp->flags &=~ SLF_TOSS; 445df8bae1dSRodney W. Grimes comp->last_recv = *cp++; 446df8bae1dSRodney W. Grimes } else { 447df8bae1dSRodney W. Grimes /* this packet has an implicit state index. If we've 448df8bae1dSRodney W. Grimes * had a line error since the last time we got an 449df8bae1dSRodney W. Grimes * explicit state index, we have to toss the packet. */ 450df8bae1dSRodney W. Grimes if (comp->flags & SLF_TOSS) { 451df8bae1dSRodney W. Grimes INCR(sls_tossed) 452df8bae1dSRodney W. Grimes return (0); 453df8bae1dSRodney W. Grimes } 454df8bae1dSRodney W. Grimes } 455df8bae1dSRodney W. Grimes cs = &comp->rstate[comp->last_recv]; 456df8bae1dSRodney W. Grimes hlen = cs->cs_ip.ip_hl << 2; 457df8bae1dSRodney W. Grimes th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen]; 458df8bae1dSRodney W. Grimes th->th_sum = htons((*cp << 8) | cp[1]); 459df8bae1dSRodney W. Grimes cp += 2; 460df8bae1dSRodney W. Grimes if (changes & TCP_PUSH_BIT) 461df8bae1dSRodney W. Grimes th->th_flags |= TH_PUSH; 462df8bae1dSRodney W. Grimes else 463df8bae1dSRodney W. Grimes th->th_flags &=~ TH_PUSH; 464df8bae1dSRodney W. Grimes 465df8bae1dSRodney W. Grimes switch (changes & SPECIALS_MASK) { 466df8bae1dSRodney W. Grimes case SPECIAL_I: 467df8bae1dSRodney W. Grimes { 468df8bae1dSRodney W. Grimes register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; 469df8bae1dSRodney W. Grimes th->th_ack = htonl(ntohl(th->th_ack) + i); 470df8bae1dSRodney W. Grimes th->th_seq = htonl(ntohl(th->th_seq) + i); 471df8bae1dSRodney W. Grimes } 472df8bae1dSRodney W. Grimes break; 473df8bae1dSRodney W. Grimes 474df8bae1dSRodney W. Grimes case SPECIAL_D: 475df8bae1dSRodney W. Grimes th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) 476df8bae1dSRodney W. Grimes - cs->cs_hlen); 477df8bae1dSRodney W. Grimes break; 478df8bae1dSRodney W. Grimes 479df8bae1dSRodney W. Grimes default: 480df8bae1dSRodney W. Grimes if (changes & NEW_U) { 481df8bae1dSRodney W. Grimes th->th_flags |= TH_URG; 482df8bae1dSRodney W. Grimes DECODEU(th->th_urp) 483df8bae1dSRodney W. Grimes } else 484df8bae1dSRodney W. Grimes th->th_flags &=~ TH_URG; 485df8bae1dSRodney W. Grimes if (changes & NEW_W) 486df8bae1dSRodney W. Grimes DECODES(th->th_win) 487df8bae1dSRodney W. Grimes if (changes & NEW_A) 488df8bae1dSRodney W. Grimes DECODEL(th->th_ack) 489df8bae1dSRodney W. Grimes if (changes & NEW_S) 490df8bae1dSRodney W. Grimes DECODEL(th->th_seq) 491df8bae1dSRodney W. Grimes break; 492df8bae1dSRodney W. Grimes } 493df8bae1dSRodney W. Grimes if (changes & NEW_I) { 494df8bae1dSRodney W. Grimes DECODES(cs->cs_ip.ip_id) 495df8bae1dSRodney W. Grimes } else 496df8bae1dSRodney W. Grimes cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1); 497df8bae1dSRodney W. Grimes 498df8bae1dSRodney W. Grimes /* 499df8bae1dSRodney W. Grimes * At this point, cp points to the first byte of data in the 500df8bae1dSRodney W. Grimes * packet. If we're not aligned on a 4-byte boundary, copy the 501df8bae1dSRodney W. Grimes * data down so the ip & tcp headers will be aligned. Then back up 502df8bae1dSRodney W. Grimes * cp by the tcp/ip header length to make room for the reconstructed 503df8bae1dSRodney W. Grimes * header (we assume the packet we were handed has enough space to 504df8bae1dSRodney W. Grimes * prepend 128 bytes of header). Adjust the length to account for 505df8bae1dSRodney W. Grimes * the new header & fill in the IP total length. 506df8bae1dSRodney W. Grimes */ 507df8bae1dSRodney W. Grimes len -= (cp - *bufp); 508df8bae1dSRodney W. Grimes if (len < 0) 509df8bae1dSRodney W. Grimes /* we must have dropped some characters (crc should detect 510df8bae1dSRodney W. Grimes * this but the old slip framing won't) */ 511df8bae1dSRodney W. Grimes goto bad; 512df8bae1dSRodney W. Grimes 513df8bae1dSRodney W. Grimes if ((int)cp & 3) { 514df8bae1dSRodney W. Grimes if (len > 0) 515df8bae1dSRodney W. Grimes (void) ovbcopy(cp, (caddr_t)((int)cp &~ 3), len); 516df8bae1dSRodney W. Grimes cp = (u_char *)((int)cp &~ 3); 517df8bae1dSRodney W. Grimes } 518df8bae1dSRodney W. Grimes cp -= cs->cs_hlen; 519df8bae1dSRodney W. Grimes len += cs->cs_hlen; 520df8bae1dSRodney W. Grimes cs->cs_ip.ip_len = htons(len); 521df8bae1dSRodney W. Grimes BCOPY(&cs->cs_ip, cp, cs->cs_hlen); 522df8bae1dSRodney W. Grimes *bufp = cp; 523df8bae1dSRodney W. Grimes 524df8bae1dSRodney W. Grimes /* recompute the ip header checksum */ 525df8bae1dSRodney W. Grimes { 526df8bae1dSRodney W. Grimes register u_short *bp = (u_short *)cp; 527df8bae1dSRodney W. Grimes for (changes = 0; hlen > 0; hlen -= 2) 528df8bae1dSRodney W. Grimes changes += *bp++; 529df8bae1dSRodney W. Grimes changes = (changes & 0xffff) + (changes >> 16); 530df8bae1dSRodney W. Grimes changes = (changes & 0xffff) + (changes >> 16); 531df8bae1dSRodney W. Grimes ((struct ip *)cp)->ip_sum = ~ changes; 532df8bae1dSRodney W. Grimes } 533df8bae1dSRodney W. Grimes return (len); 534df8bae1dSRodney W. Grimes bad: 535df8bae1dSRodney W. Grimes comp->flags |= SLF_TOSS; 536df8bae1dSRodney W. Grimes INCR(sls_errorin) 537df8bae1dSRodney W. Grimes return (0); 538df8bae1dSRodney W. Grimes } 539