2 * Routines to compress and uncompess tcp packets (for transmission
\r
3 * over low speed serial lines.
\r
5 * Copyright (c) 1989 Regents of the University of California.
\r
6 * All rights reserved.
\r
8 * Redistribution and use in source and binary forms are permitted
\r
9 * provided that the above copyright notice and this paragraph are
\r
10 * duplicated in all such forms and that any documentation,
\r
11 * advertising materials, and other materials related to such
\r
12 * distribution and use acknowledge that the software was developed
\r
13 * by the University of California, Berkeley. The name of the
\r
14 * University may not be used to endorse or promote products derived
\r
15 * from this software without specific prior written permission.
\r
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
\r
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
\r
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
\r
20 * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
\r
21 * - Initial distribution.
\r
23 * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au,
\r
24 * so that the entire packet being decompressed doesn't have
\r
25 * to be in contiguous memory (just the compressed header).
\r
27 * Modified March 1998 by Guy Lancaster, glanca@gesn.com,
\r
28 * for a 16 bit processor.
\r
35 #include "pppdebug.h"
\r
40 #define INCR(counter) ++comp->stats.counter
\r
42 #define INCR(counter)
\r
45 #if defined(NO_CHAR_BITFIELDS)
\r
46 #define getip_hl(base) ((base).ip_hl_v&0xf)
\r
47 #define getth_off(base) (((base).th_x2_off&0xf0)>>4)
\r
49 #define getip_hl(base) ((base).ip_hl)
\r
50 #define getth_off(base) ((base).th_off)
\r
53 void vj_compress_init(struct vjcompress *comp)
\r
56 register struct cstate *tstate = comp->tstate;
\r
59 memset((char *)comp, 0, sizeof(*comp));
\r
61 comp->maxSlotIndex = MAX_SLOTS - 1;
\r
62 comp->compressSlot = 0; /* Disable slot ID compression by default. */
\r
63 for (i = MAX_SLOTS - 1; i > 0; --i) {
\r
64 tstate[i].cs_id = i;
\r
65 tstate[i].cs_next = &tstate[i - 1];
\r
67 tstate[0].cs_next = &tstate[MAX_SLOTS - 1];
\r
68 tstate[0].cs_id = 0;
\r
69 comp->last_cs = &tstate[0];
\r
70 comp->last_recv = 255;
\r
71 comp->last_xmit = 255;
\r
72 comp->flags = VJF_TOSS;
\r
76 /* ENCODE encodes a number that is known to be non-zero. ENCODEZ
\r
77 * checks for zero (since zero has to be encoded in the long, 3 byte
\r
80 #define ENCODE(n) { \
\r
81 if ((u_short)(n) >= 256) { \
\r
90 #define ENCODEZ(n) { \
\r
91 if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
\r
101 #define DECODEL(f) { \
\r
103 u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \
\r
104 (f) = htonl(tmp); \
\r
107 u32_t tmp = ntohl(f) + (u32_t)*cp++; \
\r
108 (f) = htonl(tmp); \
\r
112 #define DECODES(f) { \
\r
114 u_short tmp = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \
\r
115 (f) = htons(tmp); \
\r
118 u_short tmp = ntohs(f) + (u_short)*cp++; \
\r
119 (f) = htons(tmp); \
\r
123 #define DECODEU(f) { \
\r
125 (f) = htons(((u_short)cp[1] << 8) | cp[2]); \
\r
128 (f) = htons((u_short)*cp++); \
\r
133 * vj_compress_tcp - Attempt to do Van Jacobsen header compression on a
\r
134 * packet. This assumes that nb and comp are not null and that the first
\r
135 * buffer of the chain contains a valid IP header.
\r
136 * Return the VJ type code indicating whether or not the packet was
\r
139 u_int vj_compress_tcp(
\r
140 struct vjcompress *comp,
\r
144 register struct ip *ip = (struct ip *)pb->payload;
\r
145 register struct cstate *cs = comp->last_cs->cs_next;
\r
146 register u_short hlen = getip_hl(*ip);
\r
147 register struct tcphdr *oth;
\r
148 register struct tcphdr *th;
\r
149 register u_short deltaS, deltaA;
\r
150 register u_long deltaL;
\r
151 register u_int changes = 0;
\r
152 u_char new_seq[16];
\r
153 register u_char *cp = new_seq;
\r
156 * Check that the packet is IP proto TCP.
\r
158 if (ip->ip_p != IPPROTO_TCP)
\r
162 * Bail if this is an IP fragment or if the TCP packet isn't
\r
163 * `compressible' (i.e., ACK isn't set or some other control bit is
\r
166 if ((ip->ip_off & htons(0x3fff)) || pb->tot_len < 40)
\r
168 th = (struct tcphdr *)&((long *)ip)[hlen];
\r
169 if ((th->th_flags & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK)
\r
173 * Packet is compressible -- we're going to send either a
\r
174 * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need
\r
175 * to locate (or create) the connection state. Special case the
\r
176 * most recently used connection since it's most likely to be used
\r
177 * again & we don't have to do any reordering if it's used.
\r
180 if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr
\r
181 || ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr
\r
182 || *(long *)th != ((long *)&cs->cs_ip)[getip_hl(cs->cs_ip)]) {
\r
184 * Wasn't the first -- search for it.
\r
186 * States are kept in a circularly linked list with
\r
187 * last_cs pointing to the end of the list. The
\r
188 * list is kept in lru order by moving a state to the
\r
189 * head of the list whenever it is referenced. Since
\r
190 * the list is short and, empirically, the connection
\r
191 * we want is almost always near the front, we locate
\r
192 * states via linear search. If we don't find a state
\r
193 * for the datagram, the oldest state is (re-)used.
\r
195 register struct cstate *lcs;
\r
196 register struct cstate *lastcs = comp->last_cs;
\r
199 lcs = cs; cs = cs->cs_next;
\r
200 INCR(vjs_searches);
\r
201 if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
\r
202 && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr
\r
203 && *(long *)th == ((long *)&cs->cs_ip)[getip_hl(cs->cs_ip)])
\r
205 } while (cs != lastcs);
\r
208 * Didn't find it -- re-use oldest cstate. Send an
\r
209 * uncompressed packet that tells the other side what
\r
210 * connection number we're using for this conversation.
\r
211 * Note that since the state list is circular, the oldest
\r
212 * state points to the newest and we only need to set
\r
213 * last_cs to update the lru linkage.
\r
216 comp->last_cs = lcs;
\r
217 hlen += getth_off(*th);
\r
219 /* Check that the IP/TCP headers are contained in the first buffer. */
\r
220 if (hlen > pb->len)
\r
226 * Found it -- move to the front on the connection list.
\r
229 comp->last_cs = lcs;
\r
231 lcs->cs_next = cs->cs_next;
\r
232 cs->cs_next = lastcs->cs_next;
\r
233 lastcs->cs_next = cs;
\r
237 oth = (struct tcphdr *)&((long *)&cs->cs_ip)[hlen];
\r
239 hlen += getth_off(*th);
\r
241 /* Check that the IP/TCP headers are contained in the first buffer. */
\r
242 if (hlen > pb->len) {
\r
243 PPPDEBUG((LOG_INFO, "vj_compress_tcp: header len %d spans buffers\n",
\r
249 * Make sure that only what we expect to change changed. The first
\r
250 * line of the `if' checks the IP protocol version, header length &
\r
251 * type of service. The 2nd line checks the "Don't fragment" bit.
\r
252 * The 3rd line checks the time-to-live and protocol (the protocol
\r
253 * check is unnecessary but costless). The 4th line checks the TCP
\r
254 * header length. The 5th line checks IP options, if any. The 6th
\r
255 * line checks TCP options, if any. If any of these things are
\r
256 * different between the previous & current datagram, we send the
\r
257 * current datagram `uncompressed'.
\r
259 if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0]
\r
260 || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3]
\r
261 || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4]
\r
262 || getth_off(*th) != getth_off(*oth)
\r
263 || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2))
\r
264 || (getth_off(*th) > 5 && BCMP(th + 1, oth + 1, (getth_off(*th) - 5) << 2)))
\r
268 * Figure out which of the changing fields changed. The
\r
269 * receiver expects changes in the order: urgent, window,
\r
270 * ack, seq (the order minimizes the number of temporaries
\r
271 * needed in this section of code).
\r
273 if (th->th_flags & TCP_URG) {
\r
274 deltaS = ntohs(th->th_urp);
\r
277 } else if (th->th_urp != oth->th_urp)
\r
278 /* argh! URG not set but urp changed -- a sensible
\r
279 * implementation should never do this but RFC793
\r
280 * doesn't prohibit the change so we have to deal
\r
284 if ((deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) != 0) {
\r
289 if ((deltaL = ntohl(th->th_ack) - ntohl(oth->th_ack)) != 0) {
\r
290 if (deltaL > 0xffff)
\r
292 deltaA = (u_short)deltaL;
\r
297 if ((deltaL = ntohl(th->th_seq) - ntohl(oth->th_seq)) != 0) {
\r
298 if (deltaL > 0xffff)
\r
300 deltaS = (u_short)deltaL;
\r
309 * Nothing changed. If this packet contains data and the
\r
310 * last one didn't, this is probably a data packet following
\r
311 * an ack (normal on an interactive connection) and we send
\r
312 * it compressed. Otherwise it's probably a retransmit,
\r
313 * retransmitted ack or window probe. Send it uncompressed
\r
314 * in case the other side missed the compressed version.
\r
316 if (ip->ip_len != cs->cs_ip.ip_len &&
\r
317 ntohs(cs->cs_ip.ip_len) == hlen)
\r
320 /* (fall through) */
\r
325 * actual changes match one of our special case encodings --
\r
326 * send packet uncompressed.
\r
331 if (deltaS == deltaA && deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
\r
332 /* special case for echoed terminal traffic */
\r
333 changes = SPECIAL_I;
\r
339 if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
\r
340 /* special case for data xfer */
\r
341 changes = SPECIAL_D;
\r
347 deltaS = (u_short)(ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id));
\r
352 if (th->th_flags & TCP_PSH)
\r
353 changes |= TCP_PUSH_BIT;
\r
355 * Grab the cksum before we overwrite it below. Then update our
\r
356 * state with this packet's header.
\r
358 deltaA = ntohs(th->th_sum);
\r
359 BCOPY(ip, &cs->cs_ip, hlen);
\r
362 * We want to use the original packet as our compressed packet.
\r
363 * (cp - new_seq) is the number of bytes we need for compressed
\r
364 * sequence numbers. In addition we need one byte for the change
\r
365 * mask, one for the connection id and two for the tcp checksum.
\r
366 * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how
\r
367 * many bytes of the original packet to toss so subtract the two to
\r
368 * get the new packet size.
\r
370 deltaS = (u_short)(cp - new_seq);
\r
371 if (!comp->compressSlot || comp->last_xmit != cs->cs_id) {
\r
372 comp->last_xmit = cs->cs_id;
\r
373 hlen -= deltaS + 4;
\r
374 pbuf_header(pb, -hlen);
\r
375 cp = (u_char *)pb->payload;
\r
376 *cp++ = changes | NEW_C;
\r
379 hlen -= deltaS + 3;
\r
380 pbuf_header(pb, -hlen);
\r
381 cp = (u_char *)pb->payload;
\r
384 *cp++ = deltaA >> 8;
\r
386 BCOPY(new_seq, cp, deltaS);
\r
387 INCR(vjs_compressed);
\r
388 return (TYPE_COMPRESSED_TCP);
\r
391 * Update connection state cs & send uncompressed packet (that is,
\r
392 * a regular ip/tcp packet but with the 'conversation id' we hope
\r
393 * to use on future compressed packets in the protocol field).
\r
396 BCOPY(ip, &cs->cs_ip, hlen);
\r
397 ip->ip_p = cs->cs_id;
\r
398 comp->last_xmit = cs->cs_id;
\r
399 return (TYPE_UNCOMPRESSED_TCP);
\r
403 * Called when we may have missed a packet.
\r
405 void vj_uncompress_err(struct vjcompress *comp)
\r
407 comp->flags |= VJF_TOSS;
\r
412 * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP.
\r
413 * Return 0 on success, -1 on failure.
\r
415 int vj_uncompress_uncomp(
\r
417 struct vjcompress *comp
\r
420 register u_int hlen;
\r
421 register struct cstate *cs;
\r
422 register struct ip *ip;
\r
424 ip = (struct ip *)nb->payload;
\r
425 hlen = getip_hl(*ip) << 2;
\r
426 if (ip->ip_p >= MAX_SLOTS
\r
427 || hlen + sizeof(struct tcphdr) > nb->len
\r
428 || (hlen += getth_off(*((struct tcphdr *)&((char *)ip)[hlen])) << 2)
\r
430 || hlen > MAX_HDR) {
\r
431 PPPDEBUG((LOG_INFO, "vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n",
\r
432 ip->ip_p, hlen, nb->len));
\r
433 comp->flags |= VJF_TOSS;
\r
437 cs = &comp->rstate[comp->last_recv = ip->ip_p];
\r
438 comp->flags &=~ VJF_TOSS;
\r
439 ip->ip_p = IPPROTO_TCP;
\r
440 BCOPY(ip, &cs->cs_ip, hlen);
\r
441 cs->cs_hlen = hlen;
\r
442 INCR(vjs_uncompressedin);
\r
447 * Uncompress a packet of type TYPE_COMPRESSED_TCP.
\r
448 * The packet is composed of a buffer chain and the first buffer
\r
449 * must contain an accurate chain length.
\r
450 * The first buffer must include the entire compressed TCP/IP header.
\r
451 * This procedure replaces the compressed header with the uncompressed
\r
452 * header and returns the length of the VJ header.
\r
454 int vj_uncompress_tcp(
\r
456 struct vjcompress *comp
\r
463 struct pbuf *n0 = *nb;
\r
465 u_int vjlen, hlen, changes;
\r
467 INCR(vjs_compressedin);
\r
468 cp = (u_char *)n0->payload;
\r
470 if (changes & NEW_C) {
\r
472 * Make sure the state index is in range, then grab the state.
\r
473 * If we have a good state index, clear the 'discard' flag.
\r
475 if (*cp >= MAX_SLOTS) {
\r
476 PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: bad cid=%d\n", *cp));
\r
480 comp->flags &=~ VJF_TOSS;
\r
481 comp->last_recv = *cp++;
\r
484 * this packet has an implicit state index. If we've
\r
485 * had a line error since the last time we got an
\r
486 * explicit state index, we have to toss the packet.
\r
488 if (comp->flags & VJF_TOSS) {
\r
489 PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: tossing\n"));
\r
494 cs = &comp->rstate[comp->last_recv];
\r
495 hlen = getip_hl(cs->cs_ip) << 2;
\r
496 th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen];
\r
497 th->th_sum = htons((*cp << 8) | cp[1]);
\r
499 if (changes & TCP_PUSH_BIT)
\r
500 th->th_flags |= TCP_PSH;
\r
502 th->th_flags &=~ TCP_PSH;
\r
504 switch (changes & SPECIALS_MASK) {
\r
507 register u32_t i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
\r
508 /* some compilers can't nest inline assembler.. */
\r
509 tmp = ntohl(th->th_ack) + i;
\r
510 th->th_ack = htonl(tmp);
\r
511 tmp = ntohl(th->th_seq) + i;
\r
512 th->th_seq = htonl(tmp);
\r
517 /* some compilers can't nest inline assembler.. */
\r
518 tmp = ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
\r
519 th->th_seq = htonl(tmp);
\r
523 if (changes & NEW_U) {
\r
524 th->th_flags |= TCP_URG;
\r
525 DECODEU(th->th_urp);
\r
527 th->th_flags &=~ TCP_URG;
\r
528 if (changes & NEW_W)
\r
529 DECODES(th->th_win);
\r
530 if (changes & NEW_A)
\r
531 DECODEL(th->th_ack);
\r
532 if (changes & NEW_S)
\r
533 DECODEL(th->th_seq);
\r
536 if (changes & NEW_I) {
\r
537 DECODES(cs->cs_ip.ip_id);
\r
539 cs->cs_ip.ip_id = ntohs(cs->cs_ip.ip_id) + 1;
\r
540 cs->cs_ip.ip_id = htons(cs->cs_ip.ip_id);
\r
544 * At this point, cp points to the first byte of data in the
\r
545 * packet. Fill in the IP total length and update the IP
\r
548 vjlen = (u_short)(cp - (u_char*)n0->payload);
\r
549 if (n0->len < vjlen) {
\r
551 * We must have dropped some characters (crc should detect
\r
552 * this but the old slip framing won't)
\r
554 PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: head buffer %d too short %d\n",
\r
559 #if BYTE_ORDER == LITTLE_ENDIAN
\r
560 tmp = n0->tot_len - vjlen + cs->cs_hlen;
\r
561 cs->cs_ip.ip_len = htons(tmp);
\r
563 cs->cs_ip.ip_len = htons(n0->tot_len - vjlen + cs->cs_hlen);
\r
566 /* recompute the ip header checksum */
\r
567 bp = (u_short *) &cs->cs_ip;
\r
568 cs->cs_ip.ip_sum = 0;
\r
569 for (tmp = 0; hlen > 0; hlen -= 2)
\r
571 tmp = (tmp & 0xffff) + (tmp >> 16);
\r
572 tmp = (tmp & 0xffff) + (tmp >> 16);
\r
573 cs->cs_ip.ip_sum = (u_short)(~tmp);
\r
575 /* Remove the compressed header and prepend the uncompressed header. */
\r
576 pbuf_header(n0, -vjlen);
\r
578 if(MEM_ALIGN(n0->payload) != n0->payload) {
\r
579 struct pbuf *np, *q;
\r
582 np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL);
\r
584 PPPDEBUG((LOG_WARNING, "vj_uncompress_tcp: realign failed\n"));
\r
589 pbuf_header(np, -cs->cs_hlen);
\r
591 bufptr = n0->payload;
\r
592 for(q = np; q != NULL; q = q->next) {
\r
593 memcpy(q->payload, bufptr, q->len);
\r
598 pbuf_chain(np, n0->next);
\r
605 if(pbuf_header(n0, cs->cs_hlen)) {
\r
608 LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE);
\r
609 np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL);
\r
611 PPPDEBUG((LOG_WARNING, "vj_uncompress_tcp: prepend failed\n"));
\r
618 LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen);
\r
619 memcpy(n0->payload, &cs->cs_ip, cs->cs_hlen);
\r
626 comp->flags |= VJF_TOSS;
\r