2017-10-19 248 views
0

我想發送帶有一些數據的ICMP請求消息。我只能發送沒有數據的簡單ICMP請求。當我想在緩衝區的icmp結構的末尾添加一些字符時,不發送ICMP請求。當我將sendto函數中的結束字符或更改發送消息的大小刪除到sizeof(icmp)時,消息會正常發送。哪裏不對?帶數據的ICMP請求

在ping函數中的參數很好。

void ping(struct sockaddr_in *addr) { 
    int sd; 
    const int val=255; 
    struct sockaddr_in r_addr; 
    sd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); 
    if (sd < 0) { 
     perror("socket"); 
     return; 
    } 
    if (setsockopt(sd, SOL_IP, IP_TTL, &val, sizeof(val)) != 0) 
     perror("Set TTL option"); 
    if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) 
     perror("Request nonblocking I/O"); 
    socklen_t len=sizeof(r_addr); 
    struct icmp *icmp_send, *icmp_recv; 
    icmp_send->icmp_type = ICMP_ECHO; 
    icmp_send->icmp_code = 0; 
    icmp_send->icmp_id = getpid(); 
    icmp_send->icmp_seq = 1; 
    icmp_send->icmp_cksum = 0; 
    icmp_send->icmp_cksum = checksum(icmp_send, sizeof(icmp_send)); 
    unsigned char buff[2000]; 
    unsigned char*p = buff; 
    memcpy(buff, icmp_send, sizeof(icmp)); 
    p += sizeof(icmp); 
    *p = 'A'; 
    if (sendto(sd, (unsigned char*)buff, sizeof(icmp_send) + 1 , 0, (struct sockaddr*)addr, sizeof(*addr)) <= 0) { 
       printf("Send err.\n"); 
    } 
} 
+3

C!= C++。只使用您正在使用的語言標記,除非兩者實際相關。 – tambre

+2

您的* icmp_send未初始化。你顯然知道'perror',你爲什麼要打印「發送錯誤」。代替? – rustyx

+0

在發送數據包之前,您是否需要在修改緩衝區之後重新計算校驗和?這裏給出一個很好的例子: http://www.binarytides.com/icmp-ping-flood-code-sockets-c-linux/ –

回答

1

您需要重新計算ICMP標頭中的校驗和,並將字節附加到標頭。

最好的方法是初始化* icmp_send指向你的buff字符串,然後在那裏打亂你的領域。這會避免你從從* icmp_sendbuff。這是一個例子。

unsigned char buff[2000]; 
unsigned char *p = buff; 
struct icmp *icmp_send; 

icmp_send = (struct icmp *)buff; 
icmp_send->icmp_type = ICMP_ECHO; 
icmp_send->icmp_code = 0; 
icmp_send->icmp_id = getpid(); 
icmp_send->icmp_cksum = 0; 
icmp_send->icmp_seq = htons(1); 
p += sizeof(icmp_send); 
*p = 'A'; 

icmp_send->icmp_cksum = checksum(buff, sizeof(icmp_send) + 1); 

if (sendto(sd, (unsigned char*)buff, sizeof(icmp_send) + 1, 0, (struct sockaddr*)addr, sizeof(*addr)) <= 0) { 
      printf("Send err.\n"); 
} 

在上面的例子,則可以在有效載荷的大小調整到傳輸(sizeof(icmp_send) + 1)爲任意值。

UPDATE:正如評論利瑪竇,鑄造buffstruct icmp指針訪問然後修改ICMP領域可能不是安全的,如果你的平臺需要你正確處理訪問未對齊的字段結構。在通過網絡發送之前,將結構單獨編寫,然後將memcpy寫入buff即可。

回到您的代碼,您將在代碼示例中獲得更多詳細信息,並在以後完成代碼。這裏的想法是計算和複製指針之間的值,因爲* icmp_sendbuff是不相關的。

該代碼發送帶有數據的ICMP回顯請求,並接收ICMP回顯應答。與tcpdump一起測試以驗證數據包正在主機之間進行交換。

完成以下代碼(OT:如有需要,請務必通過網絡發送數據時使用hton功能)

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

void ping(struct sockaddr_in *addr) { 
    int sd; 
    const int val=255; 
    struct sockaddr_in r_addr; 
    sd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); 
    if (sd < 0) { 
     perror("socket"); 
     return; 
    } 
    if (setsockopt(sd, SOL_IP, IP_TTL, &val, sizeof(val)) != 0) 
     perror("Set TTL option"); 
    if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) 
     perror("Request nonblocking I/O"); 
    socklen_t len=sizeof(r_addr); 
    struct icmp *icmp_send, *icmp_recv; 
    icmp_send->icmp_type = ICMP_ECHO; 
    icmp_send->icmp_code = 0; 
    icmp_send->icmp_id = getpid(); 
    icmp_send->icmp_seq = htons(1); 
    unsigned char buff[2000]; 
    unsigned char*p = buff; 
    p += sizeof(icmp_send); 
    *p = 'A'; 

    icmp_send->icmp_cksum = 0; 
    memcpy(buff, icmp_send, sizeof(icmp_send)) ; 

    icmp_send->icmp_cksum = checksum(buff, sizeof(icmp_send) + 1); 
    memcpy(buff, icmp_send, sizeof(icmp_send)) ; 

    if (sendto(sd, (unsigned char*)buff, sizeof(icmp_send) + 1, 0, (struct sockaddr*)addr, sizeof(*addr)) <= 0) { 
       printf("Send err.\n"); 
    } 
} 

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

    struct sockaddr_in addr; 
    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; 
    } 


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

    ping(&addr); 
    return 0; 
} 

希望這有助於!

+1

「最好的方法是初始化'* icmp_send'指向你的'buff'字符串,然後在那裏打亂你的領域。「如果你的平臺不像x86那樣寬容訪問,你必須小心這種技巧。一般來說,最好是安全的使用'memcpy'。 –

+1

我根據你的評論更新了我的答案,謝謝Matteo。 –

+0

你好@stepanVich,你能接受答案嗎?或者,如果您需要更多信息,請告訴我如何提供幫助。 –