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
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 ;).