blob: cbdf8dbd83b506bd4bf7afc2f606a0b447031781 [file] [log] [blame]
Philippb3e116c2016-08-22 10:26:35 +02001/*
2 * Routines to compress and uncompress tcp packets (for transmission
3 * over low speed serial lines).
4 *
5 * Copyright (c) 1989 Regents of the University of California.
6 * All rights reserved.
7 *
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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
21 * - Initial distribution.
22 *
23 *
24 * modified for KA9Q Internet Software Package by
25 * Katie Stevens (dkstevens@ucdavis.edu)
26 * University of California, Davis
27 * Computing Services
28 * - 01-31-90 initial adaptation (from 1.19)
29 * PPP.05 02-15-90 [ks]
30 * PPP.08 05-02-90 [ks] use PPP protocol field to signal compression
31 * PPP.15 09-90 [ks] improve mbuf handling
32 * PPP.16 11-02 [karn] substantially rewritten to use NOS facilities
33 *
34 * - Feb 1991 Bill_Simpson@um.cc.umich.edu
35 * variable number of conversation slots
36 * allow zero or one slots
37 * separate routines
38 * status display
39 * - Jul 1994 Dmitry Gorodchanin
40 * Fixes for memory leaks.
41 * - Oct 1994 Dmitry Gorodchanin
42 * Modularization.
43 * - Jan 1995 Bjorn Ekwall
44 * Use ip_fast_csum from ip.h
45 * - July 1995 Christos A. Polyzols
46 * Spotted bug in tcp option checking
47 *
48 *
49 * This module is a difficult issue. It's clearly inet code but it's also clearly
50 * driver code belonging close to PPP and SLIP
51 */
52
Philipp2c7f8372016-08-26 16:58:41 +020053#include <string.h>
54#include <stdio.h>
55#include <errno.h>
56#include <stdint.h>
57#include <arpa/inet.h>
58#include <osmocom/core/utils.h>
59#include <osmocom/core/talloc.h>
60#include <openbsc/slhc.h>
61#include <openbsc/debug.h>
Philippb3e116c2016-08-22 10:26:35 +020062
Philipp2c7f8372016-08-26 16:58:41 +020063#define ERR_PTR(x) x
64
Philippb3e116c2016-08-22 10:26:35 +020065
66static unsigned char *encode(unsigned char *cp, unsigned short n);
67static long decode(unsigned char **cpp);
68static unsigned char * put16(unsigned char *cp, unsigned short x);
69static unsigned short pull16(unsigned char **cpp);
70
Philipp2c7f8372016-08-26 16:58:41 +020071/* Replacement for kernel space function ip_fast_csum() */
72static uint16_t ip_fast_csum(uint8_t *iph, int ihl)
73{
74 int i;
75 uint16_t temp;
76 uint32_t accumulator = 0xFFFF;
77
78 for(i=0;i<ihl*2;i++)
79 {
80 temp = ((*iph) << 8)&0xFF00;
81 iph++;
82 temp |= (*iph)&0xFF;
83 iph++;
84
85 accumulator+=temp;
86 if(accumulator>0xFFFF)
87 {
88 accumulator++;
89 accumulator&=0xFFFF;
90 }
91 }
92
93 return (uint16_t)(htons(~accumulator)&0xFFFF);
94}
95
96/* Replacement for kernel space function put_unaligned() */
97static void put_unaligned(uint16_t val, void *ptr)
98{
99 memcpy(ptr,&val,sizeof(val));
100}
101
102
Philippb3e116c2016-08-22 10:26:35 +0200103/* Allocate compression data structure
104 * slots must be in range 0 to 255 (zero meaning no compression)
105 * Returns pointer to structure or ERR_PTR() on error.
106 */
107struct slcompress *
Philipp2c7f8372016-08-26 16:58:41 +0200108slhc_init(const void *ctx, int rslots, int tslots)
Philippb3e116c2016-08-22 10:26:35 +0200109{
110 register short i;
111 register struct cstate *ts;
112 struct slcompress *comp;
113
114 if (rslots < 0 || rslots > 255 || tslots < 0 || tslots > 255)
Philipp2c7f8372016-08-26 16:58:41 +0200115 return NULL;
Philippb3e116c2016-08-22 10:26:35 +0200116
Philipp2c7f8372016-08-26 16:58:41 +0200117 comp = (struct slcompress *)talloc_zero_size(ctx,sizeof(struct slcompress));
Philippb3e116c2016-08-22 10:26:35 +0200118 if (! comp)
119 goto out_fail;
120
121 if (rslots > 0) {
122 size_t rsize = rslots * sizeof(struct cstate);
Philipp2c7f8372016-08-26 16:58:41 +0200123 comp->rstate = (struct cstate *) talloc_zero_size(ctx, rsize);
Philippb3e116c2016-08-22 10:26:35 +0200124 if (! comp->rstate)
125 goto out_free;
126 comp->rslot_limit = rslots - 1;
127 }
128
129 if (tslots > 0) {
130 size_t tsize = tslots * sizeof(struct cstate);
Philipp2c7f8372016-08-26 16:58:41 +0200131 comp->tstate = (struct cstate *) talloc_zero_size(ctx, tsize);
Philippb3e116c2016-08-22 10:26:35 +0200132 if (! comp->tstate)
133 goto out_free2;
134 comp->tslot_limit = tslots - 1;
135 }
136
137 comp->xmit_oldest = 0;
138 comp->xmit_current = 255;
139 comp->recv_current = 255;
140 /*
141 * don't accept any packets with implicit index until we get
142 * one with an explicit index. Otherwise the uncompress code
143 * will try to use connection 255, which is almost certainly
144 * out of range
145 */
146 comp->flags |= SLF_TOSS;
147
148 if ( tslots > 0 ) {
149 ts = comp->tstate;
150 for(i = comp->tslot_limit; i > 0; --i){
151 ts[i].cs_this = i;
152 ts[i].next = &(ts[i - 1]);
153 }
154 ts[0].next = &(ts[comp->tslot_limit]);
155 ts[0].cs_this = 0;
156 }
157 return comp;
158
159out_free2:
Philipp2c7f8372016-08-26 16:58:41 +0200160 talloc_free(comp->rstate);
Philippb3e116c2016-08-22 10:26:35 +0200161out_free:
Philipp2c7f8372016-08-26 16:58:41 +0200162 talloc_free(comp);
Philippb3e116c2016-08-22 10:26:35 +0200163out_fail:
Philipp2c7f8372016-08-26 16:58:41 +0200164 return NULL;
Philippb3e116c2016-08-22 10:26:35 +0200165}
166
167
168/* Free a compression data structure */
169void
170slhc_free(struct slcompress *comp)
171{
Philipp2c7f8372016-08-26 16:58:41 +0200172 DEBUGP(DSLHC, "slhc_free(): Freeing compression states...\n");
173
Philippb3e116c2016-08-22 10:26:35 +0200174 if ( comp == NULLSLCOMPR )
175 return;
176
177 if ( comp->tstate != NULLSLSTATE )
Philipp2c7f8372016-08-26 16:58:41 +0200178 talloc_free(comp->tstate );
Philippb3e116c2016-08-22 10:26:35 +0200179
180 if ( comp->rstate != NULLSLSTATE )
Philipp2c7f8372016-08-26 16:58:41 +0200181 talloc_free( comp->rstate );
Philippb3e116c2016-08-22 10:26:35 +0200182
Philipp2c7f8372016-08-26 16:58:41 +0200183 talloc_free( comp );
Philippb3e116c2016-08-22 10:26:35 +0200184}
185
186
187/* Put a short in host order into a char array in network order */
188static inline unsigned char *
189put16(unsigned char *cp, unsigned short x)
190{
191 *cp++ = x >> 8;
192 *cp++ = x;
193
194 return cp;
195}
196
197
198/* Encode a number */
199static unsigned char *
200encode(unsigned char *cp, unsigned short n)
201{
202 if(n >= 256 || n == 0){
203 *cp++ = 0;
204 cp = put16(cp,n);
205 } else {
206 *cp++ = n;
207 }
Philipp2c7f8372016-08-26 16:58:41 +0200208
209 DEBUGP(DSLHC, "encode(): n=%04x\n",n);
Philippb3e116c2016-08-22 10:26:35 +0200210 return cp;
211}
212
213/* Pull a 16-bit integer in host order from buffer in network byte order */
214static unsigned short
215pull16(unsigned char **cpp)
216{
217 short rval;
218
219 rval = *(*cpp)++;
220 rval <<= 8;
221 rval |= *(*cpp)++;
222 return rval;
223}
224
225/* Decode a number */
226static long
227decode(unsigned char **cpp)
228{
229 register int x;
230
231 x = *(*cpp)++;
232 if(x == 0){
233 return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */
234 } else {
235 return x & 0xff; /* -1 if PULLCHAR returned error */
236 }
237}
238
239/*
240 * icp and isize are the original packet.
241 * ocp is a place to put a copy if necessary.
242 * cpp is initially a pointer to icp. If the copy is used,
243 * change it to ocp.
244 */
245
246int
247slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
248 unsigned char *ocp, unsigned char **cpp, int compress_cid)
249{
250 register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
251 register struct cstate *lcs = ocs;
252 register struct cstate *cs = lcs->next;
253 register unsigned long deltaS, deltaA;
254 register short changes = 0;
255 int hlen;
256 unsigned char new_seq[16];
257 register unsigned char *cp = new_seq;
258 struct iphdr *ip;
259 struct tcphdr *th, *oth;
260 __sum16 csum;
261
262
263 /*
264 * Don't play with runt packets.
265 */
266
267 if(isize<sizeof(struct iphdr))
268 return isize;
269
270 ip = (struct iphdr *) icp;
271
272 /* Bail if this packet isn't TCP, or is an IP fragment */
273 if (ip->protocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x3fff)) {
274 /* Send as regular IP */
275 if(ip->protocol != IPPROTO_TCP)
276 comp->sls_o_nontcp++;
277 else
278 comp->sls_o_tcp++;
Philipp2c7f8372016-08-26 16:58:41 +0200279 DEBUGP(DSLHC, "slhc_compress(): Not a TCP packat, will not touch...\n");
Philippb3e116c2016-08-22 10:26:35 +0200280 return isize;
281 }
282 /* Extract TCP header */
283
284 th = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4);
285 hlen = ip->ihl*4 + th->doff*4;
286
287 /* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or
288 * some other control bit is set). Also uncompressible if
289 * it's a runt.
290 */
291 if(hlen > isize || th->syn || th->fin || th->rst ||
292 ! (th->ack)){
293 /* TCP connection stuff; send as regular IP */
294 comp->sls_o_tcp++;
Philipp2c7f8372016-08-26 16:58:41 +0200295 DEBUGP(DSLHC, "slhc_compress(): Packet is part of a TCP connection, will not touch...\n");
Philippb3e116c2016-08-22 10:26:35 +0200296 return isize;
297 }
298 /*
299 * Packet is compressible -- we're going to send either a
300 * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way,
301 * we need to locate (or create) the connection state.
302 *
303 * States are kept in a circularly linked list with
304 * xmit_oldest pointing to the end of the list. The
305 * list is kept in lru order by moving a state to the
306 * head of the list whenever it is referenced. Since
307 * the list is short and, empirically, the connection
308 * we want is almost always near the front, we locate
309 * states via linear search. If we don't find a state
310 * for the datagram, the oldest state is (re-)used.
311 */
Philipp2c7f8372016-08-26 16:58:41 +0200312
313 DEBUGP(DSLHC, "slhc_compress(): Compressible packet detected!\n");
314
Philippb3e116c2016-08-22 10:26:35 +0200315 for ( ; ; ) {
316 if( ip->saddr == cs->cs_ip.saddr
317 && ip->daddr == cs->cs_ip.daddr
318 && th->source == cs->cs_tcp.source
319 && th->dest == cs->cs_tcp.dest)
320 goto found;
321
322 /* if current equal oldest, at end of list */
323 if ( cs == ocs )
324 break;
325 lcs = cs;
326 cs = cs->next;
327 comp->sls_o_searches++;
328 }
329 /*
330 * Didn't find it -- re-use oldest cstate. Send an
331 * uncompressed packet that tells the other side what
332 * connection number we're using for this conversation.
333 *
334 * Note that since the state list is circular, the oldest
335 * state points to the newest and we only need to set
336 * xmit_oldest to update the lru linkage.
337 */
Philipp2c7f8372016-08-26 16:58:41 +0200338
339 DEBUGP(DSLHC, "slhc_compress(): Header not yet seen, will memorize header for the next turn...\n");
Philippb3e116c2016-08-22 10:26:35 +0200340 comp->sls_o_misses++;
341 comp->xmit_oldest = lcs->cs_this;
342 goto uncompressed;
343
344found:
Philipp2c7f8372016-08-26 16:58:41 +0200345 DEBUGP(DSLHC, "slhc_compress(): Header already seen, trying to compress...\n");
Philippb3e116c2016-08-22 10:26:35 +0200346 /*
347 * Found it -- move to the front on the connection list.
348 */
349 if(lcs == ocs) {
350 /* found at most recently used */
351 } else if (cs == ocs) {
352 /* found at least recently used */
353 comp->xmit_oldest = lcs->cs_this;
354 } else {
355 /* more than 2 elements */
356 lcs->next = cs->next;
357 cs->next = ocs->next;
358 ocs->next = cs;
359 }
360
361 /*
362 * Make sure that only what we expect to change changed.
363 * Check the following:
364 * IP protocol version, header length & type of service.
365 * The "Don't fragment" bit.
366 * The time-to-live field.
367 * The TCP header length.
368 * IP options, if any.
369 * TCP options, if any.
370 * If any of these things are different between the previous &
371 * current datagram, we send the current datagram `uncompressed'.
372 */
373 oth = &cs->cs_tcp;
374
Philipp2c7f8372016-08-26 16:58:41 +0200375 /* Display a little more debug information about which of the
376 * header fields changed unexpectedly */
377 if(ip->version != cs->cs_ip.version)
378 DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->version != cs->cs_ip.version\n");
379 if(ip->ihl != cs->cs_ip.ihl)
380 DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ihl != cs->cs_ip.ihl\n");
381 if(ip->tos != cs->cs_ip.tos)
382 DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->tos != cs->cs_ip.tos\n");
383 if((ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000)))
384 DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))\n");
385 if(ip->ttl != cs->cs_ip.ttl)
386 DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ttl != cs->cs_ip.ttl\n");
387 if(th->doff != cs->cs_tcp.doff)
388 DEBUGP(DSLHC, "slhc_compress(): Unexpected change: th->doff != cs->cs_tcp.doff\n");
389 if(ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) {
390 DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)\n");
391 DEBUGP(DSLHC, "slhc_compress(): ip->ihl = %i\n", ip->ihl);
392 DEBUGP(DSLHC, "slhc_compress(): ip+1 = %s\n",
393 osmo_hexdump_nospc((uint8_t*)(ip+1),((ip->ihl)-5)*4));
394 DEBUGP(DSLHC, "slhc_compress(): Unexpected change: cs->cs_ipopt = %s\n",
395 osmo_hexdump_nospc((uint8_t*)(cs->cs_ipopt),((ip->ihl)-5)*4));
396 }
397 if(th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0) {
398 DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)\n");
399 DEBUGP(DSLHC, "slhc_compress(): th->doff = %i\n", th->doff);
400 DEBUGP(DSLHC, "slhc_compress(): th+1 = %s\n",
401 osmo_hexdump_nospc((uint8_t*)(th+1),((th->doff)-5)*4));
402 DEBUGP(DSLHC, "slhc_compress(): cs->cs_tcpopt = %s\n",
403 osmo_hexdump_nospc((uint8_t*)cs->cs_tcpopt,
404 ((th->doff)-5)*4));
405 }
406
407
Philippb3e116c2016-08-22 10:26:35 +0200408 if(ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl
409 || ip->tos != cs->cs_ip.tos
410 || (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))
411 || ip->ttl != cs->cs_ip.ttl
412 || th->doff != cs->cs_tcp.doff
413 || (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)
414 || (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)){
Philipp2c7f8372016-08-26 16:58:41 +0200415 DEBUGP(DSLHC, "slhc_compress(): The header contains unexpected changes, can't compress...\n");
Philippb3e116c2016-08-22 10:26:35 +0200416 goto uncompressed;
417 }
418
419 /*
420 * Figure out which of the changing fields changed. The
421 * receiver expects changes in the order: urgent, window,
422 * ack, seq (the order minimizes the number of temporaries
423 * needed in this section of code).
424 */
425 if(th->urg){
426 deltaS = ntohs(th->urg_ptr);
Philipp2c7f8372016-08-26 16:58:41 +0200427 DEBUGP(DSLHC, "slhc_compress(): flag: Urgent Pointer (U) = 1\n");
Philippb3e116c2016-08-22 10:26:35 +0200428 cp = encode(cp,deltaS);
429 changes |= NEW_U;
430 } else if(th->urg_ptr != oth->urg_ptr){
431 /* argh! URG not set but urp changed -- a sensible
432 * implementation should never do this but RFC793
433 * doesn't prohibit the change so we have to deal
434 * with it. */
Philipp2c7f8372016-08-26 16:58:41 +0200435 DEBUGP(DSLHC, "slhc_compress(): URG not set but urp changed, can't compress...\n");
Philippb3e116c2016-08-22 10:26:35 +0200436 goto uncompressed;
437 }
438 if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){
Philipp2c7f8372016-08-26 16:58:41 +0200439 DEBUGP(DSLHC, "slhc_compress(): flag: Delta Window (W) = 1\n");
Philippb3e116c2016-08-22 10:26:35 +0200440 cp = encode(cp,deltaS);
441 changes |= NEW_W;
442 }
443 if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){
Philipp2c7f8372016-08-26 16:58:41 +0200444 if(deltaA > 0x0000ffff) {
445 DEBUGP(DSLHC, "slhc_compress(): (deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L, can't compress...\n");
Philippb3e116c2016-08-22 10:26:35 +0200446 goto uncompressed;
Philipp2c7f8372016-08-26 16:58:41 +0200447 }
448 DEBUGP(DSLHC, "slhc_compress(): flag: Delta Ack (A) = 1\n");
Philippb3e116c2016-08-22 10:26:35 +0200449 cp = encode(cp,deltaA);
450 changes |= NEW_A;
451 }
452 if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){
Philipp2c7f8372016-08-26 16:58:41 +0200453 if(deltaS > 0x0000ffff) {
454 DEBUGP(DSLHC, "slhc_compress(): (deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L, can't compress...\n");
Philippb3e116c2016-08-22 10:26:35 +0200455 goto uncompressed;
Philipp2c7f8372016-08-26 16:58:41 +0200456 }
457 DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1\n");
Philippb3e116c2016-08-22 10:26:35 +0200458 cp = encode(cp,deltaS);
459 changes |= NEW_S;
460 }
461
462 switch(changes){
463 case 0: /* Nothing changed. If this packet contains data and the
464 * last one didn't, this is probably a data packet following
465 * an ack (normal on an interactive connection) and we send
466 * it compressed. Otherwise it's probably a retransmit,
467 * retransmitted ack or window probe. Send it uncompressed
468 * in case the other side missed the compressed version.
469 */
470 if(ip->tot_len != cs->cs_ip.tot_len &&
471 ntohs(cs->cs_ip.tot_len) == hlen)
472 break;
Philipp2c7f8372016-08-26 16:58:41 +0200473 DEBUGP(DSLHC, "slhc_compress(): Retransmitted packet detected, can't compress...\n");
Philippb3e116c2016-08-22 10:26:35 +0200474 goto uncompressed;
475 case SPECIAL_I:
476 case SPECIAL_D:
477 /* actual changes match one of our special case encodings --
478 * send packet uncompressed.
479 */
Philipp2c7f8372016-08-26 16:58:41 +0200480 DEBUGP(DSLHC, "slhc_compress(): Special case detected, can't compress...\n");
Philippb3e116c2016-08-22 10:26:35 +0200481 goto uncompressed;
482 case NEW_S|NEW_A:
483 if(deltaS == deltaA &&
484 deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
485 /* special case for echoed terminal traffic */
Philipp2c7f8372016-08-26 16:58:41 +0200486 DEBUGP(DSLHC, "slhc_compress(): Special case for echoed terminal traffic detected...\n");
487 DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
Philippb3e116c2016-08-22 10:26:35 +0200488 changes = SPECIAL_I;
489 cp = new_seq;
490 }
491 break;
492 case NEW_S:
493 if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
494 /* special case for data xfer */
Philipp2c7f8372016-08-26 16:58:41 +0200495 DEBUGP(DSLHC, "slhc_compress(): Special case for data xfer detected...\n");
496 DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Ack (A) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
Philippb3e116c2016-08-22 10:26:35 +0200497 changes = SPECIAL_D;
498 cp = new_seq;
499 }
500 break;
501 }
502 deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id);
503 if(deltaS != 1){
Philipp2c7f8372016-08-26 16:58:41 +0200504 DEBUGP(DSLHC, "slhc_compress(): flag: Delta IP ID (I) = 1\n");
Philippb3e116c2016-08-22 10:26:35 +0200505 cp = encode(cp,deltaS);
506 changes |= NEW_I;
507 }
Philipp2c7f8372016-08-26 16:58:41 +0200508 if(th->psh) {
509 DEBUGP(DSLHC, "slhc_compress(): flag: Push (P) = 1\n");
Philippb3e116c2016-08-22 10:26:35 +0200510 changes |= TCP_PUSH_BIT;
Philipp2c7f8372016-08-26 16:58:41 +0200511 }
Philippb3e116c2016-08-22 10:26:35 +0200512 /* Grab the cksum before we overwrite it below. Then update our
513 * state with this packet's header.
514 */
515 csum = th->check;
516 memcpy(&cs->cs_ip,ip,20);
517 memcpy(&cs->cs_tcp,th,20);
518 /* We want to use the original packet as our compressed packet.
519 * (cp - new_seq) is the number of bytes we need for compressed
520 * sequence numbers. In addition we need one byte for the change
521 * mask, one for the connection id and two for the tcp checksum.
522 * So, (cp - new_seq) + 4 bytes of header are needed.
523 */
524 deltaS = cp - new_seq;
525 if(compress_cid == 0 || comp->xmit_current != cs->cs_this){
526 cp = ocp;
527 *cpp = ocp;
Philipp2c7f8372016-08-26 16:58:41 +0200528 DEBUGP(DSLHC, "slhc_compress(): flag: Connection number (C) = 1\n");
Philippb3e116c2016-08-22 10:26:35 +0200529 *cp++ = changes | NEW_C;
530 *cp++ = cs->cs_this;
531 comp->xmit_current = cs->cs_this;
532 } else {
533 cp = ocp;
534 *cpp = ocp;
535 *cp++ = changes;
536 }
537 *(__sum16 *)cp = csum;
538 cp += 2;
539/* deltaS is now the size of the change section of the compressed header */
Philipp2c7f8372016-08-26 16:58:41 +0200540
541 DEBUGP(DSLHC, "slhc_compress(): Delta-list length (deltaS) = %li\n",deltaS);
542 DEBUGP(DSLHC, "slhc_compress(): Original header len (hlen) = %i\n",hlen);
543
Philippb3e116c2016-08-22 10:26:35 +0200544 memcpy(cp,new_seq,deltaS); /* Write list of deltas */
545 memcpy(cp+deltaS,icp+hlen,isize-hlen);
546 comp->sls_o_compressed++;
547 ocp[0] |= SL_TYPE_COMPRESSED_TCP;
548 return isize - hlen + deltaS + (cp - ocp);
549
550 /* Update connection state cs & send uncompressed packet (i.e.,
551 * a regular ip/tcp packet but with the 'conversation id' we hope
552 * to use on future compressed packets in the protocol field).
553 */
554uncompressed:
Philipp2c7f8372016-08-26 16:58:41 +0200555 DEBUGP(DSLHC, "slhc_compress(): Packet will be sent uncompressed...\n");
Philippb3e116c2016-08-22 10:26:35 +0200556 memcpy(&cs->cs_ip,ip,20);
557 memcpy(&cs->cs_tcp,th,20);
558 if (ip->ihl > 5)
559 memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4);
560 if (th->doff > 5)
561 memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * 4);
562 comp->xmit_current = cs->cs_this;
563 comp->sls_o_uncompressed++;
564 memcpy(ocp, icp, isize);
565 *cpp = ocp;
566 ocp[9] = cs->cs_this;
567 ocp[0] |= SL_TYPE_UNCOMPRESSED_TCP;
568 return isize;
569}
570
571
572int
573slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
574{
575 register int changes;
576 long x;
577 register struct tcphdr *thp;
578 register struct iphdr *ip;
579 register struct cstate *cs;
580 int len, hdrlen;
581 unsigned char *cp = icp;
582
583 /* We've got a compressed packet; read the change byte */
584 comp->sls_i_compressed++;
585 if(isize < 3){
586 comp->sls_i_error++;
587 return 0;
588 }
589 changes = *cp++;
590 if(changes & NEW_C){
591 /* Make sure the state index is in range, then grab the state.
592 * If we have a good state index, clear the 'discard' flag.
593 */
594 x = *cp++; /* Read conn index */
595 if(x < 0 || x > comp->rslot_limit)
596 goto bad;
597
598 comp->flags &=~ SLF_TOSS;
599 comp->recv_current = x;
600 } else {
601 /* this packet has an implicit state index. If we've
602 * had a line error since the last time we got an
603 * explicit state index, we have to toss the packet. */
604 if(comp->flags & SLF_TOSS){
605 comp->sls_i_tossed++;
606 return 0;
607 }
608 }
609 cs = &comp->rstate[comp->recv_current];
610 thp = &cs->cs_tcp;
611 ip = &cs->cs_ip;
612
613 thp->check = *(__sum16 *)cp;
614 cp += 2;
615
616 thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
617/*
618 * we can use the same number for the length of the saved header and
619 * the current one, because the packet wouldn't have been sent
620 * as compressed unless the options were the same as the previous one
621 */
622
623 hdrlen = ip->ihl * 4 + thp->doff * 4;
624
625 switch(changes & SPECIALS_MASK){
626 case SPECIAL_I: /* Echoed terminal traffic */
Philipp2c7f8372016-08-26 16:58:41 +0200627 DEBUGP(DSLHC, "slhc_uncompress(): Echoed terminal traffic detected\n");
628
Philippb3e116c2016-08-22 10:26:35 +0200629 {
630 register short i;
631 i = ntohs(ip->tot_len) - hdrlen;
632 thp->ack_seq = htonl( ntohl(thp->ack_seq) + i);
633 thp->seq = htonl( ntohl(thp->seq) + i);
634 }
635 break;
636
637 case SPECIAL_D: /* Unidirectional data */
Philipp2c7f8372016-08-26 16:58:41 +0200638 DEBUGP(DSLHC, "slhc_uncompress(): Unidirectional data detected\n");
Philippb3e116c2016-08-22 10:26:35 +0200639 thp->seq = htonl( ntohl(thp->seq) +
640 ntohs(ip->tot_len) - hdrlen);
641 break;
642
643 default:
Philipp2c7f8372016-08-26 16:58:41 +0200644 DEBUGP(DSLHC, "slhc_uncompress(): default packet type detected\n");
Philippb3e116c2016-08-22 10:26:35 +0200645 if(changes & NEW_U){
646 thp->urg = 1;
647 if((x = decode(&cp)) == -1) {
648 goto bad;
649 }
650 thp->urg_ptr = htons(x);
651 } else
652 thp->urg = 0;
653 if(changes & NEW_W){
654 if((x = decode(&cp)) == -1) {
655 goto bad;
656 }
657 thp->window = htons( ntohs(thp->window) + x);
658 }
659 if(changes & NEW_A){
660 if((x = decode(&cp)) == -1) {
661 goto bad;
662 }
663 thp->ack_seq = htonl( ntohl(thp->ack_seq) + x);
664 }
665 if(changes & NEW_S){
666 if((x = decode(&cp)) == -1) {
667 goto bad;
668 }
669 thp->seq = htonl( ntohl(thp->seq) + x);
670 }
671 break;
672 }
673 if(changes & NEW_I){
674 if((x = decode(&cp)) == -1) {
675 goto bad;
676 }
677 ip->id = htons (ntohs (ip->id) + x);
678 } else
679 ip->id = htons (ntohs (ip->id) + 1);
680
681 /*
682 * At this point, cp points to the first byte of data in the
683 * packet. Put the reconstructed TCP and IP headers back on the
684 * packet. Recalculate IP checksum (but not TCP checksum).
685 */
686
687 len = isize - (cp - icp);
688 if (len < 0)
689 goto bad;
690 len += hdrlen;
691 ip->tot_len = htons(len);
692 ip->check = 0;
693
Philipp2c7f8372016-08-26 16:58:41 +0200694 DEBUGP(DSLHC, "slhc_uncompress(): making space for the reconstructed header...\n");
Philippb3e116c2016-08-22 10:26:35 +0200695 memmove(icp + hdrlen, cp, len - hdrlen);
696
697 cp = icp;
698 memcpy(cp, ip, 20);
699 cp += 20;
700
701 if (ip->ihl > 5) {
702 memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4);
703 cp += (ip->ihl - 5) * 4;
704 }
705
706 put_unaligned(ip_fast_csum(icp, ip->ihl),
707 &((struct iphdr *)icp)->check);
708
709 memcpy(cp, thp, 20);
710 cp += 20;
711
712 if (thp->doff > 5) {
713 memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4);
714 cp += ((thp->doff) - 5) * 4;
715 }
716
717 return len;
718bad:
Philipp2c7f8372016-08-26 16:58:41 +0200719 DEBUGP(DSLHC, "slhc_uncompress(): bad packet detected!\n");
Philippb3e116c2016-08-22 10:26:35 +0200720 comp->sls_i_error++;
721 return slhc_toss( comp );
722}
723
724
725int
726slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
727{
728 register struct cstate *cs;
729 unsigned ihl;
730
731 unsigned char index;
732
733 if(isize < 20) {
734 /* The packet is shorter than a legal IP header */
735 comp->sls_i_runt++;
Philipp2c7f8372016-08-26 16:58:41 +0200736 DEBUGP(DSLHC, "slhc_remember(): The packet is shorter than a legal IP header ==> slhc_toss()\n");
Philippb3e116c2016-08-22 10:26:35 +0200737 return slhc_toss( comp );
738 }
739 /* Peek at the IP header's IHL field to find its length */
740 ihl = icp[0] & 0xf;
741 if(ihl < 20 / 4){
742 /* The IP header length field is too small */
743 comp->sls_i_runt++;
Philipp2c7f8372016-08-26 16:58:41 +0200744 DEBUGP(DSLHC, "slhc_remember(): The IP header length field is too small ==> slhc_toss()\n");
Philippb3e116c2016-08-22 10:26:35 +0200745 return slhc_toss( comp );
746 }
747 index = icp[9];
748 icp[9] = IPPROTO_TCP;
749
750 if (ip_fast_csum(icp, ihl)) {
751 /* Bad IP header checksum; discard */
752 comp->sls_i_badcheck++;
Philipp2c7f8372016-08-26 16:58:41 +0200753 DEBUGP(DSLHC, "slhc_remember(): Bad IP header checksum; discard ==> slhc_toss()\n");
Philippb3e116c2016-08-22 10:26:35 +0200754 return slhc_toss( comp );
755 }
756 if(index > comp->rslot_limit) {
757 comp->sls_i_error++;
Philipp2c7f8372016-08-26 16:58:41 +0200758 DEBUGP(DSLHC, "slhc_remember(): index > comp->rslot_limit ==> slhc_toss()\n");
Philippb3e116c2016-08-22 10:26:35 +0200759 return slhc_toss(comp);
760 }
761
762 /* Update local state */
763 cs = &comp->rstate[comp->recv_current = index];
764 comp->flags &=~ SLF_TOSS;
765 memcpy(&cs->cs_ip,icp,20);
766 memcpy(&cs->cs_tcp,icp + ihl*4,20);
767 if (ihl > 5)
768 memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4);
769 if (cs->cs_tcp.doff > 5)
770 memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4);
771 cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2;
772 /* Put headers back on packet
773 * Neither header checksum is recalculated
774 */
775 comp->sls_i_uncompressed++;
776 return isize;
777}
778
779int
780slhc_toss(struct slcompress *comp)
781{
Philipp2c7f8372016-08-26 16:58:41 +0200782 DEBUGP(DSLHC, "slhc_toss(): Reset compression state...\n");
Philippb3e116c2016-08-22 10:26:35 +0200783 if ( comp == NULLSLCOMPR )
784 return 0;
785
786 comp->flags |= SLF_TOSS;
787 return 0;
788}
789
Philipp2c7f8372016-08-26 16:58:41 +0200790void slhc_i_status(struct slcompress *comp)
Philippb3e116c2016-08-22 10:26:35 +0200791{
Philipp2c7f8372016-08-26 16:58:41 +0200792 if (comp != NULLSLCOMPR) {
793 DEBUGP(DSLHC, "slhc_i_status(): %d Cmp, %d Uncmp, %d Bad, %d Tossed\n",
794 comp->sls_i_compressed,
795 comp->sls_i_uncompressed,
796 comp->sls_i_error,
797 comp->sls_i_tossed);
798 }
Philippb3e116c2016-08-22 10:26:35 +0200799}
800
Philipp2c7f8372016-08-26 16:58:41 +0200801void slhc_o_status(struct slcompress *comp)
Philippb3e116c2016-08-22 10:26:35 +0200802{
Philipp2c7f8372016-08-26 16:58:41 +0200803 if (comp != NULLSLCOMPR) {
804 DEBUGP(DSLHC, "slhc_o_status(): %d Cmp, %d Uncmp, %d AsIs, %d NotTCP %d Searches, %d Misses\n",
805 comp->sls_o_compressed,
806 comp->sls_o_uncompressed,
807 comp->sls_o_tcp,
808 comp->sls_o_nontcp,
809 comp->sls_o_searches,
810 comp->sls_o_misses);
811 }
Philippb3e116c2016-08-22 10:26:35 +0200812}
813