您正在尝试将原始数据包数据(包括标头)打印为字符串。在这种情况下,E
ascii0x45
是 IP 标头的第一个字节。高 4 位表示“IPv4”,低 4 位是 IHL(IP 标头中 32 位字的数量),即 5*4 = 20 字节。
要正确访问这些数据,您应该使用 linux 提供的 IP/ICMP 标头结构。我已经更新了你的代码来说明:
# include <unistd.h>
# include <sys/socket.h>
# include <sys/types.h>
# include <string.h>
# include <netinet/in.h>
# include <stdio.h>
# include<stdlib.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
main(){
int sockfd,retval,n;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
char buf[10000];
int i;
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd < 0){
perror("sock:");
exit(1);
}
clilen = sizeof(struct sockaddr_in);
while(1){
printf(" before recvfrom\n");
n=recvfrom(sockfd,buf,10000,0,(struct sockaddr *)&cliaddr,&clilen);
printf(" rec'd %d bytes\n",n);
struct iphdr *ip_hdr = (struct iphdr *)buf;
printf("IP header is %d bytes.\n", ip_hdr->ihl*4);
for (i = 0; i < n; i++) {
printf("%02X%s", (uint8_t)buf[i], (i + 1)%16 ? " " : "\n");
}
printf("\n");
struct icmphdr *icmp_hdr = (struct icmphdr *)((char *)ip_hdr + (4 * ip_hdr->ihl));
printf("ICMP msgtype=%d, code=%d", icmp_hdr->type, icmp_hdr->code);
}
}
现在,如果我运行它并且ping 127.0.0.1
: 你会看到这样的输出:
before recvfrom
rec'd 84 bytes
IP header is 20 bytes.
45 00 00 54 00 00 40 00 40 01 3C A7 7F 00 00 01
7F 00 00 01 08 00 A9 DF 11 66 00 01 9A 77 1A 51
00 00 00 00 BA 1D 0F 00 00 00 00 00 10 11 12 13
14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23
24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33
34 35 36 37
ICMP msgtype=8, code=0 before recvfrom
rec'd 84 bytes
IP header is 20 bytes.
45 00 00 54 8D F3 00 00 40 01 EE B3 7F 00 00 01
7F 00 00 01 00 00 B1 DF 11 66 00 01 9A 77 1A 51
00 00 00 00 BA 1D 0F 00 00 00 00 00 10 11 12 13
14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23
24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33
34 35 36 37
ICMP msgtype=0, code=0 before recvfrom
这里显示了一个 msgtype 8(回显请求)和一个 msgtype 0(回显回复)。请注意,当以这种方式从数组访问数据时,您可能会遇到对齐问题(x86/x64 很乐意为您处理它,但其他架构可能不会那么慷慨)。我将把它作为练习留给读者;)。