2014-02-28 55 views
1

我的程序向指定的主機發送icmp請求。使用原始套接字的c ping請求失敗

#include <sys/socket.h> 
#include <netinet/in.h> 
#include <netinet/ip.h> 
#include <netinet/ip_icmp.h> 
#include <netdb.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 

#define ICMP_DATALEN 4 
#define IP4_HDRLEN    20 
#define ICMP_HDRLEN    8 

uint16_t        checksum (uint16_t *addr, int len) 
{ 
    int nleft = len; 
    int sum = 0; 
    uint16_t *w = addr; 
    uint16_t answer = 0; 

    while (nleft > 1) { 
      sum += *w++; 
      nleft -= sizeof (uint16_t); 
    } 

    if (nleft == 1) { 
      *(uint8_t *) (&answer) = *(uint8_t *) w; 
      sum += answer; 
    } 

    sum = (sum >> 16) + (sum & 0xFFFF); 
    sum += (sum >> 16); 
    answer = ~sum; 
    return (answer); 
} 

int            main(int ac, char **av) 
{ 
    int        icmp_socket; 
    uint8_t       data[ICMP_DATALEN]; 
    unsigned char     datagram[IP_MAXPACKET]; 
    int        ip_flags[4]; 
    struct ip      *iph = (struct ip *) datagram; 
    struct icmp      *icmph = (struct icmp *) (datagram + IP4_HDRLEN); 
    struct sockaddr_in    dst; 
    int        one = 1; 
    int        *val = &one; 

    if (ac != 3) 
    { 
      fprintf(stderr, "usage: %s <source adress> <target adress>\n", av[0]); 
      return (1); 
    } 

    if ((icmp_socket = socket(AF_INET, SOCK_RAW, 1)) == -1) 
    { 
      perror("socket"); 
      exit(1); 
    } 
    if (setsockopt(icmp_socket, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) 
    { 
      perror("setsockopt"); 
      exit(1); 
    } 

    memset(datagram, 0, IP_MAXPACKET); 

    //prepare ipv4 header data 
    memset((void*)&dst, 0, sizeof(dst)); 
    dst.sin_family = AF_INET; 
    inet_pton(AF_INET, av[2], &(dst.sin_addr)); 
    ip_flags[0] = 0; 
    ip_flags[1] = 0; 
    ip_flags[2] = 0; 
    ip_flags[3] = 0; 

    //fill in ipv4 header 
    iph->ip_hl = IP4_HDRLEN/sizeof(uint32_t); 
    iph->ip_v = 4; 
    iph->ip_tos = 0; 
    iph->ip_len = (IP4_HDRLEN + ICMP_HDRLEN + ICMP_DATALEN); 
    iph->ip_id = 666; 
    iph->ip_off = (ip_flags[0] << 15) 
              + (ip_flags[1] << 14) 
              + (ip_flags[2] << 13) 
              + ip_flags[3]; 
    iph->ip_ttl = 255; 
    iph->ip_p = IPPROTO_ICMP; 
    iph->ip_src.s_addr = inet_addr(av[1]); 
    iph->ip_dst.s_addr = dst.sin_addr.s_addr; 
    iph->ip_sum = 0; 
    iph->ip_sum = checksum((uint16_t *)datagram, IP4_HDRLEN); 

    //prepare icmp data 
    data[0] = '4'; 
    data[1] = '2'; 
    data[2] = '4'; 
    data[3] = '2'; 

    //fill in icmp header 
    icmph->icmp_type = ICMP_ECHO; 
    icmph->icmp_code = 0; 
    icmph->icmp_id = htons(666); 
    icmph->icmp_seq = htons(0); 
    memcpy(datagram + IP4_HDRLEN + ICMP_HDRLEN, data, ICMP_DATALEN); 
    icmph->icmp_cksum = 0; 
    icmph->icmp_cksum = checksum((uint16_t *)datagram + IP4_HDRLEN, (ICMP_HDRLEN + ICMP_DATALEN)); 

    int    i = 0; 

    printf("sending packet:\n\n"); 
    while (i < (IP4_HDRLEN + ICMP_HDRLEN + ICMP_DATALEN)) 
    { 
      printf("%02x", datagram[i++]); 
      if (i % 2 == 0) 
        printf(" "); 
      if (i % 16 == 0) 
        printf("\n"); 
    } 
    printf("ip header checksump: %hx\n", iph->ip_sum); 
    printf("icmp header checksump: %hx\n", icmph->icmp_cksum); 

    if (sendto(icmp_socket, datagram, (IP4_HDRLEN + ICMP_HDRLEN + ICMP_DATALEN), 0, (struct sockaddr *)&dst, sizeof(dst)) < 0) 
    { 
      perror("sendto"); 
      exit(1); 
    } 
    close(icmp_socket); 
    return (0); 
} 

我看着tcpdump的網絡和數據包被髮送,但我從來沒有收到一個答案,因爲我會用ping命令。 我希望你能幫助我!

回答

2

校驗和採用網絡字節順序。

您需要使用ntohs讀取要添加到校驗和中的字節,並使用htons回寫校驗和。

+0

非常感謝,它現在工作完美! – user3365521