how to sniff all ICMP packets using RAW sockets

2020-07-27 05:37发布

I am learning RAW sockets. In the below code i am trying to print all ICMP packets headers information. Looks like some error in the code. Can anyone please help me where i am wrong.

# 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条回答
家丑人穷心不美
2楼-- · 2020-07-27 06:07

You are trying to print raw packet data (including headers) as a string. In this case, E which is ascii 0x45 is the first byte of the IP header. The upper 4 bits mean "IPv4" and the lower 4 bits is the IHL (number of 32-bit words in the IP header) which is 5*4 = 20 bytes.

To properly access this data you should use the IP/ICMP header structs provided by linux. I've updated your code a little to illustrate:

# 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);
  }
}

Now, if I run that and ping 127.0.0.1: you see output like this:

 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

Here this shows a msgtype 8 (echo request) and msgtype 0 (echo reply). Beware that when accessing data this way from an array you can run into alignment problems (x86/x64 are happy to handle it for you, but other architectures may not be so generous). I'll leave that as an exercise to the reader ;).

查看更多
登录 后发表回答