如何使用RAW套接字嗅探所有ICMP数据包

时间:2013-02-12 16:43:58

标签: c linux sockets raw-sockets

我正在学习RAW套接字。在下面的代码中,我试图打印所有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>

main(){
int sockfd,retval,n;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
char buf[10000]; 

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);
    buf[n]='\0';
    printf(" msg from client = %s\n",buf);
}
}

o/p

before recvfrom
rec'd 60 bytes
msg from client = E
before recvfrom
rec'd 52 bytes

1 个答案:

答案 0 :(得分:7)

您正在尝试将原始数据包数据(包括标题)打印为字符串。在这种情况下,as E ascii 0x45是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(echo请求)和msgtype 0(echo reply)。请注意,当从阵列以这种方式访问​​数据时,您可能遇到对齐问题(x86 / x64很乐意为您处理它,但其他架构可能不那么慷慨)。我将把它作为练习留给读者;)。