先决条件:ICMP |原始插座|互联网校验和|域名解析
Ping是Internet调试的必要条件。
Ping是一种基本的Internet工具,它使用户可以使用其他工具来验证特定IP地址是否存在并可以接受请求。
Ping通过打开RAW套接字发送ICMP数据包,该套接字与TCP和UDP分开。
由于IP没有任何内置的机制来发送错误和控制消息。提供错误控制取决于Internet控制消息协议(ICMP)。它用于报告错误和管理查询。
Ubuntu Ping示例
ping www.google.com
PING www.google.com (172.217.194.105) 56(84) bytes of data.
64 bytes from 172.217.194.105 (172.217.194.105): icmp_seq=1 ttl=46 time=116 ms
64 bytes from 172.217.194.105 (172.217.194.105): icmp_seq=2 ttl=46 time=102 ms
64 bytes from 172.217.194.105 (172.217.194.105): icmp_seq=3 ttl=46 time=119 ms
^C
--- www.google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 3110ms
工作机制
Internet Ping程序的工作原理类似于声纳回声定位,将包含ICMP ECHO_REQUEST的一小包信息发送到指定的计算机,然后由该计算机发送ECHO_REPLY包作为回报。
数据包具有TTL(生存时间)值,该值确定路由器跃点的最大数量。
如果数据包没有到达,则发回给发件人并显示错误信息。
错误有以下几种类型:
- TTL在运输中过期
- 目标主机不可达
- 请求超时,即无回复
- 未知主机
执行
一个简单的ping程序遵循的步骤是:
- 以主机名作为输入
- 进行DNS查找
可以使用gethostbyname()完成DNS查找。 gethostbyname()函数转换普通的人类可读网站,并返回hostent类型的结构,该结构包含二进制点符号形式的IP地址以及地址类型。
- 一些ping程序(如ubuntu附带的ping程序)支持反向DNS查找。
反向DNS查找使用getnameinfo()进行,并将点表示法IP地址转换为主机名。例如,对google.com进行ping操作经常会给出一个奇怪的地址:
bom07s18-in-f14.1e100.net这是反向DNS查找的结果。
- 使用SOCK_RAW(协议为IPPROTO_ICMP)打开Raw套接字。
注意:原始套接字需要超级用户权限,因此您必须使用sudo运行此代码 - 按下crtl + C时,ping将给出报告。该中断被中断处理程序捕获
这只是将我们的ping循环条件设置为false。 - 这是主要的ping发送循环。
我们必须:- 将ttl选项设置为套接字中的值
设置TTL值以限制数据包可以进行的跳数。 - 设置recv函数的超时时间
如果未设置超时,则recv将永远等待,从而停止循环。 - 填满icmp数据包
如下:- 将报文头类型设置为ICMP_ECHO。
- 将ID设置为进程的pid
- 随机填充味精部分。
- 计算校验和并将其填写在校验和字段中。
- 发送数据包
- 等待它被接收。
这里的主要问题是接收到的数据包并不意味着目的地正在运行。
回声表示目的地确定。回声答复是从目标OS内核发送的。这是所有类型和代码的列表。
这里的问题是,如果一切正确,程序将显示类型69和代码0,而不是代表echo_reply的0。// C program to Implement Ping // compile as -o ping // run as sudo ./ping
#include #include #include #include #include #include #include #include #include #include #include #include #include #include // Define the Packet Constants // ping packet size #define PING_PKT_S 64 // Automatic port number #define PORT_NO 0 // Automatic port number #define PING_SLEEP_RATE 1000000 x // Gives the timeout delay for receiving packets // in seconds #define RECV_TIMEOUT 1 // Define the Ping Loop int pingloop=1; // ping packet structure struct ping_pkt { struct icmphdr hdr; char msg[PING_PKT_S-sizeof(struct icmphdr)]; }; // Calculating the Check Sum unsigned short checksum(void *b, int len) { unsigned short *buf = b; unsigned int sum=0; unsigned short result; for ( sum = 0; len > 1; len -= 2 ) sum += *buf++; if ( len == 1 ) sum += *(unsigned char*)buf; sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); result = ~sum; return result; } // Interrupt handler void intHandler(int dummy) { pingloop=0; } // Performs a DNS lookup char *dns_lookup(char *addr_host, struct sockaddr_in *addr_con) { printf("\nResolving DNS..\n"); struct hostent *host_entity; char *ip=(char*)malloc(NI_MAXHOST*sizeof(char)); int i; if ((host_entity = gethostbyname(addr_host)) == NULL) { // No ip found for hostname return NULL; } //filling up address structure strcpy(ip, inet_ntoa(*(struct in_addr *) host_entity->h_addr)); (*addr_con).sin_family = host_entity->h_addrtype; (*addr_con).sin_port = htons (PORT_NO); (*addr_con).sin_addr.s_addr = *(long*)host_entity->h_addr; return ip; } // Resolves the reverse lookup of the hostname char* reverse_dns_lookup(char *ip_addr) { struct sockaddr_in temp_addr; socklen_t len; char buf[NI_MAXHOST], *ret_buf; temp_addr.sin_family = AF_INET; temp_addr.sin_addr.s_addr = inet_addr(ip_addr); len = sizeof(struct sockaddr_in); if (getnameinfo((struct sockaddr *) &temp_addr, len, buf, sizeof(buf), NULL, 0, NI_NAMEREQD)) { printf("Could not resolve reverse lookup of hostname\n"); return NULL; } ret_buf = (char*)malloc((strlen(buf) +1)*sizeof(char) ); strcpy(ret_buf, buf); return ret_buf; } // make a ping request void send_ping(int ping_sockfd, struct sockaddr_in *ping_addr, char *ping_dom, char *ping_ip, char *rev_host) { int ttl_val=64, msg_count=0, i, addr_len, flag=1, msg_received_count=0; struct ping_pkt pckt; struct sockaddr_in r_addr; struct timespec time_start, time_end, tfs, tfe; long double rtt_msec=0, total_msec=0; struct timeval tv_out; tv_out.tv_sec = RECV_TIMEOUT; tv_out.tv_usec = 0; clock_gettime(CLOCK_MONOTONIC, &tfs); // set socket options at ip to TTL and value to 64, // change to what you want by setting ttl_val if (setsockopt(ping_sockfd, SOL_IP, IP_TTL, &ttl_val, sizeof(ttl_val)) != 0) { printf("\nSetting socket options to TTL failed!\n"); return; } else { printf("\nSocket set to TTL..\n"); } // setting timeout of recv setting setsockopt(ping_sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv_out, sizeof tv_out); // send icmp packet in an infinite loop while(pingloop) { // flag is whether packet was sent or not flag=1; //filling packet bzero(&pckt, sizeof(pckt)); pckt.hdr.type = ICMP_ECHO; pckt.hdr.un.echo.id = getpid(); for ( i = 0; i < sizeof(pckt.msg)-1; i++ ) pckt.msg[i] = i+'0'; pckt.msg[i] = 0; pckt.hdr.un.echo.sequence = msg_count++; pckt.hdr.checksum = checksum(&pckt, sizeof(pckt)); usleep(PING_SLEEP_RATE); //send packet clock_gettime(CLOCK_MONOTONIC, &time_start); if ( sendto(ping_sockfd, &pckt, sizeof(pckt), 0, (struct sockaddr*) ping_addr, sizeof(*ping_addr)) <= 0) { printf("\nPacket Sending Failed!\n"); flag=0; } //receive packet addr_len=sizeof(r_addr); if ( recvfrom(ping_sockfd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&r_addr, &addr_len) <= 0 && msg_count>1) { printf("\nPacket receive failed!\n"); } else { clock_gettime(CLOCK_MONOTONIC, &time_end); double timeElapsed = ((double)(time_end.tv_nsec - time_start.tv_nsec))/1000000.0 rtt_msec = (time_end.tv_sec- time_start.tv_sec) * 1000.0 + timeElapsed; // if packet was not sent, don't receive if(flag) { if(!(pckt.hdr.type ==69 && pckt.hdr.code==0)) { printf("Error..Packet received with ICMP type %d code %d\n", pckt.hdr.type, pckt.hdr.code); } else { printf("%d bytes from %s (h: %s) (%s) msg_seq=%d ttl=%d rtt = %Lf ms.\n", PING_PKT_S, ping_dom, rev_host, ping_ip, msg_count, ttl_val, rtt_msec); msg_received_count++; } } } } clock_gettime(CLOCK_MONOTONIC, &tfe); double timeElapsed = ((double)(tfe.tv_nsec - tfs.tv_nsec))/1000000.0; total_msec = (tfe.tv_sec-tfs.tv_sec)*1000.0+ timeElapsed printf("\n===%s ping statistics===\n", ping_ip); printf("\n%d packets sent, %d packets received, %f percent packet loss. Total time: %Lf ms.\n\n", msg_count, msg_received_count, ((msg_count - msg_received_count)/msg_count) * 100.0, total_msec); } // Driver Code int main(int argc, char *argv[]) { int sockfd; char *ip_addr, *reverse_hostname; struct sockaddr_in addr_con; int addrlen = sizeof(addr_con); char net_buf[NI_MAXHOST]; if(argc!=2) { printf("\nFormat %s \n", argv[0]); return 0; } ip_addr = dns_lookup(argv[1], &addr_con); if(ip_addr==NULL) { printf("\nDNS lookup failed! Could not resolve hostname!\n"); return 0; } reverse_hostname = reverse_dns_lookup(ip_addr); printf("\nTrying to connect to '%s' IP: %s\n", argv[1], ip_addr); printf("\nReverse Lookup domain: %s", reverse_hostname); //socket() sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if(sockfd<0) { printf("\nSocket file descriptor not received!!\n"); return 0; } else printf("\nSocket file descriptor %d received\n", sockfd); signal(SIGINT, intHandler);//catching interrupt //send pings continuously send_ping(sockfd, &addr_con, reverse_hostname, ip_addr, argv[1]); return 0; } 输出示例:
运行sudo ./ping google.comResolving DNS.. Trying to connect to 'google.com' IP: 172.217.27.206 Reverse Lookup domain: bom07s15-in-f14.1e100.net Socket file descriptor 3 received Socket set to TTL.. 64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206) msg_seq=1 ttl=64 rtt = 57.320584 ms. 64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206) msg_seq=2 ttl=64 rtt = 58.666775 ms. 64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206) msg_seq=3 ttl=64 rtt = 58.081148 ms. 64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206) msg_seq=4 ttl=64 rtt = 58.700630 ms. 64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206) msg_seq=5 ttl=64 rtt = 58.281802 ms. 64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206) msg_seq=6 ttl=64 rtt = 58.360916 ms. ===172.217.27.206 ping statistics=== 6 packets sent, 6 packets received, 0.000000 percent packet loss. Total time: 6295.187804 ms.
想要从精选的最佳视频中学习和练习问题,请查看《基础知识到高级C的C基础课程》。
- 将ttl选项设置为套接字中的值