icmp实现ping
以前弄到的一段代码, 一个用原始套接字raw socket实现icmp协议ping工具
myping.c
#include <stdio.h> #include <errno.h> #include <signal.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <netinet/ip_icmp.h> #define ICMP_PACKET_SIZE 16 #define TIME_OUT_SECONDS 2 unsigned short cal_chksum(unsigned short *buf, int len) { unsigned int sum = 0; unsigned short ret; while(len > 1) { sum += *buf; buf++; len -= 2; } if(1 == len) { sum += (*(unsigned char *)buf); } sum = (sum >> 16) + (sum & 0xFFFF); sum = (sum >> 16) + (sum & 0xFFFF); //sum = (sum >> 16) + sum; ret = ~sum; return ret; } int pack_icmp(char *buf, int seq) { struct icmp *icmp_packet = (struct icmp *)buf; icmp_packet->icmp_type = ICMP_ECHO; icmp_packet->icmp_code = 0; icmp_packet->icmp_id = getpid(); icmp_packet->icmp_seq = seq; icmp_packet->icmp_cksum = 0; //must clean it before cal_chksum or you will get bad chksum(can't error when ping your own ipaddress ) struct timeval tv; gettimeofday(&tv, NULL); memcpy(buf + 8, &tv, sizeof(tv)); icmp_packet->icmp_cksum = cal_chksum((unsigned short *)icmp_packet,ICMP_PACKET_SIZE); } int parse_ip_icmp_info(void *buf, struct sockaddr_in answer) { unsigned short chk_sum; unsigned char ttl; unsigned short seq; struct timeval tv_send, tv_now; unsigned int mini_sec; struct ip *ip_packet = (struct ip *)buf; struct icmp *icmp_packet = (struct icmp *)(buf + (ip_packet->ip_hl << 2)); if(icmp_packet->icmp_id != getpid()) { return -1; } if(icmp_packet->icmp_type != ICMP_ECHOREPLY) { return -1; } ttl = ip_packet->ip_ttl; seq = icmp_packet->icmp_seq; chk_sum = icmp_packet->icmp_cksum; icmp_packet->icmp_cksum = 0; if(chk_sum != cal_chksum((unsigned short *)icmp_packet,ICMP_PACKET_SIZE)) { return -1; } gettimeofday(&tv_now, NULL); memcpy(&tv_send, ((char *)icmp_packet + 8), sizeof(tv_send)); mini_sec = (tv_now.tv_sec - tv_send.tv_sec) * 1000000 + (tv_now.tv_usec - tv_send.tv_usec); printf("%d bytes data from: %s icmp_seq = %d, ttl = %d, times = %.3fms\n", (ip_packet->ip_hl << 2) + 16, inet_ntoa(answer.sin_addr), seq, ttl, mini_sec/1000.0); return 0; } int main(int argc, char *argv[]) { if(argc != 2) { printf("Usage: %s ipaddr/hostname\n", argv[0]); exit( 0 ); } struct hostent * host = gethostbyname(argv[1]); if(host == NULL) { printf("ping: unknow host %s \n", argv[1]); exit(2); } struct sockaddr_in dest; dest.sin_family = AF_INET; memcpy( &dest.sin_addr, host->h_addr, sizeof(int)); int sock_raw_fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if(sock_raw_fd == -1) { perror("socket"); exit(1); } char buf[ICMP_PACKET_SIZE]; int seq = 0; char recv_buf[50]; struct sockaddr_in answer; int answer_len = sizeof(answer); while(1) { seq ++; pack_icmp(buf, seq); sendto(sock_raw_fd, buf, ICMP_PACKET_SIZE, 0, (struct sockaddr *)&dest, sizeof(dest)); while(1) { fd_set readset; FD_ZERO(&readset); FD_SET(sock_raw_fd, &readset); struct timeval tv; tv.tv_sec = TIME_OUT_SECONDS; tv.tv_usec = 0; int ret = select(sock_raw_fd+1, &readset, NULL, NULL, &tv); if(ret == -1) { perror("select"); exit(3); } else if(ret == 0) { printf("time out.\n"); break; } else { int resolve = 0; int ret = recvfrom(sock_raw_fd, recv_buf, 36, 0, (struct sockaddr *)&answer, &answer_len); if(ret > 0) resolve = parse_ip_icmp_info(recv_buf, answer); if(resolve == 0) break; } } sleep(1); } return 0; }
编译链接执行. 输出如下: