2017-10-19 166 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,你能接受答案吗?或者,如果您需要更多信息,请告诉我如何提供帮助。 –