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