2011-11-27 15 views
6

यह आईपी प्रोटोकॉल के तहत ICMP सॉकेट उपयोग करने के लिए संभव है? हो सकता है कि कुछ इस तरह:ICMP सॉकेट (लिनक्स)

socket(PF_INET, <type>, IPPROTO_ICMP)?

क्या मैं < प्रकार> क्षेत्र में रखना चाहिए? मैंने SOCK_RAW का उपयोग करके कुछ उदाहरण देखे, लेकिन क्या ओएस प्रोटोकॉल को संभालने के लिए ओएस को अपना काम करने से रोक नहीं पाएगा?

और एक और बात। प्रोटोकॉल के साथ कोई बंदरगाह क्यों नहीं है, क्योंकि ओएस को आईसीएमपी डेटाग्राम भेजना चाहिए, इस बारे में ओएस कैसे जान सकता है?

उत्तर

7

हाँ, यह संभव है, के बाद से ping आदेश ICMP करता है।

शामिल है, आपको लगता है कि आदेश (रूट के तहत) strace कर सकते हैं syscalls पता लगाने के लिए।

आप उस कमांड के स्रोत कोड में भी देख सकते हैं, उदा। Debian's ping

और वहाँ तुम्हारी मदद करने liboping पुस्तकालय है ...

14

लिनक्स एक विशेष ICMP सॉकेट प्रकार आप के साथ उपयोग कर सकते हैं:

socket(PF_INET, SOCK_DGRAM IPPROTO_ICMP); 

यह अनुमति देता है आप केवल ICMP गूंज अनुरोध भेजने के लिए कर्नेल इसे विशेष रूप से संभालेगा (मैच अनुरोध/प्रतिक्रियाएं, चेकसम भरें)। अगर एक special sysctl सेट कर दिया जाता

यह केवल काम करता है। डिफ़ॉल्ट रूप से भी रूट इस प्रकार की सॉकेट का उपयोग नहीं कर सकता है। आप उन उपयोगकर्ता समूहों को निर्दिष्ट करते हैं जो इसे एक्सेस कर सकते हैं। , ICMP सॉकेट उपयोग करने के लिए रूट (समूह 0) की अनुमति देने के कार्य करें:

#include <stdio.h> 
#include <errno.h> 
#include <string.h> 
#include <stdlib.h> 
#include <sys/socket.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <netinet/ip_icmp.h> 
#include <arpa/inet.h> 
#include <sys/select.h> 

//note, to allow root to use icmp sockets, run: 
//sysctl -w net.ipv4.ping_group_range="0 0" 

void ping_it(struct in_addr *dst) 
{ 
    struct icmphdr icmp_hdr; 
    struct sockaddr_in addr; 
    int sequence = 0; 
    int sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_ICMP); 
    if (sock < 0) { 
     perror("socket"); 
     return ; 
    } 

    memset(&addr, 0, sizeof addr); 
    addr.sin_family = AF_INET; 
    addr.sin_addr = *dst; 

    memset(&icmp_hdr, 0, sizeof icmp_hdr); 
    icmp_hdr.type = ICMP_ECHO; 
    icmp_hdr.un.echo.id = 1234;//arbitrary id 

    for (;;) { 
     unsigned char data[2048]; 
     int rc; 
     struct timeval timeout = {3, 0}; //wait max 3 seconds for a reply 
     fd_set read_set; 
     socklen_t slen; 
     struct icmphdr rcv_hdr; 

     icmp_hdr.un.echo.sequence = sequence++; 
     memcpy(data, &icmp_hdr, sizeof icmp_hdr); 
     memcpy(data + sizeof icmp_hdr, "hello", 5); //icmp payload 
     rc = sendto(sock, data, sizeof icmp_hdr + 5, 
         0, (struct sockaddr*)&addr, sizeof addr); 
     if (rc <= 0) { 
      perror("Sendto"); 
      break; 
     } 
     puts("Sent ICMP\n"); 

     memset(&read_set, 0, sizeof read_set); 
     FD_SET(sock, &read_set); 

     //wait for a reply with a timeout 
     rc = select(sock + 1, &read_set, NULL, NULL, &timeout); 
     if (rc == 0) { 
      puts("Got no reply\n"); 
      continue; 
     } else if (rc < 0) { 
      perror("Select"); 
      break; 
     } 

     //we don't care about the sender address in this example.. 
     slen = 0; 
     rc = recvfrom(sock, data, sizeof data, 0, NULL, &slen); 
     if (rc <= 0) { 
      perror("recvfrom"); 
      break; 
     } else if (rc < sizeof rcv_hdr) { 
      printf("Error, got short ICMP packet, %d bytes\n", rc); 
      break; 
     } 
     memcpy(&rcv_hdr, data, sizeof rcv_hdr); 
     if (rcv_hdr.type == ICMP_ECHOREPLY) { 
      printf("ICMP Reply, id=0x%x, sequence = 0x%x\n", 
          icmp_hdr.un.echo.id, icmp_hdr.un.echo.sequence); 
     } else { 
      printf("Got ICMP packet with type 0x%x ?!?\n", rcv_hdr.type); 
     } 
    } 
} 

int main(int argc, char *argv[]) 
{ 
    if (argc != 2) { 
     printf("usage: %s destination_ip\n", argv[0]); 
     return 1; 
    } 
    struct in_addr dst; 

    if (inet_aton(argv[1], &dst) == 0) { 

     perror("inet_aton"); 
     printf("%s isn't a valid IP address\n", argv[1]); 
     return 1; 
    } 

    ping_it(&dst); 
    return 0; 
} 

ध्यान दें कि गिरी:

sysctl -w net.ipv4.ping_group_range="0 0" 

यहाँ एक ICMP गूंज अनुरोध भेजने की बहुत ही बुनियादी उपयोग प्रदर्शित करने के लिए एक उदाहरण कार्यक्रम है प्रेषित() कॉल को खारिज कर देगा और विफल करेगा यदि भेजे गए डेटा में उचित आईसीएमपी हेडर के लिए कमरा नहीं है, और आईसीएमपी type 8 (आईसीएमपी_ईसीएचओ) होना चाहिए और आईसीएमपी कोड 0 होना चाहिए।

+0

क्या आप सृजन निर्माण नहीं करेंगे गलत हो? SOCK_DRAM ओएसआई/आईएसओ 7 परत स्टैक के परिवहन परत का हिस्सा है ... ICMP नेटवर्क परत में परत 3 का हिस्सा है। मैं स्टीवंस द्वारा यूनिक्स सॉकेट प्रोग्रामिंग पढ़ रहा हूं और ऐसा करने के लिए आपको आईसीएमपी पैकेट प्राप्त करने के लिए एक SOCK_RAW घोषित करने की आवश्यकता है। इस कोड को देखें [यहां] (http://www.binarytides.com/packet-sniffer-code-c-linux/) –

+1

@Florida_Jake SOCK_DGRAM परत 7 से बंधे नहीं है। यूनिक्स सॉकेट प्रोग्रामिंग पुस्तक वर्णन नहीं करती है कि कैसे लिनक्स विशिष्ट आईसीएमपी गूंज सॉकेट का उपयोग करने के लिए, यहां उदाहरण काम करता है। आईसीएमपी संदेशों को भेजने/प्राप्त करने का पारंपरिक तरीका वास्तव में SOCK_RAW का उपयोग करना है, और आईसीएमपी संदेशों को स्वयं बनाना है। मेरे द्वारा पोस्ट किया गया कोड आईसीएमपी गूंज संदेश भेजने और प्राप्त करने के लिए वैकल्पिक, लिनक्स विशिष्ट सुविधा का उपयोग करता है। – nos

+0

अच्छा, परिणाम तुरंत प्राप्त करने के लिए मेरे लिए बहुत अच्छा काम किया। अब आईपीवी 6 पिंग को देखने का समय है। :) –

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