slhc.c
上传用户:hepax88
上传日期:2007-01-03
资源大小:1101k
文件大小:16k
源码类别:

TCP/IP协议栈

开发平台:

Visual C++

  1. /*
  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 MERCHANTIBILITY 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.  */
  40. #include "global.h"
  41. #include "mbuf.h"
  42. #include "internet.h"
  43. #include "ip.h"
  44. #include "tcp.h"
  45. #include "slhc.h"
  46. static uint8 *encode(uint8 *cp,uint16 n);
  47. static long decode(struct mbuf **bpp);
  48. /* Initialize compression data structure
  49.  * slots must be in range 0 to 255 (zero meaning no compression)
  50.  */
  51. struct slcompress *
  52. slhc_init(rslots,tslots)
  53. int rslots;
  54. int tslots;
  55. {
  56. register uint16 i;
  57. register struct cstate *ts;
  58. struct slcompress *comp;
  59. comp = callocw( 1, sizeof(struct slcompress) );
  60. if ( rslots > 0  &&  rslots < 256 ) {
  61. comp->rstate = callocw( rslots, sizeof(struct cstate) );
  62. comp->rslot_limit = rslots - 1;
  63. }
  64. if ( tslots > 0  &&  tslots < 256 ) {
  65. comp->tstate = callocw( tslots, sizeof(struct cstate) );
  66. comp->tslot_limit = tslots - 1;
  67. }
  68. comp->xmit_oldest = 0;
  69. comp->xmit_current = 255;
  70. comp->recv_current = 255;
  71. if ( tslots > 0 ) {
  72. ts = comp->tstate;
  73. for(i = comp->tslot_limit; i > 0; --i){
  74. ts[i].this = i;
  75. ts[i].next = &(ts[i - 1]);
  76. }
  77. ts[0].next = &(ts[comp->tslot_limit]);
  78. ts[0].this = 0;
  79. }
  80. return comp;
  81. }
  82. /* Free a compression data structure */
  83. void
  84. slhc_free(comp)
  85. struct slcompress *comp;
  86. {
  87. if ( comp == NULL )
  88. return;
  89. if ( comp->rstate != NULL )
  90. free( comp->rstate );
  91. if ( comp->tstate != NULL )
  92. free( comp->tstate );
  93. free( comp );
  94. }
  95. /* Encode a number */
  96. static uint8 *
  97. encode(cp,n)
  98. register uint8 *cp;
  99. uint16 n;
  100. {
  101. if(n >= 256 || n == 0){
  102. *cp++ = 0;
  103. cp = put16(cp,n);
  104. } else {
  105. *cp++ = n;
  106. }
  107. return cp;
  108. }
  109. /* Decode a number */
  110. static long
  111. decode(bpp)
  112. struct mbuf **bpp;
  113. {
  114. register int x;
  115. x = PULLCHAR(bpp);
  116. if(x == 0){
  117. return pull16(bpp); /* pull16 returns -1 on error */
  118. } else {
  119. return (long)x; /* -1 if PULLCHAR returned error */
  120. }
  121. }
  122. int
  123. slhc_compress(comp, bpp, compress_cid)
  124. struct slcompress *comp;
  125. struct mbuf **bpp;
  126. int compress_cid;
  127. {
  128. register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
  129. register struct cstate *lcs = ocs;
  130. register struct cstate *cs = lcs->next;
  131. register uint16 hlen,iplen;
  132. register struct tcp *oth;
  133. register unsigned long deltaS, deltaA;
  134. register uint16 changes = 0;
  135. uint8 new_seq[16];
  136. register uint8 *cp = new_seq;
  137. struct tcp th;
  138. struct ip iph;
  139. struct mbuf *copy;
  140. /* Copy TCP/IP header, allowing for worst-case options in both
  141.  * Using dup_p seemed to result in unexplained
  142.  * memory leaks -- but only some of the time. Must find out why.
  143.  */
  144. /* dup_p(&copy,*bpp,0,IPLEN+IP_MAXOPT+TCPLEN+TCP_MAXOPT); */
  145. copy = copy_p(*bpp,IPLEN+IP_MAXOPT+TCPLEN+TCP_MAXOPT);
  146. /* Peek at IP header */
  147. iplen = hlen = ntohip(&iph,&copy);
  148. /* Bail if this packet isn't TCP, or is an IP fragment */
  149. if(iph.protocol != TCP_PTCL || iph.offset != 0 || iph.flags.mf){
  150. /* Send as regular IP */
  151. if(iph.protocol != TCP_PTCL)
  152. comp->sls_o_nontcp++;
  153. else
  154. comp->sls_o_tcp++;
  155. free_p(&copy);
  156. return SL_TYPE_IP;
  157. }
  158. /* Extract TCP header */
  159. hlen += ntohtcp(&th,&copy);
  160. free_p(&copy); /* Done with copy */
  161. /*  Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or
  162.  *  some other control bit is set, or has options).
  163.  */
  164. if(th.flags.syn || th.flags.fin || th.flags.rst || !th.flags.ack
  165.  || th.flags.mss || th.flags.wscale || th.flags.tstamp){
  166. /* TCP connection stuff; send as regular IP */
  167. comp->sls_o_tcp++;
  168. return SL_TYPE_IP;
  169. }
  170. /*
  171.  * Packet is compressible -- we're going to send either a
  172.  * COMPRESSED_TCP or UNCOMPRESSED_TCP packet.  Either way,
  173.  * we need to locate (or create) the connection state.
  174.  *
  175.  * States are kept in a circularly linked list with
  176.  * xmit_oldest pointing to the end of the list.  The
  177.  * list is kept in lru order by moving a state to the
  178.  * head of the list whenever it is referenced.  Since
  179.  * the list is short and, empirically, the connection
  180.  * we want is almost always near the front, we locate
  181.  * states via linear search.  If we don't find a state
  182.  * for the datagram, the oldest state is (re-)used.
  183.  */
  184. for ( ; ; ) {
  185. if( iph.source == cs->cs_ip.source
  186.  && iph.dest == cs->cs_ip.dest
  187.  && th.source == cs->cs_tcp.source
  188.  && th.dest == cs->cs_tcp.dest)
  189. goto found;
  190. /* if current equal oldest, at end of list */
  191. if ( cs == ocs )
  192. break;
  193. lcs = cs;
  194. cs = cs->next;
  195. comp->sls_o_searches++;
  196. };
  197. /*
  198.  * Didn't find it -- re-use oldest cstate.  Send an
  199.  * uncompressed packet that tells the other side what
  200.  * connection number we're using for this conversation.
  201.  *
  202.  * Note that since the state list is circular, the oldest
  203.  * state points to the newest and we only need to set
  204.  * xmit_oldest to update the lru linkage.
  205.  */
  206. comp->sls_o_misses++;
  207. comp->xmit_oldest = lcs->this;
  208. goto uncompressed;
  209. found:
  210. /*
  211.  * Found it -- move to the front on the connection list.
  212.  */
  213. if(lcs == ocs) {
  214. /* found at most recently used */
  215. } else if (cs == ocs) {
  216. /* found at least recently used */
  217. comp->xmit_oldest = lcs->this;
  218. } else {
  219. /* more than 2 elements */
  220. lcs->next = cs->next;
  221. cs->next = ocs->next;
  222. ocs->next = cs;
  223. }
  224. /*
  225.  * Make sure that only what we expect to change changed.
  226.  * Check the following:
  227.  * IP protocol version, header length & type of service.
  228.  * The "Don't fragment" bit.
  229.  * The time-to-live field.
  230.  * The TCP header length.
  231.  * IP options, if any.
  232.  * TCP options, if any.
  233.  * If any of these things are different between the previous &
  234.  * current datagram, we send the current datagram `uncompressed'.
  235.  */
  236. oth = &cs->cs_tcp;
  237. if(iph.version != cs->cs_ip.version || iph.optlen != cs->cs_ip.optlen
  238.  || iph.tos != cs->cs_ip.tos
  239.  || iph.flags.df != cs->cs_ip.flags.df
  240.  || iph.ttl != cs->cs_ip.ttl
  241.  || (iph.optlen > 0 && memcmp(iph.options,cs->cs_ip.options,iph.optlen) != 0)){
  242. goto uncompressed;
  243. }
  244. /*
  245.  * Figure out which of the changing fields changed.  The
  246.  * receiver expects changes in the order: urgent, window,
  247.  * ack, seq (the order minimizes the number of temporaries
  248.  * needed in this section of code).
  249.  */
  250. if(th.flags.urg){
  251. deltaS = th.up;
  252. cp = encode(cp,deltaS);
  253. changes |= NEW_U;
  254. } else if(th.up != oth->up){
  255. /* argh! URG not set but urp changed -- a sensible
  256.  * implementation should never do this but RFC793
  257.  * doesn't prohibit the change so we have to deal
  258.  * with it. */
  259. goto uncompressed;
  260. }
  261. if((deltaS = th.wnd - oth->wnd) != 0){
  262. cp = encode(cp,deltaS);
  263. changes |= NEW_W;
  264. }
  265. if((deltaA = th.ack - oth->ack) != 0L){
  266. if(deltaA > 0x0000ffff)
  267. goto uncompressed;
  268. cp = encode(cp,deltaA);
  269. changes |= NEW_A;
  270. }
  271. if((deltaS = th.seq - oth->seq) != 0L){
  272. if(deltaS > 0x0000ffff)
  273. goto uncompressed;
  274. cp = encode(cp,deltaS);
  275. changes |= NEW_S;
  276. }
  277. switch(changes){
  278. case 0: /* Nothing changed. If this packet contains data and the
  279.  * last one didn't, this is probably a data packet following
  280.  * an ack (normal on an interactive connection) and we send
  281.  * it compressed.  Otherwise it's probably a retransmit,
  282.  * retransmitted ack or window probe.  Send it uncompressed
  283.  * in case the other side missed the compressed version.
  284.  */
  285. if(iph.length != cs->cs_ip.length && cs->cs_ip.length == hlen)
  286. break;
  287. goto uncompressed;
  288. case SPECIAL_I:
  289. case SPECIAL_D:
  290. /* actual changes match one of our special case encodings --
  291.  * send packet uncompressed.
  292.  */
  293. goto uncompressed;
  294. case NEW_S|NEW_A:
  295. if(deltaS == deltaA &&
  296.     deltaS == cs->cs_ip.length - hlen){
  297. /* special case for echoed terminal traffic */
  298. changes = SPECIAL_I;
  299. cp = new_seq;
  300. }
  301. break;
  302. case NEW_S:
  303. if(deltaS == cs->cs_ip.length - hlen){
  304. /* special case for data xfer */
  305. changes = SPECIAL_D;
  306. cp = new_seq;
  307. }
  308. break;
  309. }
  310. deltaS = iph.id - cs->cs_ip.id;
  311. if(deltaS != 1){
  312. cp = encode(cp,deltaS);
  313. changes |= NEW_I;
  314. }
  315. if(th.flags.psh)
  316. changes |= TCP_PUSH_BIT;
  317. /* Grab the cksum before we overwrite it below.  Then update our
  318.  * state with this packet's header.
  319.  */
  320. deltaA = th.checksum;
  321. ASSIGN(cs->cs_ip,iph);
  322. ASSIGN(cs->cs_tcp,th);
  323. /* We want to use the original packet as our compressed packet.
  324.  * (cp - new_seq) is the number of bytes we need for compressed
  325.  * sequence numbers.  In addition we need one byte for the change
  326.  * mask, one for the connection id and two for the tcp checksum.
  327.  * So, (cp - new_seq) + 4 bytes of header are needed.
  328.  */
  329. deltaS = cp - new_seq;
  330. pullup(bpp,NULL,hlen); /* Strip TCP/IP headers */
  331. if(compress_cid == 0 || comp->xmit_current != cs->this){
  332. pushdown(bpp,NULL,deltaS + 4);
  333. cp = (*bpp)->data;
  334. *cp++ = changes | NEW_C;
  335. *cp++ = cs->this;
  336. comp->xmit_current = cs->this;
  337. } else {
  338. pushdown(bpp,NULL,deltaS + 3);
  339. cp = (*bpp)->data;
  340. *cp++ = changes;
  341. }
  342. cp = put16(cp,(uint16)deltaA); /* Write TCP checksum */
  343. memcpy(cp,new_seq,deltaS); /* Write list of deltas */
  344. comp->sls_o_compressed++;
  345. return SL_TYPE_COMPRESSED_TCP;
  346. /* Update connection state cs & send uncompressed packet (i.e.,
  347.  * a regular ip/tcp packet but with the 'conversation id' we hope
  348.  * to use on future compressed packets in the protocol field).
  349.  */
  350. uncompressed:
  351. iph.protocol = cs->this;
  352. ASSIGN(cs->cs_ip,iph);
  353. ASSIGN(cs->cs_tcp,th);
  354. comp->xmit_current = cs->this;
  355. comp->sls_o_uncompressed++;
  356. pullup(bpp,NULL,iplen); /* Strip old IP header */
  357. htonip(&iph,bpp,IP_CS_OLD); /* replace with new one */
  358. return SL_TYPE_UNCOMPRESSED_TCP;
  359. }
  360. int
  361. slhc_uncompress(comp, bpp)
  362. struct slcompress *comp;
  363. struct mbuf **bpp;
  364. {
  365. register int changes;
  366. long x;
  367. register struct tcp *thp;
  368. register struct cstate *cs;
  369. int len;
  370. if(bpp == NULL){
  371. comp->sls_i_error++;
  372. return 0;
  373. }
  374. /* We've got a compressed packet; read the change byte */
  375. comp->sls_i_compressed++;
  376. if(len_p(*bpp) < 3){
  377. comp->sls_i_error++;
  378. return 0;
  379. }
  380. changes = PULLCHAR(bpp); /* "Can't fail" */
  381. if(changes & NEW_C){
  382. /* Make sure the state index is in range, then grab the state.
  383.  * If we have a good state index, clear the 'discard' flag.
  384.  */
  385. x = PULLCHAR(bpp); /* Read conn index */
  386. if(x < 0 || x > comp->rslot_limit)
  387. goto bad;
  388. comp->flags &=~ SLF_TOSS;
  389. comp->recv_current = x;
  390. } else {
  391. /* this packet has an implicit state index.  If we've
  392.  * had a line error since the last time we got an
  393.  * explicit state index, we have to toss the packet. */
  394. if(comp->flags & SLF_TOSS){
  395. comp->sls_i_tossed++;
  396. return 0;
  397. }
  398. }
  399. cs = &comp->rstate[comp->recv_current];
  400. thp = &cs->cs_tcp;
  401. if((x = pull16(bpp)) == -1) /* Read the TCP checksum */
  402. goto bad;
  403. thp->checksum = x;
  404. thp->flags.psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
  405. switch(changes & SPECIALS_MASK){
  406. case SPECIAL_I: /* Echoed terminal traffic */
  407. {
  408. register uint16 i;
  409. i = cs->cs_ip.length;
  410. i -= (cs->cs_ip.optlen + IPLEN + TCPLEN);
  411. thp->ack += i;
  412. thp->seq += i;
  413. }
  414. break;
  415. case SPECIAL_D: /* Unidirectional data */
  416. thp->seq += cs->cs_ip.length - (cs->cs_ip.optlen +IPLEN + TCPLEN);
  417. break;
  418. default:
  419. if(changes & NEW_U){
  420. thp->flags.urg = 1;
  421. if((x = decode(bpp)) == -1)
  422. goto bad;
  423. thp->up = x;
  424. } else
  425. thp->flags.urg = 0;
  426. if(changes & NEW_W){
  427. if((x = decode(bpp)) == -1)
  428. goto bad;
  429. thp->wnd += x;
  430. }
  431. if(changes & NEW_A){
  432. if((x = decode(bpp)) == -1)
  433. goto bad;
  434. thp->ack += x;
  435. }
  436. if(changes & NEW_S){
  437. if((x = decode(bpp)) == -1)
  438. goto bad;
  439. thp->seq += x;
  440. }
  441. break;
  442. }
  443. if(changes & NEW_I){
  444. if((x = decode(bpp)) == -1)
  445. goto bad;
  446. cs->cs_ip.id += x;
  447. } else
  448. cs->cs_ip.id++;
  449. /*
  450.  * At this point, bpp points to the first byte of data in the
  451.  * packet.  Put the reconstructed TCP and IP headers back on the
  452.  * packet.  Recalculate IP checksum (but not TCP checksum).
  453.  */
  454. len = len_p(*bpp) + IPLEN + TCPLEN + cs->cs_ip.optlen;
  455. cs->cs_ip.length = len;
  456. htontcp(thp,bpp,0,0);
  457. htonip(&cs->cs_ip,bpp,IP_CS_NEW);
  458. return len;
  459. bad:
  460. comp->sls_i_error++;
  461. return slhc_toss( comp );
  462. }
  463. int
  464. slhc_remember(comp, bpp)
  465. struct slcompress *comp;
  466. struct mbuf **bpp;
  467. {
  468. register struct cstate *cs;
  469. struct ip iph;
  470. struct tcp th;
  471. uint16 len;
  472. uint16 hdrlen;
  473. int slot;
  474. if(bpp == NULL){
  475. comp->sls_i_error++;
  476. return slhc_toss(comp);
  477. }
  478. /* Sneak a peek at the IP header's IHL field to find its length */
  479. hdrlen = ((*bpp)->data[0] & 0xf) << 2;
  480. if(hdrlen < IPLEN){
  481. /* The IP header length field is too small to be valid */
  482. comp->sls_i_error++;
  483. return slhc_toss(comp);
  484. }
  485. len = len_p(*bpp); /* Actual length of whole packet */
  486. ntohip(&iph,bpp); /* Extract IP header */
  487. /* Verify indicated length <= actual length */
  488. if(iph.length > len){
  489. /* Packet has been truncated, or header is garbage */
  490. comp->sls_i_error++;
  491. return slhc_toss(comp);
  492. }
  493. /* Verify conn ID */
  494. slot = iph.protocol;
  495. if(slot > comp->rslot_limit){
  496. /* Out of range */
  497. comp->sls_i_error++;
  498. return slhc_toss(comp);
  499. }
  500. iph.protocol = TCP_PTCL; /* Replace conn ID with TCP_PTCL */
  501. /* Extract TCP header and replace both headers
  502.  * Neither header checksum is recalculated
  503.  */
  504. ntohtcp(&th,bpp);
  505. htontcp(&th,bpp,0,0);
  506. htonip(&iph,bpp,IP_CS_OLD);
  507. /* Checksum IP header (now that protocol field is TCP again) */
  508. if(cksum(NULL,*bpp,hdrlen) != 0){
  509. /* Bad IP header checksum; discard */
  510. comp->sls_i_error++;
  511. return slhc_toss(comp);
  512. }
  513. /* Update local state */
  514. comp->recv_current = slot;
  515. cs = &comp->rstate[slot];
  516. comp->flags &=~ SLF_TOSS;
  517. ASSIGN(cs->cs_ip,iph);
  518. ASSIGN(cs->cs_tcp,th);
  519. comp->sls_i_uncompressed++;
  520. return len;
  521. }
  522. int
  523. slhc_toss(comp)
  524. struct slcompress *comp;
  525. {
  526. if ( comp == NULL )
  527. return 0;
  528. comp->flags |= SLF_TOSS;
  529. return 0;
  530. }
  531. void
  532. slhc_i_status(comp)
  533. struct slcompress *comp;
  534. {
  535. if (comp != NULL) {
  536. printf("t%10ld Cmp,"
  537. " %10ld Uncmp,"
  538. " %10ld Bad, "
  539. " %10ld Tossedn",
  540. comp->sls_i_compressed,
  541. comp->sls_i_uncompressed,
  542. comp->sls_i_error,
  543. comp->sls_i_tossed);
  544. }
  545. }
  546. void
  547. slhc_o_status(comp)
  548. struct slcompress *comp;
  549. {
  550. if (comp != NULL) {
  551. printf("t%10ld Cmp,"
  552. " %10ld Uncmp,"
  553. " %10ld AsIs,"
  554. " %10ld NotTCPn",
  555. comp->sls_o_compressed,
  556. comp->sls_o_uncompressed,
  557. comp->sls_o_tcp,
  558. comp->sls_o_nontcp);
  559. printf("t%10ld Searches,"
  560. " %10ld Missesn",
  561. comp->sls_o_searches,
  562. comp->sls_o_misses);
  563. }
  564. }