home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 1 / HamRadio.cdr / misc / tcpipsrc / ip.c < prev    next >
C/C++ Source or Header  |  1991-01-26  |  13KB  |  524 lines

  1. /* Upper half of IP, consisting of send/receive primitives, including
  2.  * fragment reassembly, for higher level protocols.
  3.  * Not needed when running as a standalone gateway.
  4.  * Copyright 1991 Phil Karn, KA9Q
  5.  */
  6. #include "global.h"
  7. #include "mbuf.h"
  8. #include "timer.h"
  9. #include "internet.h"
  10. #include "netuser.h"
  11. #include "iface.h"
  12. #include "pktdrvr.h"
  13. #include "ip.h"
  14. #include "icmp.h"
  15.  
  16. static struct mbuf *fraghandle __ARGS((struct ip *ip,struct mbuf *bp));
  17. static void ip_timeout __ARGS((void *arg));
  18. static void free_reasm __ARGS((struct reasm *rp));
  19. static void freefrag __ARGS((struct frag *fp));
  20. static struct reasm *lookup_reasm __ARGS((struct ip *ip));
  21. static struct reasm *creat_reasm __ARGS((struct ip *ip));
  22. static struct frag *newfrag __ARGS((int16 offset,int16 last,struct mbuf *bp));
  23.  
  24. struct mib_entry Ip_mib[20] = {
  25.     "",            0,
  26.     "ipForwarding",        1,
  27.     "ipDefaultTTL",        MAXTTL,
  28.     "ipInReceives",        0,
  29.     "ipInHdrErrors",    0,
  30.     "ipInAddrErrors",    0,
  31.     "ipForwDatagrams",    0,
  32.     "ipInUnknownProtos",    0,
  33.     "ipInDiscards",        0,
  34.     "ipInDelivers",        0,
  35.     "ipOutRequests",    0,
  36.     "ipOutDiscards",    0,
  37.     "ipOutNoRoutes",    0,
  38.     "ipReasmTimeout",    TLB,
  39.     "ipReasmReqds",        0,
  40.     "ipReasmOKs",        0,
  41.     "ipReasmFails",        0,
  42.     "ipFragOKs",        0,
  43.     "ipFragFails",        0,
  44.     "ipFragCreates",    0,
  45. };
  46.  
  47. struct reasm *Reasmq;
  48. static struct raw_ip *Raw_ip;
  49.  
  50. #define    INSERT    0
  51. #define    APPEND    1
  52. #define    PREPEND    2
  53.  
  54. /* Send an IP datagram. Modeled after the example interface on p 32 of
  55.  * RFC 791
  56.  */
  57. int
  58. ip_send(source,dest,protocol,tos,ttl,bp,length,id,df)
  59. int32 source;            /* source address */
  60. int32 dest;            /* Destination address */
  61. char protocol;            /* Protocol */
  62. char tos;            /* Type of service */
  63. char ttl;            /* Time-to-live */
  64. struct mbuf *bp;        /* Data portion of datagram */
  65. int16 length;            /* Optional length of data portion */
  66. int16 id;            /* Optional identification */
  67. char df;            /* Don't-fragment flag */
  68. {
  69.     struct mbuf *tbp;
  70.     struct ip ip;        /* Pointer to IP header */
  71.     static int16 id_cntr;    /* Datagram serial number */
  72.     struct phdr phdr;
  73.  
  74.     ipOutRequests++;
  75.  
  76.     if(source == INADDR_ANY)
  77.         source = locaddr(dest);
  78.     if(length == 0 && bp != NULLBUF)
  79.         length = len_p(bp);
  80.     if(id == 0)
  81.         id = id_cntr++;        
  82.     if(ttl == 0)
  83.         ttl = ipDefaultTTL;
  84.  
  85.     /* Fill in IP header */
  86.     ip.version = IPVERSION;
  87.     ip.tos = tos;
  88.     ip.length = IPLEN + length;
  89.     ip.id = id;
  90.     ip.offset = 0;
  91.     ip.flags.mf = 0;
  92.     ip.flags.df = df;
  93.     ip.ttl = ttl;
  94.     ip.protocol = protocol;
  95.     ip.source = source;
  96.     ip.dest = dest;
  97.     ip.optlen = 0;
  98.     if((tbp = htonip(&ip,bp,0)) == NULLBUF){
  99.         free_p(bp);
  100.         return -1;
  101.     }
  102.     if((bp = pushdown(tbp,sizeof(phdr))) == NULLBUF){
  103.         free_p(tbp);
  104.         return -1;
  105.     }
  106.     if(ismyaddr(ip.dest)){
  107.         /* Pretend it has been sent by the loopback interface before
  108.          * it appears in the receive queue
  109.          */
  110.         phdr.iface = &Loopback;
  111.         Loopback.ipsndcnt++;
  112.         Loopback.rawsndcnt++;
  113.     } else
  114.         phdr.iface = NULLIF;
  115.     phdr.type = CL_NONE;
  116.     memcpy(&bp->data[0],(char *)&phdr,sizeof(phdr));
  117.     enqueue(&Hopper,bp);
  118.     return 0;
  119. }
  120.  
  121. /* Reassemble incoming IP fragments and dispatch completed datagrams
  122.  * to the proper transport module
  123.  */
  124. void
  125. ip_recv(iface,ip,bp,rxbroadcast)
  126. struct iface *iface;    /* Incoming interface */
  127. struct ip *ip;        /* Extracted IP header */
  128. struct mbuf *bp;    /* Data portion */
  129. int rxbroadcast;    /* True if received on subnet broadcast address */
  130. {
  131.     /* Function to call with completed datagram */
  132.     register struct raw_ip *rp;
  133.     struct mbuf *bp1,*tbp;
  134.     int rxcnt = 0;
  135.     register struct iplink *ipp;
  136.  
  137.     /* If we have a complete packet, call the next layer
  138.      * to handle the result. Note that fraghandle passes back
  139.      * a length field that does NOT include the IP header
  140.      */
  141.     if((bp = fraghandle(ip,bp)) == NULLBUF)
  142.         return;        /* Not done yet */
  143.  
  144.     ipInDelivers++;
  145.  
  146.     for(rp = Raw_ip;rp != NULLRIP;rp = rp->next){
  147.         if(rp->protocol != ip->protocol)
  148.             continue;
  149.         rxcnt++;
  150.         /* Duplicate the data portion, and put the header back on */
  151.         dup_p(&bp1,bp,0,len_p(bp));
  152.         if(bp1 != NULLBUF && (tbp = htonip(ip,bp1,1)) != NULLBUF){
  153.             enqueue(&rp->rcvq,tbp);
  154.             if(rp->r_upcall != NULLVFP)
  155.                 (*rp->r_upcall)(rp);
  156.         } else {
  157.             free_p(bp1);
  158.         }
  159.     }
  160.     /* Look it up in the transport protocol table */
  161.     for(ipp = Iplink;ipp->funct != NULL;ipp++){
  162.         if(ipp->proto == ip->protocol)
  163.             break;
  164.     }
  165.     if(ipp->funct != NULL){
  166.         /* Found, call transport protocol */
  167.         (*ipp->funct)(iface,ip,bp,rxbroadcast);
  168.     } else {
  169.         /* Not found */
  170.         if(rxcnt == 0){
  171.             /* Send an ICMP Protocol Unknown response... */
  172.             ipInUnknownProtos++;
  173.             /* ...unless it's a broadcast */
  174.             if(!rxbroadcast){
  175.                 icmp_output(ip,bp,ICMP_DEST_UNREACH,
  176.                  ICMP_PROT_UNREACH,NULLICMP);
  177.             }
  178.         }
  179.         free_p(bp);
  180.     }
  181. }
  182. /* Process IP datagram fragments
  183.  * If datagram is complete, return it with ip->length containing the data
  184.  * length (MINUS header); otherwise return NULLBUF
  185.  */
  186. static
  187. struct mbuf *
  188. fraghandle(ip,bp)
  189. struct ip *ip;        /* IP header, host byte order */
  190. struct mbuf *bp;    /* The fragment itself */
  191. {
  192.     register struct reasm *rp; /* Pointer to reassembly descriptor */
  193.     struct frag *lastfrag,*nextfrag,*tfp;
  194.     struct mbuf *tbp;
  195.     int16 i;
  196.     int16 last;        /* Index of first byte beyond fragment */
  197.  
  198.     last = ip->offset + ip->length - (IPLEN + ip->optlen);
  199.  
  200.     rp = lookup_reasm(ip);
  201.     if(ip->offset == 0 && !ip->flags.mf){
  202.         /* Complete datagram received. Discard any earlier fragments */
  203.         if(rp != NULLREASM){
  204.             free_reasm(rp);
  205.             ipReasmOKs++;
  206.         }
  207.         return bp;
  208.     }
  209.     ipReasmReqds++;
  210.     if(rp == NULLREASM){
  211.         /* First fragment; create new reassembly descriptor */
  212.         if((rp = creat_reasm(ip)) == NULLREASM){
  213.             /* No space for descriptor, drop fragment */
  214.             ipReasmFails++;
  215.             free_p(bp);
  216.             return NULLBUF;
  217.         }
  218.     }
  219.     /* Keep restarting timer as long as we keep getting fragments */
  220.     stop_timer(&rp->timer);
  221.     start_timer(&rp->timer);
  222.  
  223.     /* If this is the last fragment, we now know how long the
  224.      * entire datagram is; record it
  225.      */
  226.     if(!ip->flags.mf)
  227.         rp->length = last;
  228.  
  229.     /* Set nextfrag to the first fragment which begins after us,
  230.      * and lastfrag to the last fragment which begins before us
  231.      */
  232.     lastfrag = NULLFRAG;
  233.     for(nextfrag = rp->fraglist;nextfrag != NULLFRAG;nextfrag = nextfrag->next){
  234.         if(nextfrag->offset > ip->offset)
  235.             break;
  236.         lastfrag = nextfrag;
  237.     }
  238.     /* Check for overlap with preceeding fragment */
  239.     if(lastfrag != NULLFRAG  && ip->offset < lastfrag->last){
  240.         /* Strip overlap from new fragment */
  241.         i = lastfrag->last - ip->offset;
  242.         pullup(&bp,NULLCHAR,i);
  243.         if(bp == NULLBUF)
  244.             return NULLBUF;    /* Nothing left */
  245.         ip->offset += i;
  246.     }
  247.     /* Look for overlap with succeeding segments */
  248.     for(; nextfrag != NULLFRAG; nextfrag = tfp){
  249.         tfp = nextfrag->next;    /* save in case we delete fp */
  250.  
  251.         if(nextfrag->offset >= last)
  252.             break;    /* Past our end */
  253.         /* Trim the front of this entry; if nothing is
  254.          * left, remove it.
  255.          */
  256.         i = last - nextfrag->offset;
  257.         pullup(&nextfrag->buf,NULLCHAR,i);
  258.         if(nextfrag->buf == NULLBUF){
  259.             /* superseded; delete from list */
  260.             if(nextfrag->prev != NULLFRAG)
  261.                 nextfrag->prev->next = nextfrag->next;
  262.             else
  263.                 rp->fraglist = nextfrag->next;
  264.             if(tfp->next != NULLFRAG)
  265.                 nextfrag->next->prev = nextfrag->prev;
  266.             freefrag(nextfrag);
  267.         } else
  268.             nextfrag->offset = last;
  269.     }
  270.     /* Lastfrag now points, as before, to the fragment before us;
  271.      * nextfrag points at the next fragment. Check to see if we can
  272.      * join to either or both fragments.
  273.      */
  274.     i = INSERT;
  275.     if(lastfrag != NULLFRAG && lastfrag->last == ip->offset)
  276.         i |= APPEND;
  277.     if(nextfrag != NULLFRAG && nextfrag->offset == last)
  278.         i |= PREPEND;
  279.     switch(i){
  280.     case INSERT:    /* Insert new desc between lastfrag and nextfrag */
  281.         tfp = newfrag(ip->offset,last,bp);
  282.         tfp->prev = lastfrag;
  283.         tfp->next = nextfrag;
  284.         if(lastfrag != NULLFRAG)
  285.             lastfrag->next = tfp;    /* Middle of list */
  286.         else
  287.             rp->fraglist = tfp;    /* First on list */
  288.         if(nextfrag != NULLFRAG)
  289.             nextfrag->prev = tfp;
  290.         break;
  291.     case APPEND:    /* Append to lastfrag */
  292.         append(&lastfrag->buf,bp);
  293.         lastfrag->last = last;    /* Extend forward */
  294.         break;
  295.     case PREPEND:    /* Prepend to nextfrag */
  296.         tbp = nextfrag->buf;
  297.         nextfrag->buf = bp;
  298.         append(&nextfrag->buf,tbp);
  299.         nextfrag->offset = ip->offset;    /* Extend backward */
  300.         break;
  301.     case (APPEND|PREPEND):
  302.         /* Consolidate by appending this fragment and nextfrag
  303.          * to lastfrag and removing the nextfrag descriptor
  304.          */
  305.         append(&lastfrag->buf,bp);
  306.         append(&lastfrag->buf,nextfrag->buf);
  307.         nextfrag->buf = NULLBUF;
  308.         lastfrag->last = nextfrag->last;
  309.  
  310.         /* Finally unlink and delete the now unneeded nextfrag */
  311.         lastfrag->next = nextfrag->next;
  312.         if(nextfrag->next != NULLFRAG)
  313.             nextfrag->next->prev = lastfrag;
  314.         freefrag(nextfrag);
  315.         break;
  316.     }
  317.     if(rp->fraglist->offset == 0 && rp->fraglist->next == NULLFRAG 
  318.         && rp->length != 0){
  319.         /* We've gotten a complete datagram, so extract it from the
  320.          * reassembly buffer and pass it on.
  321.          */
  322.         bp = rp->fraglist->buf;
  323.         rp->fraglist->buf = NULLBUF;
  324.         /* Tell IP the entire length */
  325.         ip->length = rp->length + (IPLEN + ip->optlen);
  326.         free_reasm(rp);
  327.         ipReasmOKs++;
  328.         return bp;
  329.     } else
  330.         return NULLBUF;
  331. }
  332. /* Arrange for receipt of raw IP datagrams */
  333. struct raw_ip *
  334. raw_ip(protocol,r_upcall)
  335. int protocol;
  336. void (*r_upcall)();
  337. {
  338.     register struct raw_ip *rp;
  339.  
  340.     rp = (struct raw_ip *)callocw(1,sizeof(struct raw_ip));
  341.     rp->protocol = protocol;
  342.     rp->r_upcall = r_upcall;
  343.     rp->next = Raw_ip;
  344.     if(rp->next != NULLRIP)
  345.         rp->next->prev = rp;
  346.     Raw_ip = rp;
  347.     return rp;
  348. }
  349. /* Free a raw IP descriptor */
  350. void
  351. del_ip(rpp)
  352. struct raw_ip *rpp;
  353. {
  354.     register struct raw_ip *rp;
  355.  
  356.     /* Do sanity check on arg */
  357.     for(rp = Raw_ip;rp != NULLRIP;rp = rp->next)
  358.         if(rp == rpp)
  359.             break;
  360.     if(rp == NULLRIP)
  361.         return;    /* Doesn't exist */
  362.  
  363.     /* Unlink */
  364.     if(rp->prev != NULLRIP)
  365.         rp->prev->next = rp->next;
  366.     else
  367.         Raw_ip = rp->next;
  368.     if(rp->next != NULLRIP)
  369.         rp->next->prev = rp->prev;
  370.     /* Free resources */
  371.     free_q(&rp->rcvq);
  372.     free((char *)rp);
  373. }
  374.  
  375. static struct reasm *
  376. lookup_reasm(ip)
  377. struct ip *ip;
  378. {
  379.     register struct reasm *rp;
  380.  
  381.     for(rp = Reasmq;rp != NULLREASM;rp = rp->next){
  382.         if(ip->source == rp->source && ip->dest == rp->dest
  383.          && ip->protocol == rp->protocol && ip->id == rp->id)
  384.             return rp;
  385.     }
  386.     return NULLREASM;
  387. }
  388. #ifdef    FOO
  389. static
  390. int16
  391. hash_reasm(source,dest,protocol,id)
  392. int32 source;
  393. int32 dest,
  394. char protocol;
  395. int16 id;
  396. {
  397.     register unsigned int hval;
  398.  
  399.     hval = loword(source);
  400.     hval ^= hiword(source);
  401.     hval ^= loword(dest);
  402.     hval ^= hiword(dest);
  403.     hval ^= uchar(protocol);
  404.     hval ^= id;
  405.     return hval % RHASH;
  406. }
  407. #endif
  408. /* Create a reassembly descriptor,
  409.  * put at head of reassembly list
  410.  */
  411. static struct reasm *
  412. creat_reasm(ip)
  413. register struct ip *ip;
  414. {
  415.     register struct reasm *rp;
  416.  
  417.     if((rp = (struct reasm *)calloc(1,sizeof(struct reasm))) == NULLREASM)
  418.         return rp;    /* No space for descriptor */
  419.     rp->source = ip->source;
  420.     rp->dest = ip->dest;
  421.     rp->id = ip->id;
  422.     rp->protocol = ip->protocol;
  423.     set_timer(&rp->timer,ipReasmTimeout * 1000);
  424.     rp->timer.func = ip_timeout;
  425.     rp->timer.arg = rp;
  426.  
  427.     rp->next = Reasmq;
  428.     if(rp->next != NULLREASM)
  429.         rp->next->prev = rp;
  430.     Reasmq = rp;
  431.     return rp;
  432. }
  433.  
  434. /* Free all resources associated with a reassembly descriptor */
  435. static void
  436. free_reasm(rp)
  437. register struct reasm *rp;
  438. {
  439.     register struct frag *fp;
  440.  
  441.     stop_timer(&rp->timer);
  442.     /* Remove from list of reassembly descriptors */
  443.     if(rp->prev != NULLREASM)
  444.         rp->prev->next = rp->next;
  445.     else
  446.         Reasmq = rp->next;
  447.     if(rp->next != NULLREASM)
  448.         rp->next->prev = rp->prev;
  449.     /* Free any fragments on list, starting at beginning */
  450.     while((fp = rp->fraglist) != NULLFRAG){
  451.         rp->fraglist = fp->next;
  452.         free_p(fp->buf);
  453.         free((char *)fp);
  454.     }
  455.     free((char *)rp);
  456. }
  457.  
  458. /* Handle reassembly timeouts by deleting all reassembly resources */
  459. static void
  460. ip_timeout(arg)
  461. void *arg;
  462. {
  463.     register struct reasm *rp;
  464.  
  465.     rp = (struct reasm *)arg;
  466.     free_reasm(rp);
  467.     ipReasmFails++;
  468. }
  469. /* Create a fragment */
  470. static
  471. struct frag *
  472. newfrag(offset,last,bp)
  473. int16 offset,last;
  474. struct mbuf *bp;
  475. {
  476.     struct frag *fp;
  477.  
  478.     if((fp = (struct frag *)calloc(1,sizeof(struct frag))) == NULLFRAG){
  479.         /* Drop fragment */
  480.         free_p(bp);
  481.         return NULLFRAG;
  482.     }
  483.     fp->buf = bp;
  484.     fp->offset = offset;
  485.     fp->last = last;
  486.     return fp;
  487. }
  488. /* Delete a fragment, return next one on queue */
  489. static
  490. void
  491. freefrag(fp)
  492. struct frag *fp;
  493. {
  494.     free_p(fp->buf);
  495.     free((char *)fp);
  496. }
  497.  
  498. /* In red alert mode, blow away the whole reassembly queue. Otherwise crunch
  499.  * each fragment on each reassembly descriptor
  500.  */
  501. void
  502. ip_garbage(red)
  503. int red;
  504. {
  505.     struct reasm *rp,*rp1;
  506.     struct frag *fp;
  507.     struct raw_ip *rwp;
  508.  
  509.     /* Run through the reassembly queue */
  510.     for(rp = Reasmq;rp != NULLREASM;rp = rp1){
  511.         rp1 = rp->next;
  512.         if(red){
  513.             free_reasm(rp);
  514.         } else {
  515.             for(fp = rp->fraglist;fp != NULLFRAG;fp = fp->next){
  516.                 mbuf_crunch(&fp->buf);
  517.             }
  518.         }
  519.     }
  520.     /* Run through the raw IP queue */
  521.     for(rwp = Raw_ip;rwp != NULLRIP;rwp = rwp->next)
  522.         mbuf_crunch(&rwp->rcvq);
  523. }
  524.