首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ICMP请求与数据

ICMP请求与数据
EN

Stack Overflow用户
提问于 2017-10-19 11:29:29
回答 1查看 3.4K关注 0票数 2

我想发送ICMP请求消息与一些数据在最后。我只能发送没有数据的简单ICMP请求。当我想在缓冲区中的icmp结构的末尾添加一些字符时,不发送ICMP请求。当我删除sendto函数(Icmp)中发送消息的结尾字符或更改其大小时,该消息将正常发送。怎么啦?

ping函数中的参数是可以的。

代码语言:javascript
复制
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");
    }
}
EN

回答 1

Stack Overflow用户

发布于 2017-10-19 23:48:18

您需要重新计算ICMP头中的校验和,并将字节附加到标头中。

实现这一目标的最佳方法是初始化* icmp_send以指向您的buff字符串,然后在那里损坏您的字段。这将避免您从memcpy* icmp_sendbuff。下面是一个例子。

代码语言:javascript
复制
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:正如Matteo所评论的,如果您的平台需要正确处理对结构中对齐字段的访问,则将buff转换为struct icmp指针以访问ICMP字段可能是不安全的。在这种情况下,先分别编写结构,然后在通过网络发送memcpy之前将其发送到buff

回到您的代码,您将在代码示例中获得更多细节,并在下面完成代码。这里的想法是计算和复制指针之间的值,因为* icmp_sendbuff是不相关的。

此代码发送带有数据的ICMP回送请求,接收ICMP回送答复。与tcpdump一起进行测试,以验证数据包是否在主机之间交换。

代码语言:javascript
复制
...
unsigned char*p = buff;
p += sizeof(icmp_send);
*p = 'A';

// 'A' has been appended into buff right after the header in the previous
// lines, we therefore need to compute the checksum using buff, without
// overwriting 'A', this is why we copy the 8 byte long ICMP header here.
//
// buff looks like [ ICMP HEADER (8 bytes)]['A' (1 byte)]
//
// Before computing the checksum, we also make sure its value in the header
// is set to 0 (mandatory).
icmp_send->icmp_cksum = 0;
memcpy(buff, icmp_send, sizeof(icmp_send));

// Now we can compute the checksum, and have to instruct our checksum()
// function that we want to include the data ('A') by setting the length
// in the second argument to 8 (ICMP header length) + 1 (data length of 'A')
icmp_send->icmp_cksum = checksum(buff, sizeof(icmp_send) + 1);

// Checksum is now ok, lets copy it again to buff. Here we copy the whole
// header but the idea is just to update the ICMP checksum field.
memcpy(buff, icmp_send, sizeof(icmp_send));

// buff contains header + data, ok to send it. Note that you don't need to
// compute the checksum of the IP packet. The system does it for you because
// your socket was created with the IPPROTO_ICMP
if ( sendto(sd, (unsigned char*)buff, sizeof(icmp_send) + 1, 0, (struct sockaddr*)addr, sizeof(*addr)) <= 0 ) {
            printf("Send err.\n");
}

完整的代码(OT :如果需要的话,确保在通过网络发送数据时使用hton函数)

代码语言:javascript
复制
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;
}

希望这能有所帮助!

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46828866

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档