ICMP recvfrom 函数始终接收数据,即使目标已关闭

ICMP recvfrom function always receive data even though the destination was shutdown

我用 C 写了一个程序来 ping 一台机器并接收来自那台机器的回声来检测这台机器是否还活着。我的代码如下:

#define PING_PKT_S 64
#define PORT_NO 0
#define PING_SLEEP_RATE 1000000
#define RECV_TIMEOUT 1

int pingloop=1;

struct ping_pkt {
    struct icmphdr hdr;
    char msg[PING_PKT_S-sizeof(struct icmphdr)];
};

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

//to check if this machine is actively working
int isActiveNow(){
    const char *pre = "161.";
    char *prefixArr[] = {pre};
    char myIp[64];
    memset(myIp, 0, sizeof(myIp));
    get_local_ip(myIp, prefixArr);
    printf("---isActiveNow:%s\n",myIp);
    if(myIp[0]=='[=10=]'){
        printf("Not Active Now!\n");
        return 0;
    }
    return 1;
}

void intHandler(int dummy) {
    pingloop=0;
}

long current_timestamp() {
    struct timeval te;
    gettimeofday(&te, NULL);
    long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000;
    return milliseconds;
}

void display(void *buf, int bytes){
    int i;
    struct iphdr *ip = buf;
    struct icmphdr *icmp = buf+ip->ihl*4;
    printf("---------------------Start Display\n");
    for ( i = 0; i < bytes; i++ ){
        printf("%c", ((unsigned char*)buf)[i]);
    }
    printf("---------------------End Display\n");
}

int failedNum = 0;

// make a ping request
void send_ping(int ping_sockfd, char *ping_ip) {
    int ttl_val=64, msg_count=0, i, addr_len, msg_received_count=0;

    struct ping_pkt pckt;
    struct sockaddr_in r_addr;
    struct timespec time_start, time_end;

    // set socket options at ip to TTL and value to 64,
    if (setsockopt(ping_sockfd, SOL_IP, IP_TTL,  &ttl_val, sizeof(ttl_val)) != 0) {
        printf("Setting socket options to TTL failed!\n");
        return;
    }else{
        printf("\nSocket set to TTL..\n");
    }

    // send icmp packet in an infinite loop
    while(pingloop) {
        sleep(2);
        if(isActiveNow()){
            printf("--current is active!");
            continue;
        }
        //filling packet
        bzero(&pckt, sizeof(pckt));

        pckt.hdr.type = ICMP_ECHO;
        pckt.hdr.un.echo.id = getpid();

        for ( i = 0; i < 10; i++ ){
            pckt.msg[i] = 'w';
        }
        pckt.msg[i] = 0;
        pckt.hdr.un.echo.sequence = msg_count++;
        pckt.hdr.checksum = checksum(&pckt, sizeof(pckt));

        long s1 = current_timestamp();
        struct hostent *hostentObj;
        hostentObj = gethostbyname(ping_ip);
        struct sockaddr_in ping_addr;
        ping_addr.sin_port = 0;
        ping_addr.sin_family = hostentObj->h_addrtype;
        ping_addr.sin_addr.s_addr = *(long*) hostentObj->h_addr;

        //Using synchronize
        if ( sendto(ping_sockfd, &pckt, sizeof(pckt), 0,  (struct sockaddr*) &ping_addr, sizeof(ping_addr)) <= 0) {
            printf("\nPacket Sending Failed!\n");
        }

        struct timeval tv_out;
        tv_out.tv_sec = 10;
        tv_out.tv_usec = 0;
//        setsockopt(ping_sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv_out, sizeof tv_out);

        struct sockaddr_in myAddr;
        unsigned char buf[1024];
        int len=sizeof(myAddr);

        bzero(buf, sizeof(buf));
        struct ping_pkt pckt2;
        int bytes = recvfrom(ping_sockfd, &pckt2, sizeof(pckt2), 0, (struct sockaddr*)&myAddr, &len);
        printf("---------------------bytes len:%d\n",bytes);
        if ( bytes > 0 ){
            display(&pckt2, bytes);
            long s2 = current_timestamp();
            printf("from %s msg_seq=%d ttl=%d rtt = %ld ms. pckt.hdr.type=%d \n", ping_ip, msg_count,ttl_val, (s2-s1), pckt.hdr.type);
        }else{
            printf("-------NO RESPONSE!!!!!!\n");
            failedNum++;
            //maximum detected failed number is 10
            if(failedNum>3){
                printf("--The other server dead!!!\n");
            }
        }
    }
}

bool prefix(const char *pre, const char *str){
  return strncmp(pre, str, strlen(pre)) == 0;
}

void get_local_ip(char *pubIp, char *prefixArr[]){
  char *ip;
  int fd, intrface, retn = 0;
  struct ifreq buf[INET_ADDRSTRLEN];
  struct ifconf ifc;

  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0){
    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = (caddr_t)buf;
    if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc)){
      intrface = ifc.ifc_len/sizeof(struct ifreq);
      while (intrface-- > 0){
        if (!(ioctl(fd, SIOCGIFADDR, (char *)&buf[intrface]))){
          ip=(inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr));

          int len = 0,i = 0;
          len = sizeof(prefixArr) / sizeof(*prefixArr);
          for (i = 0; i < len; i++) {
              if(prefix(prefixArr[i],ip)){
                  strcpy(pubIp,ip);
                  return;
              }
          }
        }
      }
    }
    close(fd);
  }
}

// Driver Code
int main(int argc, char *argv[]){
    int sockfd;
    char *pingIp;

    char *prefixArr[] = { "11.","10.","172."};
    char myIp[64];
    memset(myIp, 0, sizeof(myIp));
    get_local_ip(myIp, prefixArr);
    //the other server's ip for detecting it's alive or dead
    pingIp = argv[1];
    printf("my ip:%s-----ping ip:%s\n", myIp, pingIp);

    //socket()
    sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
    struct sockaddr_in r_addr;
    r_addr.sin_family = AF_INET;
    r_addr.sin_addr.s_addr = inet_addr(myIp);
    r_addr.sin_port = 0;

    bind(sockfd, (struct sockaddr *) &r_addr, sizeof(r_addr));

    if(sockfd<0){
        printf("\nSocket file descriptor not received!!\n");
        return 0;
    }else{
        printf("\nSocket file descriptor %d received\n", sockfd);
    }

    //once error seen, it will stop the loop
    signal(SIGINT, intHandler);//catching interrupt

    //send pings continuously
    send_ping(sockfd,  pingIp);

    return 0;
}

问题是 myReceiveBytes 的值始终为 64,即使目标服务器已经关闭。

您的代码实际上并未对接收到的数据包进行解码。很有可能,您收到的是一个 ICMP 目标无法到达的数据包 from your own host 或来自您的网关。

大致如下:

#include <netinet/ip.h> /* struct iphdr */
...
...
struct sockaddr_in peerAddr;
unsigned char buf[1024];
unsigned int len = sizeof(peerAddr);

int bytes_received = recvfrom(sockfd, buf, sizeof(buf), 0,
                              (struct sockaddr *) &peerAddr, &len);
if (bytes_received > 0) {
  printf("ICMP packet received from %s\n", inet_ntoa(peerAddr.sin_addr));
  struct icmphdr *icmp = (struct icmphdr *) (buf + sizeof(struct iphdr));
  switch (icmp->type) {
  case ICMP_ECHOREPLY:      printf("Type: ICMP_ECHOREPLY\n"); break;
  case ICMP_DEST_UNREACH:   printf("Type: ICMP_DEST_UNREACH\n"); break;
  case ICMP_SOURCE_QUENCH:  printf("Type: ICMP_SOURCE_QUENCH\n"); break;
  case ICMP_REDIRECT:       printf("Type: ICMP_REDIRECT\n"); break;
  case ICMP_ECHO:           printf("Type: ICMP_ECHO\n"); break;
  case ICMP_TIME_EXCEEDED:  printf("Type: ICMP_TIME_EXCEEDED\n"); break;
  case ICMP_PARAMETERPROB:  printf("Type: ICMP_PARAMETERPROB\n"); break;
  case ICMP_TIMESTAMP:      printf("Type: ICMP_TIMESTAMP\n"); break;
  case ICMP_TIMESTAMPREPLY: printf("Type: ICMP_TIMESTAMPREPLY\n"); break;
  case ICMP_INFO_REQUEST:   printf("Type: ICMP_INFO_REQUEST\n"); break;
  case ICMP_INFO_REPLY:     printf("Type: ICMP_INFO_REPLY\n"); break;
  case ICMP_ADDRESS:        printf("Type: ICMP_ADDRESS\n"); break;
  case ICMP_ADDRESSREPLY:   printf("Type: ICMP_ADDRESSREPLY\n"); break;
  default:                  printf("Type: <0x%02x>\n", icmp->type); break;
  }
}