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;
}

编译链接执行. 输出如下:

posted @ 2016-01-26 23:02  zhanglong71  阅读(797)  评论(0编辑  收藏  举报