compress.c 11 KB

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