pingLib.c
上传用户:nvosite88
上传日期:2007-01-17
资源大小:4983k
文件大小:18k
源码类别:

VxWorks

开发平台:

C/C++

  1. /* pingLib.c - Packet InterNet Groper (PING) library */
  2. /* Copyright 1994 - 2002 Wind River Systems, Inc. */
  3. #include "copyright_wrs.h"
  4. /*
  5. modification history
  6. --------------------
  7. 01r,22apr02,rae  Note that PING_OPT_DONTROUTE affects pinging localhost
  8.                  (SPR #72917), other minor changes
  9. 01q,11mar02,rae  Print stats when task killed (SPR #73570)
  10. 01p,08jan02,rae  Don't print error messages when PING_OPT_SILENT (SPR #69537)
  11. 01o,15oct01,rae  merge from truestack ver 01q, base 01k (SPRs 67440,
  12.                  30151, 66062, etc.)
  13. 01n,30oct00,gnn  Added PING_OPT_NOHOST flag to deal with SPR 22766
  14. 01m,08nov99,pul  T2 cumulative patch 2
  15. 01l,22sep99,cno  corrected ping error for pingRxPrint() (SPR22571)
  16. 01k,14mar99,jdi  doc: removed refs to config.h and/or configAll.h (SPR 25663).
  17. 01j,12mar99,p_m  Fixed SPR 8742 by documentating ping() configuration global
  18.                  variables.
  19. 01i,05feb99,dgp  document errno values
  20. 01h,17mar98,jmb  merge jmb patch of 04apr97 from HPSIM: corrected
  21.                  creation/deletion of task delete hook.
  22. 01g,30oct97,cth  changed stack size of tPingTxn from 3000 to 6000 (SPR 8222).
  23. 01f,26aug97,spm  removed compiler warnings (SPR #7866)
  24. 01e,30sep96,spm  corrected ping error for little-endian machines (SPR #4235)
  25. 01d,13mar95,dzb  changed to use free() instead of cfree() (SPR #4113)
  26. 01c,24jan95,jdi  doc tweaks
  27. 01b,10nov94,rhp  minor edits to man pages
  28. 01a,25oct94,dzb  written
  29. */
  30. /*
  31. DESCRIPTION
  32. This library contains the ping() utility, which tests the reachability
  33. of a remote host.
  34. The routine ping() is typically called from the VxWorks shell to check the
  35. network connection to another VxWorks target or to a UNIX host.  ping()
  36. may also be used programmatically by applications that require such a test.
  37. The remote host must be running TCP/IP networking code that responds to
  38. ICMP echo request packets.  The ping() routine is re-entrant, thus may
  39. be called by many tasks concurrently.
  40. The routine pingLibInit() initializes the ping() utility and allocates
  41. resources used by this library.  It is called automatically when
  42. INCLUDE_PING is defined.
  43. */
  44. /* includes */
  45. #include "vxWorks.h"
  46. #include "string.h"
  47. #include "stdioLib.h"
  48. #include "wdLib.h"
  49. #include "netLib.h"
  50. #include "sockLib.h"
  51. #include "inetLib.h"
  52. #include "semLib.h"
  53. #include "taskLib.h"
  54. #include "hostLib.h"
  55. #include "ioLib.h"
  56. #include "tickLib.h"
  57. #include "taskHookLib.h"
  58. #include "sysLib.h"
  59. #include "vxLib.h"
  60. #include "netinet/in_systm.h"
  61. #include "netinet/ip.h"
  62. #include "netinet/ip_icmp.h"
  63. #include "netinet/icmp_var.h"
  64. #include "pingLib.h"
  65. #include "errnoLib.h"
  66. #include "kernelLib.h"
  67. #ifdef VIRTUAL_STACK
  68. #include "netinet/vsLib.h"
  69. #endif /* VIRTUAL_STACK */
  70. /* defines */
  71. #define pingError(pPS) { pPS->flags |= PING_OPT_SILENT; goto release; }
  72. /* globals */
  73. int _pingTxLen = 64; /* size of icmp echo packet */
  74. int _pingTxInterval = PING_INTERVAL; /* packet interval in seconds */
  75. int _pingTxTmo = PING_TMO; /* packet timeout in seconds */
  76. extern int errno;
  77. /* locals */
  78. LOCAL PING_STAT * pingHead = NULL; /* ping list head */
  79. LOCAL SEM_ID pingSem = NULL; /* mutex for list access */
  80. /* static forward declarations */
  81. LOCAL STATUS pingRxPrint (PING_STAT *pPS, int len, struct sockaddr_in *from,
  82.                           ulong_t now);
  83. LOCAL void pingFinish (WIND_TCB *);
  84. /*******************************************************************************
  85. *
  86. * pingLibInit - initialize the ping() utility
  87. *
  88. * This routine allocates resources used by the ping() utility.
  89. * It is called automatically when INCLUDE_PING is defined.
  90. *
  91. * RETURNS:
  92. * OK
  93. */
  94. STATUS pingLibInit (void)
  95.     {
  96.     if (pingSem == NULL) /* already initialized ? */
  97. {
  98. if ((pingSem = semMCreate (SEM_Q_PRIORITY | SEM_DELETE_SAFE |
  99.     SEM_INVERSION_SAFE)) == NULL)
  100.     return (ERROR);
  101.         }
  102.     return (OK);
  103.     }
  104. /*******************************************************************************
  105. *
  106. * ping - test that a remote host is reachable
  107. *
  108. * This routine tests that a remote host is reachable by sending ICMP
  109. * echo request packets, and waiting for replies.  It may called from
  110. * the VxWorks shell as follows:
  111. * .CS
  112. *    -> ping "remoteSystem", 1, 0
  113. * .CE
  114. * where <remoteSystem> is either a host name that has been previously added
  115. * to the remote host table by a call to hostAdd(), or an Internet address in
  116. * dot notation (for example, "90.0.0.2").
  117. *
  118. * The second parameter, <numPackets>, specifies the number of ICMP packets
  119. * to receive from the remote host.  If <numPackets> is 1, this routine waits
  120. * for a single echo reply packet, and then prints a short message
  121. * indicating whether the remote host is reachable.  For all other values
  122. * of <numPackets>, timing and sequence information is printed as echoed
  123. * packets are received.  If <numPackets> is 0, this routine runs continuously.
  124. * If no replies are received within a 5-second timeout period, the
  125. * routine exits.  An ERROR status is returned if no echo replies
  126. * are received from the remote host.
  127. *
  128. * The following flags may be given through the <options> parameter:
  129. * .iP PING_OPT_SILENT
  130. * Suppress output.  This option is useful for applications that 
  131. * use ping() programmatically to examine the return status.
  132. * .iP PING_OPT_DONTROUTE
  133. * Do not route packets past the local network.  This also prevents pinging
  134. * local addresses (i.e. the IP address of the host itself).  The 127.x.x.x 
  135. * addresses will still work however.
  136. * .iP PING_OPT_NOHOST
  137. * Suppress host lookup.  This is useful when you have the DNS resolver
  138. * but the DNS server is down and not returning host names.
  139. * .iP PING_OPT_DEBUG
  140. * Enables debug output.
  141. * .RS 4 4
  142. * &NOTE: The following global variables can be set from the target shell
  143. * or Windsh to configure the ping() parameters:
  144. * .iP _pingTxLen 
  145. * Size of the ICMP echo packet (default 64).
  146. * .iP _pingTxInterval
  147. * Packet interval in seconds (default 1 second).
  148. * .iP _pingTxTmo
  149. * Packet timeout in seconds (default 5 seconds).
  150. *
  151. *.RE
  152. * RETURNS:
  153. * OK, or ERROR if the remote host is not reachable.
  154. *
  155. * ERRNO: EINVAL, S_pingLib_NOT_INITIALIZED, S_pingLib_TIMEOUT
  156. *
  157. */
  158. STATUS ping
  159.     (
  160.     char * host, /* host to ping */
  161.     int numPackets, /* number of packets to receive */
  162.     ulong_t options /* option flags */
  163.     )
  164.     {
  165.     PING_STAT * pPS; /* current ping stat struct */
  166.     struct sockaddr_in to; /* addr of Tx packet */
  167.     struct sockaddr_in from; /* addr of Rx packet */
  168.     int fromlen = sizeof (from);/* size of Rx addr */
  169.     int ix = 1; /* bytes read */
  170.     int txInterval; /* packet interval in ticks */
  171.     STATUS status = ERROR; /* return status */
  172.     struct fd_set readFd;
  173.     struct timeval pingTmo;
  174.     ulong_t now;
  175.     int sel;
  176. #ifndef ALT_PING_PAR_SEMANTIC
  177.     if (numPackets < 0)                         /* numPackets positive ? */
  178.         {
  179.         errno = EINVAL;
  180.         return (ERROR);
  181.         }
  182. #else /* alternative parameter semantic for numPackets */
  183.     if (numPackets < -1) /* numPackets positive or -1 ? */
  184. {
  185. errno = EINVAL;
  186. return (ERROR);
  187. }
  188.     if (numPackets == 0)
  189.      numPackets = 3; /* don't do infinite by default */
  190.     if (numPackets == -1)
  191.      numPackets = 0; /* infinite */
  192. #endif /* ALT_PING_PAR_SEMANTIC */
  193.     /* allocate size for ping statistics/info structure */
  194.     if ((pPS = (PING_STAT *) calloc (1, sizeof (PING_STAT))) == NULL)
  195. return (ERROR);
  196. #ifdef VIRTUAL_STACK
  197.     virtualStackIdCheck();
  198.     pPS->vsid = myStackNum;
  199. #endif /* VIRTUAL_STACK */
  200.     semTake (pingSem, WAIT_FOREVER); /* get access to list */
  201.     pPS->statNext = pingHead; /* push session onto list */
  202.     if (pingHead == NULL && !(options & PING_OPT_SILENT))
  203.         if (taskDeleteHookAdd ((FUNCPTR) pingFinish) == ERROR)
  204.     {
  205.     free ((char *) pPS);
  206.     semGive (pingSem); /* give up access to list */
  207.             if (options & PING_OPT_DEBUG)
  208.              printf ("ping: unable to add task Delete hookn");
  209.     return (ERROR);
  210.     }
  211.     pingHead = pPS;
  212.     semGive (pingSem); /* give up access to list */
  213.     pPS->tMin = 999999999; /* init min rt time */
  214.     pPS->numPacket = numPackets; /* save num to send */
  215.     pPS->flags = options; /* save flags field */
  216.     pPS->clkTick = sysClkRateGet (); /* save sys clk rate */
  217.     txInterval = _pingTxInterval * pPS->clkTick;/* init interval value */
  218.     pingTmo.tv_sec = _pingTxTmo;
  219.     pingTmo.tv_usec = 0;
  220.     pPS->pBufIcmp = (struct icmp *) pPS->bufTx; /* pointer to icmp header out */
  221.     pPS->pBufTime = (ulong_t *) (pPS->bufTx + 8);/* pointer to time out */
  222.     pPS->idRx = taskIdSelf (); /* get own task Id  */
  223.     /* initialize the socket address struct */
  224.     to.sin_family = AF_INET;
  225.     if ((to.sin_addr.s_addr = inet_addr (host)) == (u_long) ERROR)
  226. {
  227. if ((to.sin_addr.s_addr = hostGetByName (host)) == (u_long) ERROR)
  228.     {
  229.             if (!(options & PING_OPT_SILENT))
  230.         printf ("ping: unknown host %sn", host);
  231.     pingError (pPS);
  232.     }
  233. inet_ntoa_b (to.sin_addr, pPS->toInetName);
  234.         }
  235.     
  236.     strcpy (pPS->toHostName, host);    /* save host name */
  237.     _pingTxLen = max (_pingTxLen, PING_MINPACKET); /* sanity check global */
  238.     _pingTxLen = min (_pingTxLen, PING_MAXPACKET); /* sanity check global */
  239.     pPS->dataLen = _pingTxLen - 8;    /* compute size of data */
  240.     /* open raw socket for ICMP communication */
  241.     if ((pPS->pingFd = socket (AF_INET, SOCK_RAW, ICMP_PROTO)) < 0)
  242. pingError (pPS);
  243.     if (options & PING_OPT_DONTROUTE) /* disallow packet routing ? */
  244.         if (setsockopt (pPS->pingFd, SOL_SOCKET, SO_DONTROUTE, (char *) &ix,
  245.     sizeof (ix)) == ERROR)
  246.     pingError (pPS);
  247.     if (!(options & PING_OPT_SILENT) && pPS->numPacket != 1)
  248. {
  249.         printf ("PING %s", pPS->toHostName); /* print out dest info */
  250.         if (pPS->toInetName[0])
  251.             printf (" (%s)", pPS->toInetName);
  252.         printf (": %d data bytesn", pPS->dataLen);
  253. }
  254.     
  255.     pPS->pBufIcmp->icmp_type = ICMP_ECHO; /* set up Tx buffer */
  256.     pPS->pBufIcmp->icmp_code = 0;
  257.     pPS->pBufIcmp->icmp_id = pPS->idRx & 0xffff;
  258.     for (ix = 4; ix < pPS->dataLen; ix++) /* skip 4 bytes for time */
  259.         pPS->bufTx [8 + ix] = ix;
  260.      /* receive echo reply packets from remote host */
  261.     while (!pPS->numPacket || (pPS->numRx != pPS->numPacket))
  262. {
  263. *pPS->pBufTime = tickGet (); /* load current tick count */
  264.         pPS->pBufIcmp->icmp_seq = pPS->numTx++; /* increment seq number */
  265.         pPS->pBufIcmp->icmp_cksum = 0;
  266. pPS->pBufIcmp->icmp_cksum = checksum ((u_short *) pPS->pBufIcmp,
  267.       _pingTxLen);
  268.         /* transmit ICMP packet */
  269. if ((ix = sendto (pPS->pingFd, (char *) pPS->pBufIcmp,
  270.   _pingTxLen, 0, (struct sockaddr *)&to,
  271.   sizeof (struct sockaddr))) != _pingTxLen)
  272.     if (pPS->flags & PING_OPT_DEBUG)
  273.         printf ("ping: wrote %s %d chars, ret=%dn", pPS->toHostName,
  274.     _pingTxLen, ix);
  275.         /* Update ICMP statistics for ECHO messages - this is not the best
  276.  * place to put it since this shifts responsibility of updating ECHO
  277.  * statistics to anyone writing an application that sends out ECHO
  278.  * messages. However, this seems to be the only place to put it.
  279.          */ 
  280. #ifdef VIRTUAL_STACK
  281.         _icmpstat.icps_outhist [ICMP_ECHO]++;
  282. #else
  283.         icmpstat.icps_outhist [ICMP_ECHO]++;
  284. #endif /* VIRTUAL_STACK */
  285. check_fd_again: /* Wait for ICMP reply */
  286. FD_ZERO(&readFd);
  287. FD_SET(pPS->pingFd, &readFd);
  288. sel = select (pPS->pingFd+1, &readFd, NULL, NULL, &pingTmo);
  289.         if (sel == ERROR)
  290.     {
  291.     errno = errnoGet();
  292.     if (!(options & PING_OPT_SILENT))
  293.                 printf ("ping: ERRORn");
  294.     break; /* goto release */
  295.     }
  296. else if (sel == 0)
  297.     {
  298.     if (!(options & PING_OPT_SILENT))
  299.                 printf ("ping: timeoutn");
  300.             errno = S_pingLib_TIMEOUT; /* timeout error */
  301.     break; /* goto release */
  302.     }
  303. if (!FD_ISSET(pPS->pingFd, &readFd))
  304.             goto check_fd_again;
  305. /* the fd is ready - FD_ISSET isn't needed */
  306. if ((ix = recvfrom (pPS->pingFd, (char *) pPS->bufRx, PING_MAXPACKET,
  307.     0, (struct sockaddr *) &from, &fromlen)) == ERROR)
  308.             {
  309.     if (errno == EINTR)
  310.         goto check_fd_again;
  311.     break; /* goto release */
  312.     }
  313. now = tickGet();
  314. if (pingRxPrint (pPS, ix, &from, now) == ERROR)
  315.     goto check_fd_again;
  316. taskDelay (txInterval);
  317.         }
  318.     if (pPS->numRx > 0)
  319. status = OK; /* host is reachable */
  320. release:
  321.     pingFinish (taskTcb (pPS->idRx));
  322.     return (status);
  323.     }
  324. /*******************************************************************************
  325. *
  326. * pingRxPrint - print out information about a received packet
  327. *
  328. * This routine prints out information about a received ICMP echo reply
  329. * packet.  First, the packet is checked for minimum length and
  330. * correct message type and destination.
  331. *
  332. * RETURNS:
  333. * N/A.
  334. */
  335. LOCAL STATUS pingRxPrint
  336.     (
  337.     PING_STAT * pPS, /* ping stats structure */
  338.     int len, /* Rx message length */
  339.     struct sockaddr_in *from, /* Rx message address */
  340.     ulong_t now
  341.     )
  342.     {
  343.     struct ip * ip = (struct ip *) pPS->bufRx;
  344.     long * lp = (long *) pPS->bufRx;
  345.     struct icmp * icp;
  346.     int ix;
  347.     int hlen;
  348.     int triptime;
  349.     char fromHostName [MAXHOSTNAMELEN + 1];
  350.     char fromInetName [INET_ADDR_LEN];
  351.     
  352.     /* convert address notation */
  353.     inet_ntoa_b (from->sin_addr, fromInetName);
  354.     /* Do we lookup hosts or not? */
  355.     if (pPS->flags & PING_OPT_NOHOST)
  356.         *fromHostName = EOS;
  357.     else
  358.         {
  359.         if ((hostGetByAddr (from->sin_addr.s_addr, fromHostName)) == ERROR)
  360.             *fromHostName = EOS; /* hostname not found */
  361.         }
  362.     
  363.     hlen = ip->ip_hl << 2;
  364.     if (len < hlen + ICMP_MINLEN) /* at least min length ? */
  365. {
  366. if (pPS->flags & PING_OPT_DEBUG)
  367.     printf ("packet too short (%d bytes) from %sn", len,
  368. fromInetName);
  369. return (ERROR);
  370.         }
  371.     len -= hlen; /* strip IP header */
  372.     icp = (struct icmp *) (pPS->bufRx + hlen);
  373.     if (icp->icmp_type != ICMP_ECHOREPLY) /* right message ? */
  374. {
  375. if (pPS->flags & PING_OPT_DEBUG) /* debug odd message */
  376.     {
  377.     if (*fromHostName != (char)NULL)
  378.                 printf ("%d bytes from %s (%s): ", len, fromHostName,
  379.     fromInetName);
  380.     else 
  381.         printf ("%d bytes from %s: ", len, fromInetName);
  382.     icp->icmp_type = min (icp->icmp_type, ICMP_TYPENUM); 
  383.     printf ("icmp_type=%dn", icp->icmp_type);
  384.     for(ix = 0; ix < 12; ix++)
  385.         printf ("x%2.2lx: x%8.8lxn", (ULONG)(ix * sizeof (long)),
  386.                         (ULONG)*lp++);
  387.     printf ("icmp_code=%dn", icp->icmp_code);
  388.     }
  389. return (ERROR);
  390.         }
  391.     /* check if the received reply is ours. */
  392.     if (icp->icmp_id != (pPS->idRx & 0xffff))
  393. {
  394. return (ERROR); /* wasn't our ECHO */
  395. }
  396.     /* print out Rx packet stats */
  397.     if (!(pPS->flags & PING_OPT_SILENT) && pPS->numPacket != 1)
  398. {
  399. if (*fromHostName != (char)NULL)
  400.             printf ("%d bytes from %s (%s): ", len, fromHostName, fromInetName);
  401. else 
  402.             printf ("%d bytes from %s: ", len, fromInetName);
  403.         printf ("icmp_seq=%d. ", icp->icmp_seq);
  404.         triptime = (now - *((ulong_t *) icp->icmp_data)) *
  405.     (1000 / pPS->clkTick);
  406.         printf ("time=%d. msn", triptime);
  407.         pPS->tSum += triptime;
  408.         pPS->tMin = min (pPS->tMin, triptime);
  409.         pPS->tMax = max (pPS->tMax, triptime);
  410. }
  411.     pPS->numRx++;
  412.     return (OK);
  413.     }
  414. /*******************************************************************************
  415. *
  416. * pingFinish - return all allocated resources and print out final statistics
  417. *
  418. * This routine returns all resources allocated for the ping session, and
  419. * prints out a stats summary.
  420. *
  421. * The ping session is located in the session list (pingHead) by searching
  422. * the ping stats structure for the receiver task ID.  This is necessary
  423. * because this routine is passed a pointer to the task control block, and does
  424. * not have ready access to the ping stats structure itself.  This accomodates
  425. * the use of task delete hooks as a means of calling this routine.
  426. *
  427. * RETURNS:
  428. * N/A.
  429. */
  430. LOCAL void pingFinish
  431.     (
  432.     WIND_TCB * pTcb /* pointer to task control block */
  433.     )
  434.     {
  435.     PING_STAT * pPS; /* ping stats structure */
  436.     PING_STAT ** ppPrev; /* pointer to prev statNext */
  437.     semTake (pingSem, WAIT_FOREVER); /* get list access */
  438.     ppPrev = &pingHead;
  439.     for (pPS = pingHead; (pPS != NULL) && (taskTcb (pPS->idRx) != pTcb);
  440.         pPS = pPS->statNext) /* find session in list */
  441.         ppPrev = &pPS->statNext;
  442.     if (pPS == NULL) /* session found ? */
  443. {
  444.         semGive (pingSem); /* give up list access */
  445. return;
  446. }
  447.     *ppPrev = pPS->statNext; /* pop session off list */
  448.     if (pingHead == NULL)
  449.         (void) taskDeleteHookDelete ((FUNCPTR) pingFinish); 
  450.     semGive (pingSem); /* give up list access */
  451.     /* return all allocated/created resources */
  452. #ifdef VIRTUAL_STACK
  453.     if (!(pPS->flags & PING_OPT_SILENT) &&
  454.         virtualStackNumTaskIdSet(pPS->vsid) == ERROR)
  455.         {
  456.         printf("pingFinish: invalid virtual stack idn");
  457.         return;
  458.         }
  459. #endif /* VIRTUAL_STACK */
  460.     if (pPS->pingFd)
  461.         (void) close (pPS->pingFd);
  462.     if (!(pPS->flags & PING_OPT_SILENT)) /* print final report ? */
  463. {
  464.         if (pPS->numRx) /* received at least one ? */
  465.     {
  466.     if (pPS->numPacket != 1) /* full report */
  467. {
  468.                 printf ("----%s PING Statistics----n", pPS->toHostName);
  469.                 printf ("%d packets transmitted, ", pPS->numTx);
  470.                 printf ("%d packets received, ", pPS->numRx);
  471.                 if (pPS->numTx)
  472.                     printf ("%d%% packet loss", ((pPS->numTx - pPS->numRx) *
  473. 100) / pPS->numTx);
  474.                 printf ("n");
  475.                 if (pPS->numRx)
  476.                     printf ("round-trip (ms)  min/avg/max = %d/%d/%dn",
  477. pPS->tMin, pPS->tSum / pPS->numRx, pPS->tMax);
  478.                 }
  479.             else /* short report */
  480.         printf ("%s is aliven", pPS->toHostName);
  481.             }
  482.         else
  483.     printf ("no answer from %sn", pPS->toHostName);
  484. }
  485.     free ((char *)pPS); /* free stats memory space */
  486.     }