2017-10-19 9 views
0

最後にいくつかのデータを含むICMPリクエストメッセージを送信します。私は単純なICMP要求をデータなしで送ることができます。バッファにあるicmp構造の最後にいくつかの文字を追加したい場合、ICMPリクエストは送信されません。 sizeof(icmp)にsendto関数の終了文字を削除したり、送信メッセージのサイズを変更すると、メッセージは正常に送信されます。なにが問題ですか?データ付き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'について知っています、なぜあなたはprintf" send err "をしますか?代わりに? – rustyx

+0

バッファを変更してパケットを送信する直前にチェックサムを再計算する必要はありませんか?良い例がここに与えられます: http://www.binarytides.com/icmp-ping-flood-code-sockets-c-linux/ –

答えて

1

ヘッダーに追加するバイトでICMPヘッダーのチェックサムを再計算する必要があります。

* icmp_sendbuff文字列を指すように初期化し、そこでフィールドをマングルすることをお勧めします。これにより、memcpyから* 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:マッテオでコメントしたようご使用のプラットフォームが適切な構造でずれフィールドへのアクセスを処理するためにあなたを必要とする場合、ICMPフィールドを変更後、アクセスするためにstruct icmpポインタにbuffをキャストすることは安全ではないかもしれません。ネットワークを介して送信する前に、memcpybuffに別々に書いてください。この場合は、移動する方法です。

コードに戻って、コードサンプルと完全なコードについて詳しく説明します。ここでの考え方は、ポインタ間の値を計算してコピーすることです。* 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'文字列は、あなたのフィールドをmangleします。あなたのプラットフォームがx86と同じように揃っていない場合には、この種のトリックに注意する必要があります。一般的には安全でmemcpyを再生する方が良いでしょう。 –

+1

あなたの発言に応じて私の答えを更新しました、ありがとうございました。 –

+0

こんにちは@stepanVich、あなたは答えを受け入れることができますか?または、より多くの情報が必要な場合は、私がどのように手助けできるか教えてください。 –