2 * Routines to compress and uncompess tcp packets (for transmission
3 * over low speed serial lines.
5 * Copyright (c) 1989 Regents of the University of California.
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the University of California, Berkeley. The name of the
14 * University may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
21 * Initial distribution.
23 * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au,
24 * so that the entire packet being decompressed doesn't have
25 * to be in contiguous memory (just the compressed header).
27 * Modified March 1998 by Guy Lancaster, glanca@gesn.com,
28 * for a 16 bit processor.
33 #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
45 #define INCR(counter) ++comp->stats.counter
51 vj_compress_init(struct vjcompress
*comp
)
54 register struct cstate
*tstate
= comp
->tstate
;
57 memset((char *)comp
, 0, sizeof(*comp
));
59 comp
->maxSlotIndex
= MAX_SLOTS
- 1;
60 comp
->compressSlot
= 0; /* Disable slot ID compression by default. */
61 for (i
= MAX_SLOTS
- 1; i
> 0; --i
) {
63 tstate
[i
].cs_next
= &tstate
[i
- 1];
65 tstate
[0].cs_next
= &tstate
[MAX_SLOTS
- 1];
67 comp
->last_cs
= &tstate
[0];
68 comp
->last_recv
= 255;
69 comp
->last_xmit
= 255;
70 comp
->flags
= VJF_TOSS
;
74 /* ENCODE encodes a number that is known to be non-zero. ENCODEZ
75 * checks for zero (since zero has to be encoded in the long, 3 byte
79 if ((u_short)(n) >= 256) { \
81 cp[1] = (u_char)(n); \
82 cp[0] = (u_char)((n) >> 8); \
85 *cp++ = (u_char)(n); \
88 #define ENCODEZ(n) { \
89 if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
91 cp[1] = (u_char)(n); \
92 cp[0] = (u_char)((n) >> 8); \
95 *cp++ = (u_char)(n); \
99 #define DECODEL(f) { \
101 u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \
105 u32_t tmp = ntohl(f) + (u32_t)*cp++; \
110 #define DECODES(f) { \
112 u_short tmp = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \
116 u_short tmp = ntohs(f) + (u_short)*cp++; \
121 #define DECODEU(f) { \
123 (f) = htons(((u_short)cp[1] << 8) | cp[2]); \
126 (f) = htons((u_short)*cp++); \
131 * vj_compress_tcp - Attempt to do Van Jacobson header compression on a
132 * packet. This assumes that nb and comp are not null and that the first
133 * buffer of the chain contains a valid IP header.
134 * Return the VJ type code indicating whether or not the packet was
138 vj_compress_tcp(struct vjcompress
*comp
, struct pbuf
*pb
)
140 register struct ip_hdr
*ip
= (struct ip_hdr
*)pb
->payload
;
141 register struct cstate
*cs
= comp
->last_cs
->cs_next
;
142 register u_short hlen
= IPH_HL(ip
);
143 register struct tcp_hdr
*oth
;
144 register struct tcp_hdr
*th
;
145 register u_short deltaS
, deltaA
;
146 register u_long deltaL
;
147 register u_int changes
= 0;
149 register u_char
*cp
= new_seq
;
152 * Check that the packet is IP proto TCP.
154 if (IPH_PROTO(ip
) != IP_PROTO_TCP
) {
159 * Bail if this is an IP fragment or if the TCP packet isn't
160 * `compressible' (i.e., ACK isn't set or some other control bit is
163 if ((IPH_OFFSET(ip
) & PP_HTONS(0x3fff)) || pb
->tot_len
< 40) {
166 th
= (struct tcp_hdr
*)&((long *)ip
)[hlen
];
167 if ((TCPH_FLAGS(th
) & (TCP_SYN
|TCP_FIN
|TCP_RST
|TCP_ACK
)) != TCP_ACK
) {
171 * Packet is compressible -- we're going to send either a
172 * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need
173 * to locate (or create) the connection state. Special case the
174 * most recently used connection since it's most likely to be used
175 * again & we don't have to do any reordering if it's used.
178 if (!ip_addr_cmp(&ip
->src
, &cs
->cs_ip
.src
)
179 || !ip_addr_cmp(&ip
->dest
, &cs
->cs_ip
.dest
)
180 || *(long *)th
!= ((long *)&cs
->cs_ip
)[IPH_HL(&cs
->cs_ip
)]) {
182 * Wasn't the first -- search for it.
184 * States are kept in a circularly linked list with
185 * last_cs pointing to the end of the list. The
186 * list is kept in lru order by moving a state to the
187 * head of the list whenever it is referenced. Since
188 * the list is short and, empirically, the connection
189 * we want is almost always near the front, we locate
190 * states via linear search. If we don't find a state
191 * for the datagram, the oldest state is (re-)used.
193 register struct cstate
*lcs
;
194 register struct cstate
*lastcs
= comp
->last_cs
;
197 lcs
= cs
; cs
= cs
->cs_next
;
199 if (ip_addr_cmp(&ip
->src
, &cs
->cs_ip
.src
)
200 && ip_addr_cmp(&ip
->dest
, &cs
->cs_ip
.dest
)
201 && *(long *)th
== ((long *)&cs
->cs_ip
)[IPH_HL(&cs
->cs_ip
)]) {
204 } while (cs
!= lastcs
);
207 * Didn't find it -- re-use oldest cstate. Send an
208 * uncompressed packet that tells the other side what
209 * connection number we're using for this conversation.
210 * Note that since the state list is circular, the oldest
211 * state points to the newest and we only need to set
212 * last_cs to update the lru linkage.
216 hlen
+= TCPH_OFFSET(th
);
218 /* Check that the IP/TCP headers are contained in the first buffer. */
219 if (hlen
> pb
->len
) {
226 * Found it -- move to the front on the connection list.
231 lcs
->cs_next
= cs
->cs_next
;
232 cs
->cs_next
= lastcs
->cs_next
;
233 lastcs
->cs_next
= cs
;
237 oth
= (struct tcp_hdr
*)&((long *)&cs
->cs_ip
)[hlen
];
239 hlen
+= TCPH_OFFSET(th
);
241 /* Check that the IP/TCP headers are contained in the first buffer. */
242 if (hlen
> pb
->len
) {
243 PPPDEBUG(LOG_INFO
, ("vj_compress_tcp: header len %d spans buffers\n", hlen
));
248 * Make sure that only what we expect to change changed. The first
249 * line of the `if' checks the IP protocol version, header length &
250 * type of service. The 2nd line checks the "Don't fragment" bit.
251 * The 3rd line checks the time-to-live and protocol (the protocol
252 * check is unnecessary but costless). The 4th line checks the TCP
253 * header length. The 5th line checks IP options, if any. The 6th
254 * line checks TCP options, if any. If any of these things are
255 * different between the previous & current datagram, we send the
256 * current datagram `uncompressed'.
258 if (((u_short
*)ip
)[0] != ((u_short
*)&cs
->cs_ip
)[0]
259 || ((u_short
*)ip
)[3] != ((u_short
*)&cs
->cs_ip
)[3]
260 || ((u_short
*)ip
)[4] != ((u_short
*)&cs
->cs_ip
)[4]
261 || TCPH_OFFSET(th
) != TCPH_OFFSET(oth
)
262 || (deltaS
> 5 && BCMP(ip
+ 1, &cs
->cs_ip
+ 1, (deltaS
- 5) << 2))
263 || (TCPH_OFFSET(th
) > 5 && BCMP(th
+ 1, oth
+ 1, (TCPH_OFFSET(th
) - 5) << 2))) {
268 * Figure out which of the changing fields changed. The
269 * receiver expects changes in the order: urgent, window,
270 * ack, seq (the order minimizes the number of temporaries
271 * needed in this section of code).
273 if (TCPH_FLAGS(th
) & TCP_URG
) {
274 deltaS
= ntohs(th
->urgp
);
277 } else if (th
->urgp
!= oth
->urgp
) {
278 /* argh! URG not set but urp changed -- a sensible
279 * implementation should never do this but RFC793
280 * doesn't prohibit the change so we have to deal
285 if ((deltaS
= (u_short
)(ntohs(th
->wnd
) - ntohs(oth
->wnd
))) != 0) {
290 if ((deltaL
= ntohl(th
->ackno
) - ntohl(oth
->ackno
)) != 0) {
291 if (deltaL
> 0xffff) {
294 deltaA
= (u_short
)deltaL
;
299 if ((deltaL
= ntohl(th
->seqno
) - ntohl(oth
->seqno
)) != 0) {
300 if (deltaL
> 0xffff) {
303 deltaS
= (u_short
)deltaL
;
311 * Nothing changed. If this packet contains data and the
312 * last one didn't, this is probably a data packet following
313 * an ack (normal on an interactive connection) and we send
314 * it compressed. Otherwise it's probably a retransmit,
315 * retransmitted ack or window probe. Send it uncompressed
316 * in case the other side missed the compressed version.
318 if (IPH_LEN(ip
) != IPH_LEN(&cs
->cs_ip
) &&
319 ntohs(IPH_LEN(&cs
->cs_ip
)) == hlen
) {
328 * actual changes match one of our special case encodings --
329 * send packet uncompressed.
334 if (deltaS
== deltaA
&& deltaS
== ntohs(IPH_LEN(&cs
->cs_ip
)) - hlen
) {
335 /* special case for echoed terminal traffic */
342 if (deltaS
== ntohs(IPH_LEN(&cs
->cs_ip
)) - hlen
) {
343 /* special case for data xfer */
350 deltaS
= (u_short
)(ntohs(IPH_ID(ip
)) - ntohs(IPH_ID(&cs
->cs_ip
)));
355 if (TCPH_FLAGS(th
) & TCP_PSH
) {
356 changes
|= TCP_PUSH_BIT
;
359 * Grab the cksum before we overwrite it below. Then update our
360 * state with this packet's header.
362 deltaA
= ntohs(th
->chksum
);
363 BCOPY(ip
, &cs
->cs_ip
, hlen
);
366 * We want to use the original packet as our compressed packet.
367 * (cp - new_seq) is the number of bytes we need for compressed
368 * sequence numbers. In addition we need one byte for the change
369 * mask, one for the connection id and two for the tcp checksum.
370 * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how
371 * many bytes of the original packet to toss so subtract the two to
372 * get the new packet size.
374 deltaS
= (u_short
)(cp
- new_seq
);
375 if (!comp
->compressSlot
|| comp
->last_xmit
!= cs
->cs_id
) {
376 comp
->last_xmit
= cs
->cs_id
;
378 if(pbuf_header(pb
, -hlen
)){
379 /* Can we cope with this failing? Just assert for now */
380 LWIP_ASSERT("pbuf_header failed\n", 0);
382 cp
= (u_char
*)pb
->payload
;
383 *cp
++ = (u_char
)(changes
| NEW_C
);
387 if(pbuf_header(pb
, -hlen
)) {
388 /* Can we cope with this failing? Just assert for now */
389 LWIP_ASSERT("pbuf_header failed\n", 0);
391 cp
= (u_char
*)pb
->payload
;
392 *cp
++ = (u_char
)changes
;
394 *cp
++ = (u_char
)(deltaA
>> 8);
395 *cp
++ = (u_char
)deltaA
;
396 BCOPY(new_seq
, cp
, deltaS
);
397 INCR(vjs_compressed
);
398 return (TYPE_COMPRESSED_TCP
);
401 * Update connection state cs & send uncompressed packet (that is,
402 * a regular ip/tcp packet but with the 'conversation id' we hope
403 * to use on future compressed packets in the protocol field).
406 BCOPY(ip
, &cs
->cs_ip
, hlen
);
407 IPH_PROTO_SET(ip
, cs
->cs_id
);
408 comp
->last_xmit
= cs
->cs_id
;
409 return (TYPE_UNCOMPRESSED_TCP
);
413 * Called when we may have missed a packet.
416 vj_uncompress_err(struct vjcompress
*comp
)
418 comp
->flags
|= VJF_TOSS
;
423 * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP.
424 * Return 0 on success, -1 on failure.
427 vj_uncompress_uncomp(struct pbuf
*nb
, struct vjcompress
*comp
)
430 register struct cstate
*cs
;
431 register struct ip_hdr
*ip
;
433 ip
= (struct ip_hdr
*)nb
->payload
;
434 hlen
= IPH_HL(ip
) << 2;
435 if (IPH_PROTO(ip
) >= MAX_SLOTS
436 || hlen
+ sizeof(struct tcp_hdr
) > nb
->len
437 || (hlen
+= TCPH_OFFSET(((struct tcp_hdr
*)&((char *)ip
)[hlen
])) << 2)
440 PPPDEBUG(LOG_INFO
, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n",
441 IPH_PROTO(ip
), hlen
, nb
->len
));
442 comp
->flags
|= VJF_TOSS
;
446 cs
= &comp
->rstate
[comp
->last_recv
= IPH_PROTO(ip
)];
447 comp
->flags
&=~ VJF_TOSS
;
448 IPH_PROTO_SET(ip
, IP_PROTO_TCP
);
449 BCOPY(ip
, &cs
->cs_ip
, hlen
);
450 cs
->cs_hlen
= (u_short
)hlen
;
451 INCR(vjs_uncompressedin
);
456 * Uncompress a packet of type TYPE_COMPRESSED_TCP.
457 * The packet is composed of a buffer chain and the first buffer
458 * must contain an accurate chain length.
459 * The first buffer must include the entire compressed TCP/IP header.
460 * This procedure replaces the compressed header with the uncompressed
461 * header and returns the length of the VJ header.
464 vj_uncompress_tcp(struct pbuf
**nb
, struct vjcompress
*comp
)
470 struct pbuf
*n0
= *nb
;
472 u_int vjlen
, hlen
, changes
;
474 INCR(vjs_compressedin
);
475 cp
= (u_char
*)n0
->payload
;
477 if (changes
& NEW_C
) {
479 * Make sure the state index is in range, then grab the state.
480 * If we have a good state index, clear the 'discard' flag.
482 if (*cp
>= MAX_SLOTS
) {
483 PPPDEBUG(LOG_INFO
, ("vj_uncompress_tcp: bad cid=%d\n", *cp
));
487 comp
->flags
&=~ VJF_TOSS
;
488 comp
->last_recv
= *cp
++;
491 * this packet has an implicit state index. If we've
492 * had a line error since the last time we got an
493 * explicit state index, we have to toss the packet.
495 if (comp
->flags
& VJF_TOSS
) {
496 PPPDEBUG(LOG_INFO
, ("vj_uncompress_tcp: tossing\n"));
501 cs
= &comp
->rstate
[comp
->last_recv
];
502 hlen
= IPH_HL(&cs
->cs_ip
) << 2;
503 th
= (struct tcp_hdr
*)&((u_char
*)&cs
->cs_ip
)[hlen
];
504 th
->chksum
= htons((*cp
<< 8) | cp
[1]);
506 if (changes
& TCP_PUSH_BIT
) {
507 TCPH_SET_FLAG(th
, TCP_PSH
);
509 TCPH_UNSET_FLAG(th
, TCP_PSH
);
512 switch (changes
& SPECIALS_MASK
) {
515 register u32_t i
= ntohs(IPH_LEN(&cs
->cs_ip
)) - cs
->cs_hlen
;
516 /* some compilers can't nest inline assembler.. */
517 tmp
= ntohl(th
->ackno
) + i
;
518 th
->ackno
= htonl(tmp
);
519 tmp
= ntohl(th
->seqno
) + i
;
520 th
->seqno
= htonl(tmp
);
525 /* some compilers can't nest inline assembler.. */
526 tmp
= ntohl(th
->seqno
) + ntohs(IPH_LEN(&cs
->cs_ip
)) - cs
->cs_hlen
;
527 th
->seqno
= htonl(tmp
);
531 if (changes
& NEW_U
) {
532 TCPH_SET_FLAG(th
, TCP_URG
);
535 TCPH_UNSET_FLAG(th
, TCP_URG
);
537 if (changes
& NEW_W
) {
540 if (changes
& NEW_A
) {
543 if (changes
& NEW_S
) {
548 if (changes
& NEW_I
) {
549 DECODES(cs
->cs_ip
._id
);
551 IPH_ID_SET(&cs
->cs_ip
, ntohs(IPH_ID(&cs
->cs_ip
)) + 1);
552 IPH_ID_SET(&cs
->cs_ip
, htons(IPH_ID(&cs
->cs_ip
)));
556 * At this point, cp points to the first byte of data in the
557 * packet. Fill in the IP total length and update the IP
560 vjlen
= (u_short
)(cp
- (u_char
*)n0
->payload
);
561 if (n0
->len
< vjlen
) {
563 * We must have dropped some characters (crc should detect
564 * this but the old slip framing won't)
566 PPPDEBUG(LOG_INFO
, ("vj_uncompress_tcp: head buffer %d too short %d\n",
571 #if BYTE_ORDER == LITTLE_ENDIAN
572 tmp
= n0
->tot_len
- vjlen
+ cs
->cs_hlen
;
573 IPH_LEN_SET(&cs
->cs_ip
, htons((u_short
)tmp
));
575 IPH_LEN_SET(&cs
->cs_ip
, htons(n0
->tot_len
- vjlen
+ cs
->cs_hlen
));
578 /* recompute the ip header checksum */
579 bp
= (u_short
*) &cs
->cs_ip
;
580 IPH_CHKSUM_SET(&cs
->cs_ip
, 0);
581 for (tmp
= 0; hlen
> 0; hlen
-= 2) {
584 tmp
= (tmp
& 0xffff) + (tmp
>> 16);
585 tmp
= (tmp
& 0xffff) + (tmp
>> 16);
586 IPH_CHKSUM_SET(&cs
->cs_ip
, (u_short
)(~tmp
));
588 /* Remove the compressed header and prepend the uncompressed header. */
589 if(pbuf_header(n0
, -((s16_t
)(vjlen
)))) {
590 /* Can we cope with this failing? Just assert for now */
591 LWIP_ASSERT("pbuf_header failed\n", 0);
595 if(LWIP_MEM_ALIGN(n0
->payload
) != n0
->payload
) {
599 np
= pbuf_alloc(PBUF_RAW
, n0
->len
+ cs
->cs_hlen
, PBUF_POOL
);
601 PPPDEBUG(LOG_WARNING
, ("vj_uncompress_tcp: realign failed\n"));
605 if(pbuf_header(np
, -cs
->cs_hlen
)) {
606 /* Can we cope with this failing? Just assert for now */
607 LWIP_ASSERT("pbuf_header failed\n", 0);
611 bufptr
= n0
->payload
;
612 for(q
= np
; q
!= NULL
; q
= q
->next
) {
613 MEMCPY(q
->payload
, bufptr
, q
->len
);
618 pbuf_chain(np
, n0
->next
);
625 if(pbuf_header(n0
, cs
->cs_hlen
)) {
628 LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs
->cs_hlen
<= PBUF_POOL_BUFSIZE
);
629 np
= pbuf_alloc(PBUF_RAW
, cs
->cs_hlen
, PBUF_POOL
);
631 PPPDEBUG(LOG_WARNING
, ("vj_uncompress_tcp: prepend failed\n"));
637 LWIP_ASSERT("n0->len >= cs->cs_hlen", n0
->len
>= cs
->cs_hlen
);
638 MEMCPY(n0
->payload
, &cs
->cs_ip
, cs
->cs_hlen
);
645 comp
->flags
|= VJF_TOSS
;
650 #endif /* VJ_SUPPORT */
652 #endif /* PPP_SUPPORT */