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 } 527