Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <ip.h>
4
#include <auth.h>
5
#include "ppp.h"
6
 
7
typedef struct Iphdr Iphdr;
8
struct Iphdr
9
{
10
	uchar	vihl;		/* Version and header length */
11
	uchar	tos;		/* Type of service */
12
	uchar	length[2];	/* packet length */
13
	uchar	id[2];		/* Identification */
14
	uchar	frag[2];	/* Fragment information */
15
	uchar	ttl;		/* Time to live */
16
	uchar	proto;		/* Protocol */
17
	uchar	cksum[2];	/* Header checksum */
18
	ulong	src;		/* Ip source (uchar ordering unimportant) */
19
	ulong	dst;		/* Ip destination (uchar ordering unimportant) */
20
};
21
 
22
typedef struct Tcphdr Tcphdr;
23
struct Tcphdr
24
{
25
	ulong	ports;		/* defined as a ulong to make comparisons easier */
26
	uchar	seq[4];
27
	uchar	ack[4];
28
	uchar	flag[2];
29
	uchar	win[2];
30
	uchar	cksum[2];
31
	uchar	urg[2];
32
};
33
 
34
typedef struct Ilhdr Ilhdr;
35
struct Ilhdr
36
{
37
	uchar	sum[2];	/* Checksum including header */
38
	uchar	len[2];	/* Packet length */
39
	uchar	type;		/* Packet type */
40
	uchar	spec;		/* Special */
41
	uchar	src[2];	/* Src port */
42
	uchar	dst[2];	/* Dst port */
43
	uchar	id[4];	/* Sequence id */
44
	uchar	ack[4];	/* Acked sequence */
45
};
46
 
47
enum
48
{
49
	URG		= 0x20,		/* Data marked urgent */
50
	ACK		= 0x10,		/* Aknowledge is valid */
51
	PSH		= 0x08,		/* Whole data pipe is pushed */
52
	RST		= 0x04,		/* Reset connection */
53
	SYN		= 0x02,		/* Pkt. is synchronise */
54
	FIN		= 0x01,		/* Start close down */
55
 
56
	IP_DF		= 0x4000,	/* Don't fragment */
57
 
58
	IP_TCPPROTO	= 6,
59
	IP_ILPROTO	= 40,
60
	IL_IPHDR	= 20,
61
};
62
 
63
typedef struct Hdr Hdr;
64
struct Hdr
65
{
66
	uchar	buf[128];
67
	Iphdr	*ip;
68
	Tcphdr	*tcp;
69
	int	len;
70
};
71
 
72
typedef struct Tcpc Tcpc;
73
struct Tcpc
74
{
75
	uchar	lastrecv;
76
	uchar	lastxmit;
77
	uchar	basexmit;
78
	uchar	err;
79
	uchar	compressid;
80
	Hdr	t[MAX_STATES];
81
	Hdr	r[MAX_STATES];
82
};
83
 
84
enum
85
{	/* flag bits for what changed in a packet */
86
	NEW_U=(1<<0),	/* tcp only */
87
	NEW_W=(1<<1),	/* tcp only */
88
	NEW_A=(1<<2),	/* il tcp */
89
	NEW_S=(1<<3),	/* tcp only */
90
	NEW_P=(1<<4),	/* tcp only */
91
	NEW_I=(1<<5),	/* il tcp */
92
	NEW_C=(1<<6),	/* il tcp */
93
	NEW_T=(1<<7),	/* il only */
94
	TCP_PUSH_BIT	= 0x10,
95
};
96
 
97
/* reserved, special-case values of above for tcp */
98
#define SPECIAL_I (NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
99
#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
100
#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
101
 
102
int
103
encode(void *p, ulong n)
104
{
105
	uchar	*cp;
106
 
107
	cp = p;
108
	if(n >= 256 || n == 0) {
109
		*cp++ = 0;
110
		cp[0] = n >> 8;
111
		cp[1] = n;
112
		return 3;
113
	}
114
	*cp = n;
115
	return 1;
116
}
117
 
118
#define DECODEL(f) { \
119
	if (*cp == 0) {\
120
		hnputl(f, nhgetl(f) + ((cp[1] << 8) | cp[2])); \
121
		cp += 3; \
122
	} else { \
123
		hnputl(f, nhgetl(f) + (ulong)*cp++); \
124
	} \
125
}
126
#define DECODES(f) { \
127
	if (*cp == 0) {\
128
		hnputs(f, nhgets(f) + ((cp[1] << 8) | cp[2])); \
129
		cp += 3; \
130
	} else { \
131
		hnputs(f, nhgets(f) + (ulong)*cp++); \
132
	} \
133
}
134
 
135
Block*
136
tcpcompress(Tcpc *comp, Block *b, int *protop)
137
{
138
	Iphdr	*ip;		/* current packet */
139
	Tcphdr	*tcp;		/* current pkt */
140
	ulong 	iplen, tcplen, hlen;	/* header length in uchars */
141
	ulong 	deltaS, deltaA;	/* general purpose temporaries */
142
	ulong 	changes;	/* change mask */
143
	uchar 	new_seq[16];	/* changes from last to current */
144
	uchar 	*cp;
145
	Hdr	*h;		/* last packet */
146
	int 	i, j;
147
 
148
	/*
149
	 * Bail if this is not a compressible TCP/IP packet
150
	 */
151
	ip = (Iphdr*)b->rptr;
152
	iplen = (ip->vihl & 0xf) << 2;
153
	tcp = (Tcphdr*)(b->rptr + iplen);
154
	tcplen = (tcp->flag[0] & 0xf0) >> 2;
155
	hlen = iplen + tcplen;
156
	if((tcp->flag[1] & (SYN|FIN|RST|ACK)) != ACK){
157
		*protop = Pip;
158
		return b;		/* connection control */
159
	}
160
 
161
	/*
162
	 * Packet is compressible, look for a connection
163
	 */
164
	changes = 0;
165
	cp = new_seq;
166
	j = comp->lastxmit;
167
	h = &comp->t[j];
168
	if(ip->src != h->ip->src || ip->dst != h->ip->dst
169
	|| tcp->ports != h->tcp->ports) {
170
		for(i = 0; i < MAX_STATES; ++i) {
171
			j = (comp->basexmit + i) % MAX_STATES;
172
			h = &comp->t[j];
173
			if(ip->src == h->ip->src && ip->dst == h->ip->dst
174
			&& tcp->ports == h->tcp->ports)
175
				goto found;
176
		}
177
 
178
		/* no connection, reuse the oldest */
179
		if(i == MAX_STATES) {
180
			j = comp->basexmit;
181
			j = (j + MAX_STATES - 1) % MAX_STATES;
182
			comp->basexmit = j;
183
			h = &comp->t[j];
184
			goto rescue;
185
		}
186
	}
187
found:
188
 
189
	/*
190
	 * Make sure that only what we expect to change changed. 
191
	 */
192
	if(ip->vihl  != h->ip->vihl || ip->tos   != h->ip->tos ||
193
	   ip->ttl   != h->ip->ttl  || ip->proto != h->ip->proto)
194
		goto rescue;	/* headers changed */
195
	if(iplen != sizeof(Iphdr) && memcmp(ip+1, h->ip+1, iplen - sizeof(Iphdr)))
196
		goto rescue;	/* ip options changed */
197
	if(tcplen != sizeof(Tcphdr) && memcmp(tcp+1, h->tcp+1, tcplen - sizeof(Tcphdr)))
198
		goto rescue;	/* tcp options changed */
199
 
200
	if(tcp->flag[1] & URG) {
201
		cp += encode(cp, nhgets(tcp->urg));
202
		changes |= NEW_U;
203
	} else if(memcmp(tcp->urg, h->tcp->urg, sizeof(tcp->urg)) != 0)
204
		goto rescue;
205
	if(deltaS = nhgets(tcp->win) - nhgets(h->tcp->win)) {
206
		cp += encode(cp, deltaS);
207
		changes |= NEW_W;
208
	}
209
	if(deltaA = nhgetl(tcp->ack) - nhgetl(h->tcp->ack)) {
210
		if(deltaA > 0xffff)
211
			goto rescue;
212
		cp += encode(cp, deltaA);
213
		changes |= NEW_A;
214
	}
215
	if(deltaS = nhgetl(tcp->seq) - nhgetl(h->tcp->seq)) {
216
		if (deltaS > 0xffff)
217
			goto rescue;
218
		cp += encode(cp, deltaS);
219
		changes |= NEW_S;
220
	}
221
 
222
	/*
223
	 * Look for the special-case encodings.
224
	 */
225
	switch(changes) {
226
	case 0:
227
		/*
228
		 * Nothing changed. If this packet contains data and the last
229
		 * one didn't, this is probably a data packet following an
230
		 * ack (normal on an interactive connection) and we send it
231
		 * compressed. Otherwise it's probably a retransmit,
232
		 * retransmitted ack or window probe.  Send it uncompressed
233
		 * in case the other side missed the compressed version.
234
		 */
235
		if(nhgets(ip->length) == nhgets(h->ip->length) ||
236
		   nhgets(h->ip->length) != hlen)
237
			goto rescue;
238
		break;
239
	case SPECIAL_I:
240
	case SPECIAL_D:
241
		/*
242
		 * Actual changes match one of our special case encodings --
243
		 * send packet uncompressed.
244
		 */
245
		goto rescue;
246
	case NEW_S | NEW_A:
247
		if (deltaS == deltaA &&
248
			deltaS == nhgets(h->ip->length) - hlen) {
249
			/* special case for echoed terminal traffic */
250
			changes = SPECIAL_I;
251
			cp = new_seq;
252
		}
253
		break;
254
	case NEW_S:
255
		if (deltaS == nhgets(h->ip->length) - hlen) {
256
			/* special case for data xfer */
257
			changes = SPECIAL_D;
258
			cp = new_seq;
259
		}
260
		break;
261
	}
262
	deltaS = nhgets(ip->id) - nhgets(h->ip->id);
263
	if(deltaS != 1) {
264
		cp += encode(cp, deltaS);
265
		changes |= NEW_I;
266
	}
267
	if (tcp->flag[1] & PSH)
268
		changes |= TCP_PUSH_BIT;
269
	/*
270
	 * Grab the cksum before we overwrite it below. Then update our
271
	 * state with this packet's header.
272
	 */
273
	deltaA = nhgets(tcp->cksum);
274
	memmove(h->buf, b->rptr, hlen);
275
	h->len = hlen;
276
	h->tcp = (Tcphdr*)(h->buf + iplen);
277
 
278
	/*
279
	 * We want to use the original packet as our compressed packet. (cp -
280
	 * new_seq) is the number of uchars we need for compressed sequence
281
	 * numbers. In addition we need one uchar for the change mask, one
282
	 * for the connection id and two for the tcp checksum. So, (cp -
283
	 * new_seq) + 4 uchars of header are needed. hlen is how many uchars
284
	 * of the original packet to toss so subtract the two to get the new
285
	 * packet size. The temporaries are gross -egs.
286
	 */
287
	deltaS = cp - new_seq;
288
	cp = b->rptr;
289
	if(comp->lastxmit != j || comp->compressid == 0) {
290
		comp->lastxmit = j;
291
		hlen -= deltaS + 4;
292
		cp += hlen;
293
		*cp++ = (changes | NEW_C);
294
		*cp++ = j;
295
	} else {
296
		hlen -= deltaS + 3;
297
		cp += hlen;
298
		*cp++ = changes;
299
	}
300
	b->rptr += hlen;
301
	hnputs(cp, deltaA);
302
	cp += 2;
303
	memmove(cp, new_seq, deltaS);
304
	*protop = Pvjctcp;
305
	return b;
306
 
307
rescue:
308
	/*
309
	 * Update connection state & send uncompressed packet
310
	 */
311
	memmove(h->buf, b->rptr, hlen);
312
	h->tcp = (Tcphdr*)(h->buf + iplen);
313
	h->len = hlen;
314
	ip->proto = j;
315
	comp->lastxmit = j;
316
	*protop = Pvjutcp;
317
	return b;
318
}
319
 
320
Block*
321
tcpuncompress(Tcpc *comp, Block *b, int type)
322
{
323
	uchar	*cp, changes;
324
	int	i;
325
	int	iplen, len;
326
	Iphdr	*ip;
327
	Tcphdr	*tcp;
328
	Hdr	*h;
329
 
330
	if(type == Pvjutcp) {
331
		/*
332
		 *  Locate the saved state for this connection. If the state
333
		 *  index is legal, clear the 'discard' flag.
334
		 */
335
		ip = (Iphdr*)b->rptr;
336
		if(ip->proto >= MAX_STATES)
337
			goto rescue;
338
		iplen = (ip->vihl & 0xf) << 2;
339
		tcp = (Tcphdr*)(b->rptr + iplen);
340
		comp->lastrecv = ip->proto;
341
		len = iplen + ((tcp->flag[0] & 0xf0) >> 2);
342
		comp->err = 0;
343
		/*
344
		 * Restore the IP protocol field then save a copy of this
345
		 * packet header. The checksum is zeroed in the copy so we
346
		 * don't have to zero it each time we process a compressed
347
		 * packet.
348
		 */
349
		ip->proto = IP_TCPPROTO;
350
		h = &comp->r[comp->lastrecv];
351
		memmove(h->buf, b->rptr, len);
352
		h->tcp = (Tcphdr*)(h->buf + iplen);
353
		h->len = len;
354
		h->ip->cksum[0] = h->ip->cksum[1] = 0;
355
		return b;
356
	}
357
 
358
	cp = b->rptr;
359
	changes = *cp++;
360
	if(changes & NEW_C) {
361
		/*
362
		 * Make sure the state index is in range, then grab the
363
		 * state. If we have a good state index, clear the 'discard'
364
		 * flag.
365
		 */
366
		if(*cp >= MAX_STATES)
367
			goto rescue;
368
		comp->err = 0;
369
		comp->lastrecv = *cp++;
370
	} else {
371
		/*
372
		 * This packet has no state index. If we've had a
373
		 * line error since the last time we got an explicit state
374
		 * index, we have to toss the packet.
375
		 */
376
		if(comp->err != 0){
377
			freeb(b);
378
			return nil;
379
		}
380
	}
381
 
382
	/*
383
	 * Find the state then fill in the TCP checksum and PUSH bit.
384
	 */
385
	h = &comp->r[comp->lastrecv];
386
	ip = h->ip;
387
	tcp = h->tcp;
388
	len = h->len;
389
	memmove(tcp->cksum, cp, sizeof tcp->cksum);
390
	cp += 2;
391
	if(changes & TCP_PUSH_BIT)
392
		tcp->flag[1] |= PSH;
393
	else
394
		tcp->flag[1] &= ~PSH;
395
	/*
396
	 * Fix up the state's ack, seq, urg and win fields based on the
397
	 * changemask.
398
	 */
399
	switch (changes & SPECIALS_MASK) {
400
	case SPECIAL_I:
401
		i = nhgets(ip->length) - len;
402
		hnputl(tcp->ack, nhgetl(tcp->ack) + i);
403
		hnputl(tcp->seq, nhgetl(tcp->seq) + i);
404
		break;
405
 
406
	case SPECIAL_D:
407
		hnputl(tcp->seq, nhgetl(tcp->seq) + nhgets(ip->length) - len);
408
		break;
409
 
410
	default:
411
		if(changes & NEW_U) {
412
			tcp->flag[1] |= URG;
413
			if(*cp == 0){
414
				hnputs(tcp->urg, nhgets(cp+1));
415
				cp += 3;
416
			}else
417
				hnputs(tcp->urg, *cp++);
418
		} else
419
			tcp->flag[1] &= ~URG;
420
		if(changes & NEW_W)
421
			DECODES(tcp->win)
422
		if(changes & NEW_A)
423
			DECODEL(tcp->ack)
424
		if(changes & NEW_S)
425
			DECODEL(tcp->seq)
426
		break;
427
	}
428
 
429
	/* Update the IP ID */
430
	if(changes & NEW_I)
431
		DECODES(ip->id)
432
	else
433
		hnputs(ip->id, nhgets(ip->id) + 1);
434
 
435
	/*
436
	 *  At this point, cp points to the first uchar of data in the packet.
437
	 *  Back up cp by the TCP/IP header length to make room for the
438
	 *  reconstructed header.
439
	 *  We assume the packet we were handed has enough space to prepend
440
	 *  up to 128 uchars of header.
441
	 */
442
	b->rptr = cp;
443
	if(b->rptr - b->base < len){
444
		b = padb(b, len);
445
		b = pullup(b, blen(b));
446
	} else
447
		b->rptr -= len;
448
	hnputs(ip->length, BLEN(b));
449
	memmove(b->rptr, ip, len);
450
 
451
	/* recompute the ip header checksum */
452
	ip = (Iphdr*)b->rptr;
453
	ip->cksum[0] = ip->cksum[1] = 0;
454
	hnputs(ip->cksum, ipcsum(b->rptr));
455
 
456
	return b;
457
 
458
rescue:
459
	netlog("ppp: vj: Bad Packet!\n");
460
	comp->err = 1;
461
	freeb(b);
462
	return nil;
463
}
464
 
465
Tcpc*
466
compress_init(Tcpc *c)
467
{
468
	int i;
469
	Hdr *h;
470
 
471
	if(c == nil)
472
		c = malloc(sizeof(Tcpc));
473
 
474
	memset(c, 0, sizeof(*c));
475
	for(i = 0; i < MAX_STATES; i++){
476
		h = &c->t[i];
477
		h->ip = (Iphdr*)h->buf;
478
		h->tcp = (Tcphdr*)(h->buf + 20);
479
		h->len = 40;
480
		h = &c->r[i];
481
		h->ip = (Iphdr*)h->buf;
482
		h->tcp = (Tcphdr*)(h->buf + 20);
483
		h->len = 40;
484
	}
485
 
486
	return c;
487
}
488
 
489
Block*
490
compress(Tcpc *tcp, Block *b, int *protop)
491
{
492
	Iphdr		*ip;
493
 
494
	/*
495
	 * Bail if this is not a compressible IP packet
496
	 */
497
	ip = (Iphdr*)b->rptr;
498
	if((nhgets(ip->frag) & 0x3fff) != 0){
499
		*protop = Pip;
500
		return b;
501
	}
502
 
503
	switch(ip->proto) {
504
	case IP_TCPPROTO:
505
		return tcpcompress(tcp, b, protop);
506
	default:
507
		*protop = Pip;
508
		return b;
509
	}
510
}
511
 
512
int
513
compress_negotiate(Tcpc *tcp, uchar *data)
514
{
515
	if(data[0] != MAX_STATES - 1)
516
		return -1;
517
	tcp->compressid = data[1];
518
	return 0;
519
}
520
 
521
/* called by ppp when there was a bad frame received */
522
void
523
compress_error(Tcpc *tcp)
524
{
525
	tcp->err = 1;
526
}