免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1702 | 回复: 0
打印 上一主题 下一主题

[已解决] ping程序的问题. [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-11-17 19:58 |只看该作者 |倒序浏览
本帖最后由 PCliangtao 于 2010-11-17 20:50 编辑

在看UNP的时候,把书中的ping程序的例子的原理掌握之后,自己尝试写了一下.换汤不换药... 只不过纯粹只于IPv4打交道...
  不知道什么原理. 在发出echo的ICMP之后... 却一直阻塞在recvmsg调用上... 我用tcpdump看...的确发生了ICMP ECHO...
  还有奇怪的一点的所... ping  www.hao123.com能够正常工作... ping www.baidu.com www.goolge.com这样的时候就一直阻塞在recvmsg上...
接触网络变成不久... 希望有经验的兄弟们给瞧瞧... 多谢..

NB: 仔细想了一下... 估计是发送的ICMP 包被远端丢弃了...  仔细看了一下构造ICMP 头部的代码... 汗ing... 校验和的计算参数传错了...没有把ICMP头部算在内...修改了一下...
       就好了...
       可是还有一点奇怪的是... ICMP 对回送请求的应答应该是在内核里进行的吧...  但是为什么www.hao123.com主机能够接受错误的校验和的ICMP ECHO请求包呢?
       翻一翻TCP/IP的源代码去...

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <signal.h>
  6. #include <errno.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #include <netinet/ip.h>
  10. #include <netinet/ip_icmp.h>
  11. #include <netdb.h>

  12. #define BUFSIZE 1024
  13. /* need those global vars, for simplicity */
  14. const char Usage[] = "Usage: ping [-v] host(hostname or hostIP)\n";
  15. static int nsnd;
  16. static int datalen;
  17. static int sockfd;
  18. static int verbose;
  19. static pid_t pid;
  20. static char sndbuf[BUFSIZE];
  21. static struct sockaddr *rcvaddr;
  22. static struct sockaddr *sndaddr;
  23. static socklen_t addrlen;
  24. /* some of the function prototypes */
  25. static void send_echo_icmp(void);
  26. static void recv_echoreply_icmp(char *, int, struct timeval *);
  27. static void sig_alrm(int signo);
  28. static char *show_addr_ip(struct sockaddr *, socklen_t len);
  29. static inline u_int16_t ip_fast_csum(const void *, unsigned int);

  30. static inline void err_quit(const char *str)
  31. {
  32.         perror(str);
  33.         exit(-1);
  34. }
  35. static inline void err_sys(const char *str)
  36. {
  37.         perror(str);
  38. }

  39. int main(int argc, char *argv[])
  40. {

  41.         int c;
  42.         char *host;
  43.         char *hostip;
  44.         char rcvbuf[BUFSIZE], ctlbuf[BUFSIZE];
  45.         struct msghdr msg;
  46.         struct iovec iov;
  47.         struct timeval rcvtv;
  48.         int size, n;
  49.         struct addrinfo *ai, *saved_ai, hint;
  50.        
  51.         while((c = getopt(argc, argv, "v")) != -1) {
  52.                 switch(c) {
  53.                 case 'v':
  54.                         verbose++;
  55.                         break;
  56.                 default:
  57.                         printf("unrecognized option %c\n", c);
  58.                         exit(-1);
  59.                 }
  60.         }
  61.         if(optind !=  argc - 1) {
  62.                 printf(Usage);
  63.                 exit(-1);
  64.         }
  65.         host = argv[optind];
  66.         datalen = 56;
  67.         signal(SIGALRM, sig_alrm);
  68.         pid = getpid() & 0xffff;
  69.         bzero(&hint, sizeof(hint));
  70.         hint.ai_flags = AI_CANONNAME;
  71.         hint.ai_family = AF_INET;
  72.         if(getaddrinfo(host, NULL, &hint, &ai) < 0)
  73.                 err_quit("getaddrinfo error: ");
  74.         saved_ai = ai;    /* don't need to do this actually */
  75.         for(; ai; ai = ai->ai_next)
  76.                 if(ai->ai_family == AF_INET)
  77.                         break;
  78.         if(!ai) {
  79.                 printf("cann't get IPv4 addrinfo\n");
  80.                 exit(-1);
  81.         }
  82.         hostip = show_addr_ip(ai->ai_addr, ai->ai_addrlen);
  83.         printf("ping %s (%s): %d data bytes\n", ai->ai_canonname ? \
  84.                 ai->ai_canonname : hostip, hostip, datalen);
  85.         sndaddr = ai->ai_addr;
  86.         addrlen = ai->ai_addrlen;
  87.         rcvaddr = (struct sockaddr *) malloc(addrlen);
  88.         if(!rcvaddr) {
  89.                 printf("malloc failed, OOM\n");
  90.                 exit(-1);
  91.         }

  92.         /* now we got all the information required, create the RAW socket and get started */
  93.         sockfd = socket(sndaddr->sa_family, SOCK_RAW, IPPROTO_ICMP);
  94.         if(sockfd < 0)
  95.                 err_quit("create socket error: ");
  96.         setuid(getuid()); /* only create RAW sock need privilege */

  97.         size = 60 * 1024;
  98.         setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
  99.         sig_alrm(SIGALRM);
  100.        
  101.         iov.iov_base = rcvbuf;
  102.         iov.iov_len = sizeof(rcvbuf);
  103.         msg.msg_name = rcvaddr;
  104.         msg.msg_namelen = addrlen;
  105.         msg.msg_iov = &iov;
  106.         msg.msg_iovlen = 1;
  107.         msg.msg_control = ctlbuf;
  108.         msg.msg_controllen = sizeof(ctlbuf);
  109.         while(1) {
  110.                 n = recvmsg(sockfd, &msg, 0);
  111.                 if(n < 0) {
  112.                         if(errno == EINTR)
  113.                                 continue;
  114.                         else {
  115.                                 err_sys("recvmsg error: ");
  116.                                 continue;
  117.                         }
  118.                 }
  119.                 gettimeofday(&rcvtv, NULL);
  120.         /*
  121.          * what if this process is slower than recv...maybe we'll get corrupted
  122.          * rcvtv .this should not be a problem, cause the internet is much much
  123.          * slower, respectly.
  124.          */
  125.                 recv_echoreply_icmp(rcvbuf, n, &rcvtv);
  126.         }
  127. }


  128. static void sig_alrm(int signo)
  129. {
  130.         send_echo_icmp();
  131.         alarm(1);
  132.         return;
  133. }


  134. /* send the icmp echo request */
  135. static void send_echo_icmp()
  136. {
  137.         int len;
  138.         struct icmp *icmp;
  139.         icmp = (struct icmp *)sndbuf;
  140.         /* build the icmp header */
  141.         icmp->icmp_type = ICMP_ECHO;
  142.         icmp->icmp_code = 0;
  143.         icmp->icmp_id = pid;
  144.         icmp->icmp_seq = ++nsnd;
  145.         if(datalen & 3) {
  146.                 fprintf(stderr, "datalen must be a multiple of 4!\n");
  147.                 datalen = (datalen + 3) & (~3UL);
  148.         }
  149.         memset(icmp->icmp_data, 0xa5, datalen);
  150.         gettimeofday((struct timeval *)icmp->icmp_data, NULL);
  151.         len = 8 + datalen;
  152.         icmp->icmp_cksum = 0;
  153.         icmp->icmp_cksum = ip_fast_csum(icmp->icmp_data, len >> 2);
  154.         if(sendto(sockfd, sndbuf, len, 0, sndaddr, addrlen) < 0)
  155.                 if(errno != EINTR)
  156.                         err_quit("sendto error: ");
  157. }

  158. static inline void cal_rtt(struct timeval *rcv, struct timeval *snd, double *rtt)
  159. {
  160.         if((rcv->tv_usec -= snd->tv_usec) < 0) {
  161.                 rcv->tv_sec--;
  162.                 rcv->tv_usec += 1000000;
  163.         }
  164.         rcv->tv_sec -= snd->tv_sec;
  165.         *rtt = rcv->tv_sec * 1000.0+ rcv->tv_usec / 1000.0;
  166. }

  167. static void recv_echoreply_icmp(char *rcvbuf, int len, struct timeval *rcv_tv)
  168. {
  169.         int iplen, icmplen;
  170.         double rtt;
  171.         struct ip *ip;
  172.         struct icmp *icmp;
  173.         struct timeval *snd_tv;
  174.         ip = (struct ip *)rcvbuf;
  175.         iplen = ip->ip_hl << 2;
  176.         if(ip->ip_p != IPPROTO_ICMP)
  177.                 goto ignore;
  178.         icmp = (struct icmp *)(rcvbuf + iplen);
  179.         icmplen = len - iplen;
  180.         if(icmplen < 8)
  181.                 goto ignore;
  182.         if(icmp->icmp_type == ICMP_ECHOREPLY) {
  183.                 if(icmp->icmp_id != pid)
  184.                         goto ignore;
  185.                 if(icmplen < 8+sizeof(struct timeval))
  186.                         goto ignore;
  187.                 snd_tv = (struct timeval *)icmp->icmp_data;
  188.                 cal_rtt(rcv_tv, snd_tv, &rtt);

  189.                 printf("%d bytes from %s: seq=%d, ttl=%d, rtt=%.3f ms\n", \
  190.                 icmplen, show_addr_ip(rcvaddr, addrlen), icmp->icmp_seq, \
  191.                         ip->ip_ttl, rtt);
  192.         } else if(verbose) {
  193.                 printf("%d bytes from %s: type=%d, code=%d\n", icmplen, \
  194.                 show_addr_ip(rcvaddr, addrlen), icmp->icmp_type,\
  195.                  icmp->icmp_code);
  196.         }
  197. ignore:
  198.         return;       
  199. }

  200. /* it's not safe, though */
  201. static char *show_addr_ip(struct sockaddr *sa, socklen_t len)
  202. {
  203.         static char ipbuf[INET6_ADDRSTRLEN];
  204.         switch(sa->sa_family) {
  205.         case AF_INET: {
  206.                 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
  207.                 if(len < sizeof(struct sockaddr_in))
  208.                         return NULL;
  209.                 if(inet_ntop(AF_INET, &sin->sin_addr, ipbuf,\
  210.                                                  INET6_ADDRSTRLEN) < 0)
  211.                         return NULL;
  212.                 return ipbuf;
  213.                 break;
  214.                 }
  215.         default:
  216.                 printf("don't support other addr type\n");
  217.                 return NULL;
  218.         }
  219. }

  220. /*
  221. *        This is a version of ip_compute_csum() optimized for IP headers,
  222. *        which always checksum on 4 octet boundaries.
  223. *
  224. *        By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
  225. *        Arnt Gulbrandsen.
  226. */
  227. static inline u_int16_t ip_fast_csum(const void *iph, unsigned int ihl)
  228. {
  229.         unsigned int sum;

  230.         asm volatile("movl (%1), %0        ;\n"
  231.                      "subl $4, %2        ;\n"
  232.                      "jbe 2f                ;\n"
  233.                      "addl 4(%1), %0        ;\n"
  234.                      "adcl 8(%1), %0        ;\n"
  235.                      "adcl 12(%1), %0;\n"
  236.                      "1:        adcl 16(%1), %0        ;\n"
  237.                      "lea 4(%1), %1        ;\n"
  238.                      "decl %2        ;\n"
  239.                      "jne 1b                ;\n"
  240.                      "adcl $0, %0        ;\n"
  241.                      "movl %0, %2        ;\n"
  242.                      "shrl $16, %0        ;\n"
  243.                      "addw %w2, %w0        ;\n"
  244.                      "adcl $0, %0        ;\n"
  245.                      "notl %0        ;\n"
  246.                      "2:                ;\n"
  247.         /* Since the input registers which are loaded with iph and ihl
  248.            are modified, we must also specify them as outputs, or gcc
  249.            will assume they contain their original values. */
  250.                      : "=r" (sum), "=r" (iph), "=r" (ihl)
  251.                      : "1" (iph), "2" (ihl)
  252.                      : "memory");
  253.         return (u_int16_t)sum;
  254. }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP