2012-03-28 8 views
7

लक्ष्य:पहुंचने योग्य गंतव्यों पर आईसीएमपी प्रतिध्वनि अनुरोध/उत्तर के लिए उचित प्रक्रिया क्या है?

मुझे यह निर्धारित करने के लिए नेटवर्क स्विच पिंग करने में सक्षम होना चाहिए कि यह उपलब्ध है या नहीं। यह उपयोगकर्ता को यह बताने के लिए है कि नेटवर्क केबलिंग अनप्लग है, नेटवर्क स्विच अनुपलब्ध है, या कुछ अन्य समस्या नेटवर्क संचार मार्ग के भीतर है। मुझे एहसास है कि यह एक व्यापक निदान उपकरण नहीं है, लेकिन कुछ भी कुछ भी बेहतर नहीं है।

डिजाइन:

मैं कच्चे सॉकेट के साथ ICMP का उपयोग कर आईपीवी 4 बिंदु संकेतन में एक विशेष पते पर पांच (5) पिंग संदेश भेजने के लिए पर योजना बनाई। मैं सॉकेट पर एक आईसीएमपी फ़िल्टर स्थापित करूंगा और अपना खुद का आईपी हेडर नहीं बनाउंगा। आईसीएमपी का प्रसारण प्रेषण विधि के माध्यम से प्रेषण विधि और रिसेप्शन के माध्यम से होगा। यह एक धागे पर होगा (हालांकि एक और थ्रेड का उपयोग ट्रांसमिशन और रिसेप्शन को अलग करने के लिए किया जा सकता है)। किसी संदेश की रिसेप्शन प्रेषित की गई आईडी को प्राप्त संदेश की आईडी से मेल करके फ़िल्टर किया जाएगा। संग्रहीत आईडी एप्लिकेशन की चल रही प्रक्रिया आईडी होगी। यदि कोई ICMP_ECHOREPLY संदेश प्राप्त होता है और संदेश की आईडी और संग्रहीत आईडी मिलान होता है, तो पांच (4) तक पहुंचने तक काउंटर बढ़ता है (काउंटर शून्य-आधारित होता है)। मैं एक पिंग भेजने का प्रयास करूंगा, इसके जवाब का इंतजार करूंगा, और इस प्रक्रिया को पांच (5) बार दोहरा दूंगा।

समस्या:

मेरी डिजाइन कार्यान्वित होने के बाद, जब भी मैं एक सक्रिय नेटवर्क भागीदार के साथ एक विशेष मान्य नेटवर्क पते (जैसे कि 192.168.11.15) पिंग, मैं पाँच (5) पिंग्स से प्रत्येक के लिए ICMP_ECHOREPLY संदेश प्राप्त । हालांकि, जब भी मैं एक निष्क्रिय नेटवर्क पता (1 9 2.168.30.30 कहता हूं) निष्क्रिय नेटवर्क प्रतिभागियों (जिसका अर्थ है कि कोई डिवाइस किसी विशेष पते से जुड़ा हुआ नहीं है) के साथ, मुझे एक (1) ICMP_DEST_UNREACH, और चार (4) ICMP_ECHOREPLY संदेश मिलते हैं। उत्तर संदेशों में आईडी सॉफ़्टवेयर के भीतर संग्रहीत आईडी से मेल खाता है। जब भी मैं कमांडलाइन से 'पिंग 192.168.30.30' करता हूं, तो मुझे '1 9 2.168.40.50 icmp_seq = xx गंतव्य होस्ट अप्राप्य' मिलता है। क्या मुझे ICMP_ECHOREPLY संदेशों की बजाय ICMP_DEST_UNREACH संदेश प्राप्त नहीं होने चाहिए?

कोड:

Ping.h:

#include <netinet/in.h> 
#include <linux/ip.h> 
#include <linux/ipmc.h> 
#include <arpa/inet.h> 
#include <cstdio> 
#include <cstdlib> 
#include <stdint.h> 
#include <time.h> 
#include <errno.h> 
#include <string> 
#include <cstring> 
#include <netdb.h> 

class Ping 
{ 
    public: 
     Ping(std::string host) : _host(host) {} 
     ~Ping() {} 

     void start() 
     { 
      int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 
      if(sock < 0) 
      { 
       printf("Failed to create socket!\n"); 
       close(sock); 
       exit(1); 
      } 

      setuid(getuid()); 

      sockaddr_in pingaddr; 
      memset(&pingaddr, 0, sizeof(sockaddr_in)); 
      pingaddr.sin_family = AF_INET; 

      hostent *h = gethostbyname(_host.c_str()); 
      if(not h) 
      { 
       printf("Failed to get host by name!\n"); 
       close(sock); 
       exit(1); 
      } 

      memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr)); 

      // Set the ID of the sender (will go into the ID of the echo msg) 
      int pid = getpid(); 

      // Only want to receive the following messages 
      icmp_filter filter; 
      filter.data = ~((1<<ICMP_SOURCE_QUENCH) | 
          (1<<ICMP_DEST_UNREACH) | 
          (1<<ICMP_TIME_EXCEEDED) | 
          (1<<ICMP_REDIRECT) | 
          (1<<ICMP_ECHOREPLY)); 
      if(setsockopt(sock, SOL_RAW, ICMP_FILTER, (char *)&filter, sizeof(filter)) < 0) 
      { 
       perror("setsockopt(ICMP_FILTER)"); 
       exit(3); 
      } 

      // Number of valid echo receptions 
      int nrec = 0; 

      // Send the packet 
      for(int i = 0; i < 5; ++i) 
      { 
       char packet[sizeof(icmphdr)]; 
       memset(packet, 0, sizeof(packet)); 

       icmphdr *pkt = (icmphdr *)packet; 
       pkt->type = ICMP_ECHO; 
       pkt->code = 0; 
       pkt->checksum = 0; 
       pkt->un.echo.id = htons(pid & 0xFFFF); 
       pkt->un.echo.sequence = i; 
       pkt->checksum = checksum((uint16_t *)pkt, sizeof(packet)); 

       int bytes = sendto(sock, packet, sizeof(packet), 0, (sockaddr *)&pingaddr, sizeof(sockaddr_in)); 
       if(bytes < 0) 
       { 
        printf("Failed to send to receiver\n"); 
        close(sock); 
        exit(1); 
       } 
       else if(bytes != sizeof(packet)) 
       { 
        printf("Failed to write the whole packet --- bytes: %d, sizeof(packet): %d\n", bytes, sizeof(packet)); 
        close(sock); 
        exit(1); 
       } 

       while(1) 
       { 
        char inbuf[192]; 
        memset(inbuf, 0, sizeof(inbuf)); 

        int addrlen = sizeof(sockaddr_in); 
        bytes = recvfrom(sock, inbuf, sizeof(inbuf), 0, (sockaddr *)&pingaddr, (socklen_t *)&addrlen); 
        if(bytes < 0) 
        { 
         printf("Error on recvfrom\n"); 
         exit(1); 
        } 
        else 
        { 
         if(bytes < sizeof(iphdr) + sizeof(icmphdr)) 
         { 
          printf("Incorrect read bytes!\n"); 
          continue; 
         } 

         iphdr *iph = (iphdr *)inbuf; 
         int hlen = (iph->ihl << 2); 
         bytes -= hlen; 

         pkt = (icmphdr *)(inbuf + hlen); 
         int id = ntohs(pkt->un.echo.id); 
         if(pkt->type == ICMP_ECHOREPLY) 
         { 
          printf(" ICMP_ECHOREPLY\n"); 
          if(id == pid) 
          { 
           nrec++; 
           if(i < 5) break; 
          } 
         } 
         else if(pkt->type == ICMP_DEST_UNREACH) 
         { 
          printf(" ICMP_DEST_UNREACH\n"); 
          // Extract the original data out of the received message 
          int offset = sizeof(iphdr) + sizeof(icmphdr) + sizeof(iphdr); 
          if(((bytes + hlen) - offset) == sizeof(icmphdr)) 
          { 
           icmphdr *p = reinterpret_cast<icmphdr *>(inbuf + offset); 
           id = ntohs(p->un.echo.id); 
           if(origid == pid) 
           { 
            printf("  IDs match!\n"); 
            break; 
           } 
          } 
         } 
        } 
       } 
      } 

      printf("nrec: %d\n", nrec); 
     } 

    private: 
     int32_t checksum(uint16_t *buf, int32_t len) 
     { 
      int32_t nleft = len; 
      int32_t sum = 0; 
      uint16_t *w = buf; 
      uint16_t answer = 0; 

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

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

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

      return answer; 
     } 

     std::string _host; 
}; 

main.cpp:

#include "Ping.h" 

int main() 
{ 
//  Ping ping("192.168.11.15"); 
    Ping ping("192.168.30.30"); 
    ping.start(); 

    while(1) sleep(10); 
} 

आदेश में संकलित करने के लिए, बस टाइप 'जी ++ main.cpp -ओ पिंग' में लिनक्स बॉक्स की कमांड लाइन, और इसे संकलित करना चाहिए (यानी, अगर सभी स्रोत कोड स्थापित हैं)।

निष्कर्ष:

किसी को भी मुझे बता सकते हैं कि मैं क्यों एक युक्ति है कि आप उस विशेष नेटवर्क का पता पर नहीं है से एक (1) ICMP_DEST_UNREACH और चार (4) ICMP_ECHOREPLY संदेश प्राप्त कर रहा हूँ?

नोट: आप main.cpp फ़ाइल से नेटवर्क आईपी पता बदल सकते हैं। बस आईपी को उस डिवाइस पर बदलें जो वास्तव में आपके नेटवर्क पर मौजूद है या डिवाइस जो आपके नेटवर्क पर मौजूद नहीं है।

मुझे कोडिंग शैली के बारे में आलोचनाओं में भी रूचि नहीं है। मुझे पता है कि यह सुंदर नहीं है, सी ++ केस्ट के साथ मिश्रित 'सी' शैली कास्टिंग, खराब स्मृति प्रबंधन आदि है, लेकिन यह केवल प्रोटोटाइप कोड है। यह सुंदर होने का मतलब नहीं है।

+0

क्या आईसीएमपी_ईसीओएनपीएलवाई संरचना का गंतव्य पता के समान पता है? या क्या यह मध्यवर्ती राउटर है? – Beached

+0

यदि आप '/ bin/ping -c5 192.168.30.30' का उपयोग करते हैं, तो आपको क्या मिलता है? उत्तर पैकेट की सामग्री प्रिंट करना अधिक जानकारी प्रदान कर सकता है। यदि आपने स्वयं समस्या को हल किया है, तो कृपया जो आपने सीखा है उसे पोस्ट करें। –

+0

यह प्रोग्रामिंग प्रश्न की तुलना में नेटवर्क समस्या की तरह लगता है। – cxxl

उत्तर

4

ठीक है मुझे त्रुटि मिली। इन दो लाइनों को देखो।

int bytes = sendto(sock, packet, sizeof(packet), 0, (sockaddr *)&pingaddr, sizeof(sockaddr_in)); 

bytes = recvfrom(sock, inbuf, sizeof(inbuf), 0, (sockaddr *)&pingaddr, (socklen_t *)&addrlen); 
दोनों कार्यों

पैरामीटर के रूप में pingaddr सूचक का उपयोग करता है, लेकिन इस sendto() समारोह में क्योंकि परहेज करना चाहिए ICMP पैकेट के गंतव्य आईपी इंगित करने के लिए प्रयोग किया जाता है लेकिन recvfrom() में मेजबान है कि के आईपी वापस पाने के लिए प्रयोग किया जाता है जवाब दे रहे।

मान लें कि pingaddr एक आईपी के साथ सेट नहीं है। आपके पहले ICMP_REQUEST के बाद पहला गेटवे आपको ICMP_DEST_UNREACH के साथ जवाब देगा और ... त्रुटि आती है ... जब रिकॉर्फ़म कहा जाता है, तो पिंगड्रर संरचना गेटवे के आईपी के साथ ओवरराइट की जाएगी।

SO ... दूसरे पिंग से आप गेटवे आईपी पर इंगित करेंगे, जाहिर है, मौजूद है और ICMP_ECHOREPLY के साथ जवाब देगा।

समाधान:

बचने sendto() और recvfrom() दोनों के लिए एक ही sockaddr_in संरचना सूचक गुजरती हैं।

संबंधित मुद्दे