Luca Corsini
Luca Corsini

Reputation: 736

MFC C++ : IcmpSendEcho returns successfully, but status is IP_DEST_HOST_UNREACHABLE and RoundTripTime is not the default timeout time

I use IcmpSendEcho inside my program to check if a specific pc corresponding to an IP address inside my internal network is on. This solution works effectively for almost all of our customers, but one of them has a very inusual case.

I certainly know that this specific computer (it happens with more than one computer, by the way) is on and operative 24/7, but the IcmpSendEcho behavior changes during the day and I don't understand why.

During the time slot 05:00 AM - 08:00 PM, IcmpSendEcho on this computer returns a successful echo request (IP_SUCCESS) with a plausible roundtrip time, but during the time slot 08:00 PM - 05:00 AM, IcmpSendEcho returns the error IP_DEST_HOST_UNREACHABLE. The strange part about this is that the roundtrip time DOES NOT correspond with the timeout time assigned to the IcmpSendEcho function, but is lower.

If i execute the test on a computer that is switched off, my code tells me that IcmpSendEcho request has failed, so the code goes to a different instruction block.

Why is this happening? What is the difference between the IcmpSendEcho request failing and succeeding but with an error code like IP_DEST_HOST_UNREACHABLE ?

Thank you for your help.

unsigned long ipaddr = inet_addr(CT2CA(myIpAddress));
HANDLE hIcmpFile;
hIcmpFile = IcmpCreateFile();
if (hIcmpFile == INVALID_HANDLE_VALUE) {
  printf("\tUnable to open handle.\n");
  printf("IcmpCreatefile returned error: %ld\n", GetLastError());
  EDMLog::Error(_T("Unable to open handle. error code 001"));
  errorCode = _T("001");
}
else {
  char SendData[100];
  for (int x = 0; x < 100; ++x) {
    SendData[x] = 'A';
  }
  LPVOID ReplyBuffer = NULL;
  DWORD ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);

  ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);
  ReplyBuffer = (VOID*)malloc(ReplySize);
  if (ReplyBuffer == NULL) {
    printf("\tUnable to allocate memory\n");
    EDMLog::Error(_T("Unable to allocate memory. error code 001"));
    errorCode = _T("001");
  }
  else {

    DWORD dwRetVal = IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData),
      NULL, ReplyBuffer, ReplySize, 10000);

    printf("\tSent icmp message to %s\n", CT2CA(myIpAddress);
    EDMLog::Information(_T("Test su indirizzo ") + myIpAddress);
    if (dwRetVal != 0) {
      /////////////////////////////////////
      //HERE THE REQUEST IS SUCCESSFULL BUT THE ERROR CODE IS IP_DEST_HOST_UNREACHABLE
      /////////////////////////////////////
      PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
      struct in_addr ReplyAddr;
      ReplyAddr.S_un.S_addr = pEchoReply->Address;
      if (dwRetVal > 1) {

        printf("\tReceived %ld icmp message responses\n", dwRetVal);
        printf("\tInformation from the first response:\n");
      }
      else {
        printf("\tReceived %ld icmp message response\n", dwRetVal);
        printf("\tInformation from this response:\n");
      }
      printf("\t  Received from %s\n", inet_ntoa(ReplyAddr));
      printf("\t  Status = %ld\n",
        pEchoReply->Status);

      switch (pEchoReply->Status) {
        case IP_SUCCESS:
          //GESTIONE PARTICOLARE PER GAROM
          //errorCode.Format(_T("90%d"), i + 1);
          //GESTIONE TRADIZIONALE
          errorCode = _T("000");
          EDMLog::Debug(_T("Test eseguito correttamente."));
          break;
        case IP_BUF_TOO_SMALL:
          EDMLog::Error(_T("Buffer too small. error code 101"));
          errorCode = _T("101");
          break;
        case IP_DEST_NET_UNREACHABLE:
          EDMLog::Error(_T("DEST NET UNREACHABLE. error code 102"));
          errorCode = _T("102");
          break;
        case IP_DEST_HOST_UNREACHABLE:
          EDMLog::Error(_T("DEST HOST UNREACHABLE. error code 103"));
          errorCode = _T("103");
          break;
        case IP_DEST_PROT_UNREACHABLE:
          EDMLog::Error(_T("DEST PROT UNREACHABLE. error code 104"));
          errorCode = _T("104");
          break;
        case IP_DEST_PORT_UNREACHABLE:
          EDMLog::Error(_T("DEST PORT UNREACHABLE. error code 105"));
          errorCode = _T("105");
          break;
        case IP_NO_RESOURCES:
          EDMLog::Error(_T("IP NO RESOURCES. error code 106"));
          errorCode = _T("106");
          break;
        case IP_BAD_OPTION:
          EDMLog::Error(_T("IP BAD OPTION. error code 107"));
          errorCode = _T("107");
          break;
        case IP_HW_ERROR:
          EDMLog::Error(_T("IP HW ERROR. error code 108"));
          errorCode = _T("108");
          break;
        case IP_PACKET_TOO_BIG:
          EDMLog::Error(_T("IP PACKET TOO BIG. error code 109"));
          errorCode = _T("109");
          break;
        case IP_REQ_TIMED_OUT:
          EDMLog::Error(_T("IP REQ TIMED OUT. error code 110"));
          errorCode = _T("110");
          break;
        case IP_BAD_REQ:
          EDMLog::Error(_T("IP BAD REQ. error code 111"));
          errorCode = _T("111");
          break;
        case IP_BAD_ROUTE:
          EDMLog::Error(_T("IP BAD ROUTE. error code 112"));
          errorCode = _T("112");
          break;
        case IP_TTL_EXPIRED_TRANSIT:
          EDMLog::Error(_T("IP TTL EXPIRED TRANSIT. error code 113"));
          errorCode = _T("113");
          break;
        case IP_TTL_EXPIRED_REASSEM:
          EDMLog::Error(_T("IP TTL EXPIRED REASSEM. error code 114"));
          errorCode = _T("114");
          break;
        case IP_PARAM_PROBLEM:
          EDMLog::Error(_T("IP PARAM PROBLEM. error code 115"));
          errorCode = _T("115");
          break;
        case IP_SOURCE_QUENCH:
          EDMLog::Error(_T("IP SOURCE QUENCH. error code 116"));
          errorCode = _T("116");
          break;
        case IP_OPTION_TOO_BIG:
          EDMLog::Error(_T("IP OPTION TOO BIG. error code 117"));
          errorCode = _T("117");
          break;
        case IP_BAD_DESTINATION:
          EDMLog::Error(_T("IP BAD DESTINATION. error code 118"));
          errorCode = _T("118");
          break;
        case IP_GENERAL_FAILURE:
          EDMLog::Error(_T("IP GENERAL FAILURE. error code 150"));
          errorCode = _T("150");
          break;
      }
      printf("\t  Roundtrip time = %ld milliseconds\n",
        pEchoReply->RoundTripTime);

      if ((int)(pEchoReply->RoundTripTime) == 0)
        valore.Format(_T("%d"), 1);
      else
        valore.Format(_T("%d"),(int)(pEchoReply->RoundTripTime));
    }
    else {
      /////////////////////////////////////
      //HERE THE REQUEST IS NOT SUCCESSFUL (TIMEOUT)
      /////////////////////////////////////

      printf("\tCall to IcmpSendEcho failed.\n");
      DWORD lastError = GetLastError();
      printf("\tIcmpSendEcho returned error: %ld\n", lastError);



      PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
      struct in_addr ReplyAddr;
      ReplyAddr.S_un.S_addr = pEchoReply->Address;
      if (dwRetVal > 1) {
        printf("\tReceived %ld icmp message responses\n", dwRetVal);
        printf("\tInformation from the first response:\n");
      }
      else {
        printf("\tReceived %ld icmp message response\n", dwRetVal);
        printf("\tInformation from this response:\n");
      }
      printf("\t  Received from %s\n", inet_ntoa(ReplyAddr));
      printf("\t  Status = %ld\n",
        pEchoReply->Status);

      valore = _T("0");

      switch (pEchoReply->Status) {
      case IP_SUCCESS:
        errorCode = _T("000");
        EDMLog::Information(_T("Test eseguito correttamente."));
        break;
      case IP_BUF_TOO_SMALL:
        EDMLog::Error(_T("Buffer too small. error code 101"));
        errorCode = _T("101");
        break;
      case IP_DEST_NET_UNREACHABLE:
        EDMLog::Error(_T("DEST NET UNREACHABLE. error code 102"));
        errorCode = _T("102");
        break;
      case IP_DEST_HOST_UNREACHABLE:
        EDMLog::Error(_T("DEST HOST UNREACHABLE. error code 103"));
        errorCode = _T("103");
        break;
      case IP_DEST_PROT_UNREACHABLE:
        EDMLog::Error(_T("DEST PROT UNREACHABLE. error code 104"));
        errorCode = _T("104");
        break;
      case IP_DEST_PORT_UNREACHABLE:
        EDMLog::Error(_T("DEST PORT UNREACHABLE. error code 105"));
        errorCode = _T("105");
        break;
      case IP_NO_RESOURCES:
        EDMLog::Error(_T("IP NO RESOURCES. error code 106"));
        errorCode = _T("106");
        break;
      case IP_BAD_OPTION:
        EDMLog::Error(_T("IP BAD OPTION. error code 107"));
        errorCode = _T("107");
        break;
      case IP_HW_ERROR:
        EDMLog::Error(_T("IP HW ERROR. error code 108"));
        errorCode = _T("108");
        break;
      case IP_PACKET_TOO_BIG:
        EDMLog::Error(_T("IP PACKET TOO BIG. error code 109"));
        errorCode = _T("109");
        break;
      case IP_REQ_TIMED_OUT:
        EDMLog::Error(_T("IP REQ TIMED OUT. error code 110"));
        errorCode = _T("004");
        valore = _T("10000");
        break;
      case IP_BAD_REQ:
        EDMLog::Error(_T("IP BAD REQ. error code 111"));
        errorCode = _T("111");
        break;
      case IP_BAD_ROUTE:
        EDMLog::Error(_T("IP BAD ROUTE. error code 112"));
        errorCode = _T("112");
        break;
      case IP_TTL_EXPIRED_TRANSIT:
        EDMLog::Error(_T("IP TTL EXPIRED TRANSIT. error code 113"));
        errorCode = _T("113");
        break;
      case IP_TTL_EXPIRED_REASSEM:
        EDMLog::Error(_T("IP TTL EXPIRED REASSEM. error code 114"));
        errorCode = _T("114");
        break;
      case IP_PARAM_PROBLEM:
        EDMLog::Error(_T("IP PARAM PROBLEM. error code 115"));
        errorCode = _T("115");
        break;
      case IP_SOURCE_QUENCH:
        EDMLog::Error(_T("IP SOURCE QUENCH. error code 116"));
        errorCode = _T("116");
        break;
      case IP_OPTION_TOO_BIG:
        EDMLog::Error(_T("IP OPTION TOO BIG. error code 117"));
        errorCode = _T("117");
        break;
      case IP_BAD_DESTINATION:
        EDMLog::Error(_T("IP BAD DESTINATION. error code 118"));
        errorCode = _T("118");
        break;
      case IP_GENERAL_FAILURE:
        EDMLog::Error(_T("IP GENERAL FAILURE. error code 150"));
        errorCode = _T("150");
        break;
      }
    }

    IcmpCloseHandle(hIcmpFile);
  }
}

Obviously, any improvement of the above code is well received, thank you again.

Upvotes: 1

Views: 831

Answers (2)

Luca Corsini
Luca Corsini

Reputation: 736

Gotcha. The router had a rule that blocked any kind of traffic from an hour to an other hour. Obviously, the customer didn't tell me about it. Thank you all!

Upvotes: 1

rodolk
rodolk

Reputation: 5907

The problem is how the address is resolved. For the case where the PC is in the same LAN, it is necessary to obtain the MAC address based on the destination IP address. It uses ARP protocol that cannot obtain the MAC address so it cannot send the ICMP message. Then it will probably respect the timeout. It will give up at timeout. In the other case, if you are sending an echo request ICMP message to a node that is outside the LAN, that message will be routed through intermediate routers (could be a gateway too). So the IP packet, conveying the ICMP message, will leave your PC to the next hop, somewhere beyond the next hop, a router will realize if cannot route that ICMP packet and it will return the ICMP message DestinationUnreachable to the origin node (your PC). When ping receives this message it doesn't make sense to wait for the timeout, it already received a message telling it the destination is unreachable.

Upvotes: 1

Related Questions