带数据的ICMP请求
我想发送ICMP请求消息,末尾有一些数据。我只能发送没有数据的简单ICMP请求。当我想在缓冲区中icmp结构的末尾添加一些字符时,不会发送icmp请求。当我删除结束字符或将sendto函数中发送消息的大小更改为sizeof(icmp)时,消息正常发送。怎么了 ping函数中的参数没有问题带数据的ICMP请求,c,sockets,icmp,C,Sockets,Icmp,我想发送ICMP请求消息,末尾有一些数据。我只能发送没有数据的简单ICMP请求。当我想在缓冲区中icmp结构的末尾添加一些字符时,不会发送icmp请求。当我删除结束字符或将sendto函数中发送消息的大小更改为sizeof(icmp)时,消息正常发送。怎么了 ping函数中的参数没有问题 void ping(struct sockaddr_in *addr) { int sd; const int val=255; struct sockaddr_in r_addr;
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");
}
}
void ping(结构sockaddr\u in*addr){
国际标准差;
常数int val=255;
r_addr中的结构sockaddr_;
sd=插座(PF_INET、SOCK_RAW、IPPROTO_ICMP);
如果(sd<0){
佩罗(“插座”);
返回;
}
如果(设置锁定选项(sd、SOL_IP、IP_TTL和val、sizeof(val))!=0)
perror(“设置TTL选项”);
如果(fcntl(sd,F_设置FL,O_非块)!=0)
perror(“请求非阻塞I/O”);
socklen_t len=大小(r_addr);
结构icmp*icmp\u发送,*icmp\u接收;
icmp_发送->icmp_类型=icmp_回显;
icmp_发送->icmp_代码=0;
icmp_发送->icmp_id=getpid();
icmp_发送->icmp_顺序=1;
icmp\u发送->icmp\u校验和=0;
icmp_发送->icmp_校验和=校验和(icmp_发送,sizeof(icmp_发送));
未签名字符buff[2000];
无符号字符*p=buff;
memcpy(buff、icmp_send、sizeof(icmp));
p+=sizeof(icmp);
*p=‘A’;
如果(sendto(sd,(unsigned char*)buff,sizeof(icmp_send)+1,0,(struct sockaddr*)addr,sizeof(*addr))您需要在icmp标头中使用附加到标头的字节重新计算校验和
实现这一点的最佳方法是初始化*icmp\u send
以指向您的buff
字符串,然后在那里损坏您的字段。这将避免您从*icmp\u send
到buff
访问memcpy
。下面是一个示例
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");
}
在下文中填写代码(OT:如果需要,确保在通过网络发送数据时使用hton功能)
无符号短校验和(void*b,int len)
{无符号短*buf=b;
无符号整数和=0;
无符号短结果;
对于(总和=0;len>1;len-=2)
总和+=*buf++;
如果(len==1)
sum+=*(无符号字符*)buf;
总和=(总和>>16)+(总和&0xFFFF);
总和+=(总和>>16);
结果=~sum;
返回结果;
}
无效ping(在*addr中的结构sockaddr_){
国际标准差;
常数int val=255;
r_addr中的结构sockaddr_;
sd=插座(PF_INET、SOCK_RAW、IPPROTO_ICMP);
如果(sd<0){
佩罗(“插座”);
返回;
}
如果(设置锁定选项(sd、SOL_IP、IP_TTL和val、sizeof(val))!=0)
perror(“设置TTL选项”);
如果(fcntl(sd,F_设置FL,O_非块)!=0)
perror(“请求非阻塞I/O”);
socklen_t len=大小(r_addr);
结构icmp*icmp\u发送,*icmp\u接收;
icmp_发送->icmp_类型=icmp_回显;
icmp_发送->icmp_代码=0;
icmp_发送->icmp_id=getpid();
icmp_send->icmp_seq=htons(1);
未签名字符buff[2000];
无符号字符*p=buff;
p+=sizeof(icmp_发送);
*p=‘A’;
icmp\u发送->icmp\u校验和=0;
memcpy(buff、icmp_send、sizeof(icmp_send));
icmp_发送->icmp_校验和=校验和(buff,sizeof(icmp_发送)+1);
memcpy(buff、icmp_send、sizeof(icmp_send));
如果(SDETO(未签名的CHAR *)buff,siZeof(ICMPSORDER)+1, 0,(StutoSokAdDr*),ADDR,siZeof(*ADDR))C!= C++ +。标签只与您使用的语言,除非两者实际上是相关的。您的<代码> *ICMPXEng/<代码>未初始化。您显然知道<代码> PrRor ,为什么打印“发送错误”。相反?您不需要在修改缓冲区后和发送数据包之前重新计算校验和吗?这里给出了一个很好的例子:“实现这一点的最佳方法是初始化*icmp_send
以指向您的buff
字符串,然后在那里损坏字段。”如果您的平台没有x86那么宽容不一致的访问,那么您必须小心这类把戏。通常情况下,最好安全地使用memcpy
。我根据您的评论更新了我的答案,谢谢Matteo。您好@stepanVich,您能接受答案吗?或者如果您需要更多信息,请告诉我如何更新我可以帮忙。
...
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");
}
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;
}