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

  1. /*
  2.  *    HOP.C   -- trace route packets take to a remote host
  3.  *
  4.  *    02-90    -- Katie Stevens (dkstevens@ucdavis.edu)
  5.  *           UC Davis, Computing Services
  6.  *           Davis, CA
  7.  *    04-90    -- Modified by Phil Karn to use raw IP sockets to read replies
  8.  *    08-90    -- Modified by Bill Simpson to display domain names
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include "global.h"
  14. #include "mbuf.h"
  15. #include "usock.h"
  16. #include "socket.h"
  17. #include "session.h"
  18. #include "timer.h"
  19. #include "proc.h"
  20. #include "netuser.h"
  21. #include "domain.h"
  22. #include "commands.h"
  23. #include "tty.h"
  24. #include "cmdparse.h"
  25. #include "ip.h"
  26. #include "icmp.h"
  27. #include "udp.h"
  28. #include "hardware.h"
  29.  
  30. #define HOPMAXQUERY    5        /* Max# queries each TTL value */
  31. static int16 Hoprport = 32768+666;    /* funny port for udp probes */
  32. #define HOP_HIGHBIT    32768        /* Mask to check ICMP msgs */
  33.  
  34.  
  35. #define HOPTRACE    1        /* Enable HOP tracing */
  36. #ifdef HOPTRACE
  37. static int Hoptrace = 0;
  38. static int hoptrace __ARGS((int argc,char *argv[],void *p));
  39. #endif
  40.  
  41.  
  42. static unsigned  short Hopmaxttl  = 30;        /* max attempts */
  43. static unsigned  short Hopmaxwait = 5;        /* secs timeout each attempt */
  44. static unsigned  short Hopquery   = 3;        /* #probes each attempt */
  45.  
  46. static int hopcheck __ARGS((int argc,char *argv[],void *p));
  47. static int hopttl __ARGS((int argc,char *argv[],void *p));
  48. static int hopwait __ARGS((int argc,char *argv[],void *p));
  49. static int hopnum __ARGS((int argc,char *argv[],void *p));
  50. static int geticmp __ARGS((int s,int16 lport,int16 fport,
  51.     int32 *sender,char *type,char *code));
  52.  
  53. static struct cmds Hopcmds[] = {
  54.     "check",    hopcheck,    2048,    2,    "check <host>",
  55.     "maxttl",    hopttl,        0,    0,    NULLCHAR,
  56.     "maxwait",    hopwait,    0,    0,    NULLCHAR,
  57.     "queries",    hopnum,        0,    0,    NULLCHAR,
  58. #ifdef HOPTRACE
  59.     "trace",    hoptrace,    0,    0,    NULLCHAR,
  60. #endif
  61.     NULLCHAR,
  62. };
  63.  
  64. /* attempt to trace route to a remote host */
  65. int
  66. dohop(argc,argv,p)
  67. int argc;
  68. char *argv[];
  69. void *p;
  70. {
  71.     return subcmd(Hopcmds,argc,argv,p);
  72. }
  73.  
  74. /* Set/show # queries sent each TTL value */
  75. static int
  76. hopnum(argc,argv,p)
  77. int argc;
  78. char *argv[];
  79. void *p;
  80. {
  81.     int16 r;
  82.     int16 x = Hopquery;
  83.     r = setshort(&x,"# queries each attempt",argc,argv);
  84.     if ((x <= 0)||(x > HOPMAXQUERY)) {
  85.         printf("Must be  0 < x <= %d\n",HOPMAXQUERY);
  86.         return 0;
  87.     } else {
  88.         Hopquery = x;
  89.     }
  90.     return (int)r;
  91. }
  92. #ifdef HOPTRACE
  93. /* Set/show tracelevel */
  94. static int
  95. hoptrace(argc,argv,p)
  96. int argc;
  97. char *argv[];
  98. void *p;
  99. {
  100.     return setbool(&Hoptrace,"HOPCHECK tracing",argc,argv);
  101. }
  102. #endif
  103. /* Set/show maximum TTL value for a traceroute query */
  104. static int
  105. hopttl(argc,argv,p)
  106. int argc;
  107. char *argv[];
  108. void *p;
  109. {
  110.     int16 r;
  111.     int16 x = Hopmaxttl;
  112.     r = setshort(&x,"Max attempts to reach host",argc,argv);
  113.     if ((x <= 0)||(x > 255)) {
  114.         printf("Must be  0 < x <= 255\n");
  115.         return 0;
  116.     } else {
  117.         Hopmaxttl = x;
  118.     }
  119.     return (int)r;
  120. }
  121. /* Set/show #secs until timeout for a traceroute query */
  122. static int
  123. hopwait(argc,argv,p)
  124. int argc;
  125. char *argv[];
  126. void *p;
  127. {
  128.     int16 r;
  129.     int16 x = Hopmaxwait;
  130.     r = setshort(&x,"# secs to wait for reply to query",argc,argv);
  131.     if (x <= 0) {
  132.         printf("Must be >= 0\n");
  133.         return 0;
  134.     } else {
  135.         Hopmaxwait = x;
  136.     }
  137.     return (int)r;
  138. }
  139.  
  140. /* send probes to trace route of a remote host */
  141. static int
  142. hopcheck(argc,argv,p)
  143. int argc;
  144. char *argv[];
  145. void *p;
  146. {
  147.     struct session *sp;        /* Session for trace output */
  148.     int s;                /* Socket for queries */
  149.     int s1;                /* Raw socket for replies */
  150.     struct socket lsocket;        /* Local socket sending queries */
  151.     struct socket rsocket;        /* Final destination of queries */
  152.     int32 cticks;            /* Timer for query replies */
  153.     int32 icsource;            /* Sender of last ICMP reply */
  154.     char ictype;            /* ICMP type last ICMP reply */
  155.     char iccode;            /* ICMP code last ICMP reply */
  156.     int32 lastaddr;            /* Sender of previous ICMP reply */
  157.     struct sockaddr_in sock;
  158.     register struct usock *usp;
  159.     register struct sockaddr_in *sinp;
  160.     unsigned char sndttl, q;
  161.     int tracedone = 0;
  162.     int ilookup = 1;        /* Control of inverse domain lookup */
  163.     int c;
  164.     extern int optind;
  165.     char *hostname;
  166.  
  167.     optind = 1;
  168.     while((c = getopt(argc,argv,"n")) != EOF){
  169.         switch(c){
  170.         case 'n':
  171.             ilookup = 0;
  172.             break;
  173.         }
  174.     }
  175.     hostname = argv[optind];
  176.     /* Allocate a session descriptor */
  177.     if((sp = newsession(hostname,HOP)) == NULLSESSION){
  178.         tprintf("Too many sessions\n");
  179.         keywait(NULLCHAR,1);
  180.         return 1;
  181.     }
  182.     sp->s = s = -1;
  183.     sp->flowmode = 1;
  184.  
  185.     /* Setup UDP socket to remote host */
  186.     sock.sin_family = AF_INET;
  187.     sock.sin_port = Hoprport;
  188.     tprintf("Resolving %s... ",hostname);
  189.     if((sock.sin_addr.s_addr = resolve(hostname)) == 0){
  190.         tprintf("Host %s unknown\n",hostname);
  191.         keywait(NULLCHAR,1);
  192.         freesession(sp);
  193.         return 1;
  194.     }
  195.  
  196.     /* Open socket to remote host */
  197.     tprintf("traceroute to %s\n",psocket((struct sockaddr *)&sock));
  198.     if((sp->s = s = socket(AF_INET,SOCK_DGRAM,0)) == -1){
  199.         tprintf("Can't create udp socket\n");
  200.         keywait(NULLCHAR,1);
  201.         freesession(sp);
  202.         return 1;
  203.     }
  204.     if(connect(s,(char *)&sock,sizeof(sock)) == -1){
  205.         tprintf("Connect failed\n");
  206.         keywait(NULLCHAR,1);
  207.         freesession(sp);
  208.         return 1;
  209.     }
  210.     if((s1 = socket(AF_INET,SOCK_RAW,ICMP_PTCL)) == -1){
  211.         tprintf("Can't create raw socket\n");
  212.         keywait(NULLCHAR,1);
  213.         freesession(sp);
  214.         return 1;
  215.     }
  216.     /* Setup structures to send queries */
  217.     /* Retrieve socket details for user socket control block */
  218.     usp = itop(s);
  219.     sinp = (struct sockaddr_in *)usp->name;
  220.     lsocket.address = sinp->sin_addr.s_addr;
  221.     lsocket.port = sinp->sin_port;
  222.     sinp = (struct sockaddr_in *)usp->peername;
  223.     rsocket.address = sinp->sin_addr.s_addr;
  224.  
  225.     /* Send queries with increasing TTL; start with TTL=1 */
  226.     if (Hoptrace)
  227.         log(sp->s,"HOPCHECK start trace to %s\n",sp->name);
  228.     for (sndttl=1; (sndttl < Hopmaxttl); ++sndttl, sinp->sin_port++) {
  229.         /* Increment funny UDP port number each round */
  230.         rsocket.port = sinp->sin_port;
  231.         tprintf("%3d:",sndttl);
  232.         lastaddr = (int32)0;
  233.         /* Send a round of queries */
  234.         for (q=0; (q < Hopquery); ++q) {
  235.             send_udp(&lsocket,&rsocket,0,sndttl,NULLBUF,0,0,0);
  236.             cticks = msclock();
  237.             alarm( ((long)Hopmaxwait*1000L) );
  238.  
  239.             /* Wait for a reply to our query */
  240.             if(geticmp(s1,lsocket.port,rsocket.port,
  241.              &icsource,&ictype,&iccode) == -1){
  242.                 if(errno != EALARM)
  243.                     goto done;    /* User reset */
  244.                 /* Alarm rang, give up waiting for replies */
  245.                 tprintf(" ***");
  246.                 continue;
  247.             }
  248.             /* Save #ticks taken for reply */
  249.                         cticks = msclock() - cticks;
  250.             /* Report ICMP reply */
  251.             if (icsource != lastaddr) {
  252.                 struct rr *save_rrlp, *rrlp;
  253.  
  254.                 if(lastaddr != (int32)0)
  255.                     tprintf("\n    ");
  256.                 tprintf(" %-15s",inet_ntoa(icsource));
  257.                 if(ilookup){
  258.                     for(rrlp = save_rrlp = inverse_a(icsource);
  259.                         rrlp != NULLRR;
  260.                         rrlp = rrlp->next){
  261.                         if(rrlp->rdlength > 0){
  262.                             switch(rrlp->type){
  263.                             case TYPE_PTR:
  264.                                 tprintf(" %s", rrlp->rdata.name);
  265.                                 break;
  266.                             case TYPE_A:
  267.                                 tprintf(" %s", rrlp->name);
  268.                                 break;
  269.                             }
  270.                             if(rrlp->next != NULLRR)
  271.                                 tprintf("\n%20s"," ");
  272.                         }
  273.                     }
  274.                     free_rr(save_rrlp);
  275.  
  276.                 }
  277.                 lastaddr = icsource;
  278.             }
  279.                         tprintf(" (%ld ms)",cticks);
  280. #ifdef HOPTRACE
  281.             if (Hoptrace)
  282.                 log(sp->s,
  283.                     "(hopcheck) ICMP from %s (%ldms) %s %s",
  284.                     inet_ntoa(icsource),
  285.                     cticks,
  286.                     Icmptypes[ictype],
  287.                     ((ictype == ICMP_TIME_EXCEED)?Exceed[iccode]:Unreach[iccode]));
  288. #endif
  289.  
  290.             /* Check type of reply */
  291.             if (ictype == ICMP_TIME_EXCEED)
  292.                 continue;
  293.             /* Reply was: destination unreachable */
  294.             switch(iccode) {
  295.             case ICMP_PORT_UNREACH:
  296.                 ++tracedone;
  297.                 break;
  298.             case ICMP_NET_UNREACH:
  299.                 ++tracedone;
  300.                 tprintf(" !N");
  301.                 break;
  302.             case ICMP_HOST_UNREACH:
  303.                 ++tracedone;
  304.                 tprintf(" !H");
  305.                 break;
  306.             case ICMP_PROT_UNREACH:
  307.                 ++tracedone;
  308.                 tprintf(" !P");
  309.                 break;
  310.             case ICMP_FRAG_NEEDED:
  311.                 ++tracedone;
  312.                 tprintf(" !F");
  313.                 break;
  314.             case ICMP_ROUTE_FAIL:
  315.                 ++tracedone;
  316.                 tprintf(" !S");
  317.                 break;
  318.             }
  319.         }
  320.         /* Done with this round of queries */
  321.         alarm((long)0);
  322.         tprintf("\n");
  323.         /* Check if we reached remote host this round */
  324.         if (tracedone != 0)
  325.             break;
  326.     }
  327.  
  328.     /* Done with traceroute */
  329. done:    close_s(s);
  330.     sp->s = -1;
  331.     close_s(s1);
  332.     tprintf("traceroute done: ");
  333.     if (sndttl >= Hopmaxttl) {
  334.         tprintf("!! maximum TTL exceeded\n");
  335.     } else if ((icsource == rsocket.address)
  336.             &&(iccode == ICMP_PORT_UNREACH)) {
  337.         tprintf("normal (%s %s)\n",
  338.             Icmptypes[ictype],Unreach[iccode]);
  339.     } else {
  340.         tprintf("!! %s %s\n",
  341.             Icmptypes[ictype],Unreach[iccode]);
  342.     }
  343. #ifdef HOPTRACE
  344.     if (Hoptrace)
  345.         log(sp->s,"HOPCHECK to %s done",sp->name);
  346. #endif
  347.     keywait(NULLCHAR,1);
  348.     freesession(sp);
  349.     return 0;
  350. }
  351.  
  352. /* Read raw network socket looking for ICMP messages in response to our
  353.  * UDP probes
  354.  */
  355. static int
  356. geticmp(s,lport,fport,sender,type,code)
  357. int s;
  358. int16 lport;
  359. int16 fport;
  360. int32 *sender;
  361. char *type,*code;
  362. {
  363.     int size;
  364.     struct icmp icmphdr;
  365.     struct ip iphdr;
  366.     struct udp udphdr;
  367.     struct mbuf *bp;
  368.     struct sockaddr_in sock;
  369.  
  370.     for(;;){
  371.         size = sizeof(sock);
  372.         if(recv_mbuf(s,&bp,0,(char *)&sock,&size) == -1)
  373.             return -1;
  374.         /* It's an ICMP message, let's see if it's interesting */
  375.         ntohicmp(&icmphdr,&bp);
  376.         if((icmphdr.type != ICMP_TIME_EXCEED ||
  377.          icmphdr.code != ICMP_TTL_EXCEED)
  378.          && icmphdr.type != ICMP_DEST_UNREACH){
  379.             /* We're not interested in these */
  380.             free_p(bp);
  381.             continue;
  382.         }
  383.         ntohip(&iphdr,&bp);
  384.         if(iphdr.protocol != UDP_PTCL){
  385.             /* Not UDP, so can't be interesting */
  386.             free_p(bp);
  387.             continue;
  388.         }
  389.         ntohudp(&udphdr,&bp);
  390.         if(udphdr.dest != fport || udphdr.source != lport){
  391.             /* Not from our hopcheck session */
  392.             free_p(bp);
  393.             continue;
  394.         }
  395.         /* Passed all of our checks, so return it */
  396.         *sender = sock.sin_addr.s_addr;
  397.         *type = icmphdr.type;
  398.         *code = icmphdr.code;
  399.         free_p(bp);
  400.         return 0;
  401.     }
  402. }
  403.