- 论坛徽章:
- 0
|
本帖最后由 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的源代码去...
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <signal.h>
- #include <errno.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <netinet/ip.h>
- #include <netinet/ip_icmp.h>
- #include <netdb.h>
- #define BUFSIZE 1024
- /* need those global vars, for simplicity */
- const char Usage[] = "Usage: ping [-v] host(hostname or hostIP)\n";
- static int nsnd;
- static int datalen;
- static int sockfd;
- static int verbose;
- static pid_t pid;
- static char sndbuf[BUFSIZE];
- static struct sockaddr *rcvaddr;
- static struct sockaddr *sndaddr;
- static socklen_t addrlen;
- /* some of the function prototypes */
- static void send_echo_icmp(void);
- static void recv_echoreply_icmp(char *, int, struct timeval *);
- static void sig_alrm(int signo);
- static char *show_addr_ip(struct sockaddr *, socklen_t len);
- static inline u_int16_t ip_fast_csum(const void *, unsigned int);
- static inline void err_quit(const char *str)
- {
- perror(str);
- exit(-1);
- }
- static inline void err_sys(const char *str)
- {
- perror(str);
- }
- int main(int argc, char *argv[])
- {
- int c;
- char *host;
- char *hostip;
- char rcvbuf[BUFSIZE], ctlbuf[BUFSIZE];
- struct msghdr msg;
- struct iovec iov;
- struct timeval rcvtv;
- int size, n;
- struct addrinfo *ai, *saved_ai, hint;
-
- while((c = getopt(argc, argv, "v")) != -1) {
- switch(c) {
- case 'v':
- verbose++;
- break;
- default:
- printf("unrecognized option %c\n", c);
- exit(-1);
- }
- }
- if(optind != argc - 1) {
- printf(Usage);
- exit(-1);
- }
- host = argv[optind];
- datalen = 56;
- signal(SIGALRM, sig_alrm);
- pid = getpid() & 0xffff;
- bzero(&hint, sizeof(hint));
- hint.ai_flags = AI_CANONNAME;
- hint.ai_family = AF_INET;
- if(getaddrinfo(host, NULL, &hint, &ai) < 0)
- err_quit("getaddrinfo error: ");
- saved_ai = ai; /* don't need to do this actually */
- for(; ai; ai = ai->ai_next)
- if(ai->ai_family == AF_INET)
- break;
- if(!ai) {
- printf("cann't get IPv4 addrinfo\n");
- exit(-1);
- }
- hostip = show_addr_ip(ai->ai_addr, ai->ai_addrlen);
- printf("ping %s (%s): %d data bytes\n", ai->ai_canonname ? \
- ai->ai_canonname : hostip, hostip, datalen);
- sndaddr = ai->ai_addr;
- addrlen = ai->ai_addrlen;
- rcvaddr = (struct sockaddr *) malloc(addrlen);
- if(!rcvaddr) {
- printf("malloc failed, OOM\n");
- exit(-1);
- }
- /* now we got all the information required, create the RAW socket and get started */
- sockfd = socket(sndaddr->sa_family, SOCK_RAW, IPPROTO_ICMP);
- if(sockfd < 0)
- err_quit("create socket error: ");
- setuid(getuid()); /* only create RAW sock need privilege */
- size = 60 * 1024;
- setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
- sig_alrm(SIGALRM);
-
- iov.iov_base = rcvbuf;
- iov.iov_len = sizeof(rcvbuf);
- msg.msg_name = rcvaddr;
- msg.msg_namelen = addrlen;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = ctlbuf;
- msg.msg_controllen = sizeof(ctlbuf);
- while(1) {
- n = recvmsg(sockfd, &msg, 0);
- if(n < 0) {
- if(errno == EINTR)
- continue;
- else {
- err_sys("recvmsg error: ");
- continue;
- }
- }
- gettimeofday(&rcvtv, NULL);
- /*
- * what if this process is slower than recv...maybe we'll get corrupted
- * rcvtv .this should not be a problem, cause the internet is much much
- * slower, respectly.
- */
- recv_echoreply_icmp(rcvbuf, n, &rcvtv);
- }
- }
- static void sig_alrm(int signo)
- {
- send_echo_icmp();
- alarm(1);
- return;
- }
- /* send the icmp echo request */
- static void send_echo_icmp()
- {
- int len;
- struct icmp *icmp;
- icmp = (struct icmp *)sndbuf;
- /* build the icmp header */
- icmp->icmp_type = ICMP_ECHO;
- icmp->icmp_code = 0;
- icmp->icmp_id = pid;
- icmp->icmp_seq = ++nsnd;
- if(datalen & 3) {
- fprintf(stderr, "datalen must be a multiple of 4!\n");
- datalen = (datalen + 3) & (~3UL);
- }
- memset(icmp->icmp_data, 0xa5, datalen);
- gettimeofday((struct timeval *)icmp->icmp_data, NULL);
- len = 8 + datalen;
- icmp->icmp_cksum = 0;
- icmp->icmp_cksum = ip_fast_csum(icmp->icmp_data, len >> 2);
- if(sendto(sockfd, sndbuf, len, 0, sndaddr, addrlen) < 0)
- if(errno != EINTR)
- err_quit("sendto error: ");
- }
- static inline void cal_rtt(struct timeval *rcv, struct timeval *snd, double *rtt)
- {
- if((rcv->tv_usec -= snd->tv_usec) < 0) {
- rcv->tv_sec--;
- rcv->tv_usec += 1000000;
- }
- rcv->tv_sec -= snd->tv_sec;
- *rtt = rcv->tv_sec * 1000.0+ rcv->tv_usec / 1000.0;
- }
- static void recv_echoreply_icmp(char *rcvbuf, int len, struct timeval *rcv_tv)
- {
- int iplen, icmplen;
- double rtt;
- struct ip *ip;
- struct icmp *icmp;
- struct timeval *snd_tv;
- ip = (struct ip *)rcvbuf;
- iplen = ip->ip_hl << 2;
- if(ip->ip_p != IPPROTO_ICMP)
- goto ignore;
- icmp = (struct icmp *)(rcvbuf + iplen);
- icmplen = len - iplen;
- if(icmplen < 8)
- goto ignore;
- if(icmp->icmp_type == ICMP_ECHOREPLY) {
- if(icmp->icmp_id != pid)
- goto ignore;
- if(icmplen < 8+sizeof(struct timeval))
- goto ignore;
- snd_tv = (struct timeval *)icmp->icmp_data;
- cal_rtt(rcv_tv, snd_tv, &rtt);
- printf("%d bytes from %s: seq=%d, ttl=%d, rtt=%.3f ms\n", \
- icmplen, show_addr_ip(rcvaddr, addrlen), icmp->icmp_seq, \
- ip->ip_ttl, rtt);
- } else if(verbose) {
- printf("%d bytes from %s: type=%d, code=%d\n", icmplen, \
- show_addr_ip(rcvaddr, addrlen), icmp->icmp_type,\
- icmp->icmp_code);
- }
- ignore:
- return;
- }
- /* it's not safe, though */
- static char *show_addr_ip(struct sockaddr *sa, socklen_t len)
- {
- static char ipbuf[INET6_ADDRSTRLEN];
- switch(sa->sa_family) {
- case AF_INET: {
- struct sockaddr_in *sin = (struct sockaddr_in *)sa;
- if(len < sizeof(struct sockaddr_in))
- return NULL;
- if(inet_ntop(AF_INET, &sin->sin_addr, ipbuf,\
- INET6_ADDRSTRLEN) < 0)
- return NULL;
- return ipbuf;
- break;
- }
- default:
- printf("don't support other addr type\n");
- return NULL;
- }
- }
- /*
- * This is a version of ip_compute_csum() optimized for IP headers,
- * which always checksum on 4 octet boundaries.
- *
- * By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
- * Arnt Gulbrandsen.
- */
- static inline u_int16_t ip_fast_csum(const void *iph, unsigned int ihl)
- {
- unsigned int sum;
- asm volatile("movl (%1), %0 ;\n"
- "subl $4, %2 ;\n"
- "jbe 2f ;\n"
- "addl 4(%1), %0 ;\n"
- "adcl 8(%1), %0 ;\n"
- "adcl 12(%1), %0;\n"
- "1: adcl 16(%1), %0 ;\n"
- "lea 4(%1), %1 ;\n"
- "decl %2 ;\n"
- "jne 1b ;\n"
- "adcl $0, %0 ;\n"
- "movl %0, %2 ;\n"
- "shrl $16, %0 ;\n"
- "addw %w2, %w0 ;\n"
- "adcl $0, %0 ;\n"
- "notl %0 ;\n"
- "2: ;\n"
- /* Since the input registers which are loaded with iph and ihl
- are modified, we must also specify them as outputs, or gcc
- will assume they contain their original values. */
- : "=r" (sum), "=r" (iph), "=r" (ihl)
- : "1" (iph), "2" (ihl)
- : "memory");
- return (u_int16_t)sum;
- }
复制代码 |
|