我想发送ICMP请求消息与一些数据在最后。我只能发送没有数据的简单ICMP请求。当我想在缓冲区中的icmp结构的末尾添加一些字符时,不发送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");
}
}发布于 2017-10-19 23:48:18
您需要重新计算ICMP头中的校验和,并将字节附加到标头中。
实现这一目标的最佳方法是初始化* icmp_send以指向您的buff字符串,然后在那里损坏您的字段。这将避免您从memcpy到* icmp_send到buff。下面是一个例子。
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_send和buff是不相关的。
此代码发送带有数据的ICMP回送请求,接收ICMP回送答复。与tcpdump一起进行测试,以验证数据包是否在主机之间交换。
...
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函数)
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;
}希望这能有所帮助!
https://stackoverflow.com/questions/46828866
复制相似问题