您需要重新计算ICMP标头中的校验和,并将字节附加到标头。
最好的方法是初始化* icmp_send
指向你的buff
字符串,然后在那里打乱你的领域。这会避免你从从* 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:正如评论利玛窦,铸造buff
到struct icmp
指针访问然后修改ICMP领域可能不是安全的,如果你的平台需要你正确处理访问未对齐的字段结构。在通过网络发送之前,将结构单独编写,然后将memcpy
写入buff
即可。
回到您的代码,您将在代码示例中获得更多详细信息,并在以后完成代码。这里的想法是计算和复制指针之间的值,因为* icmp_send
和buff
是不相关的。
该代码发送带有数据的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;
}
希望这有助于!
C!= C++。只使用您正在使用的语言标记,除非两者实际相关。 – tambre
您的* icmp_send未初始化。你显然知道'perror',你为什么要打印“发送错误”。代替? – rustyx
在发送数据包之前,您是否需要在修改缓冲区之后重新计算校验和?这里给出一个很好的例子: http://www.binarytides.com/icmp-ping-flood-code-sockets-c-linux/ –