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
encode(void * p,ulong n)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*
tcpcompress(Tcpc * comp,Block * b,int * protop)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*
tcpuncompress(Tcpc * comp,Block * b,int type)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*
compress_init(Tcpc * c)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*
compress(Tcpc * tcp,Block * b,int * protop)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
compress_negotiate(Tcpc * tcp,uchar * data)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
compress_error(Tcpc * tcp)523 compress_error(Tcpc *tcp)
524 {
525 tcp->err = 1;
526 }
527