PDA

View Full Version : raw ICMP


Encrypted
05-24-2003, 12:39 PM
Hi, me again ;)

I just wrote a simple program that sends an ICMP Echo Request to some host.
Im happy that it works, (at least, it seems so), but the thing is i dont get entirely what it does, and i do want to know how it works.


#define __USE_BSD
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define __FAVOR_BSD
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>

unsigned short in_cksum( unsigned short *addr, int len ); /* the function for calculating the checksum */

int main( int argc, char **argv )
{

int sockfd, packet_size, res, size = 65535;
int on = 1;
char *packet, *recv;

packet_size = (sizeof(struct ip) + sizeof(struct icmphdr));
packet = malloc(packet_size);
struct sockaddr_in remote, from;
struct hostent *host = gethostbyname(argv[2]);
struct ip *iphdr = (struct ip *)packet;
struct in_addr saddr, daddr;

struct icmphdr *icmph = (struct icmphdr *)(packet + sizeof(struct ip));
struct icmphdr *icmphr;

bzero(packet, sizeof(struct iphdr) + sizeof(struct icmphdr));
saddr.s_addr = inet_addr(argv[1]);
daddr.s_addr = inet_addr(argv[2]);



if(getuid() != 0)
{
printf("You must be root\n");
exit(1);
}

if( ( sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP )) < 0 )
{
perror("socket");
printf("Prob socket\n");
exit(1);
}

if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL,(char *)&on,sizeof(on)) < 0)
{
perror("setsockopt");
printf("Prob with setting setsockopt\n");
exit(1);
}

memset(&remote, '\0', sizeof(remote) );

memset((char *)icmph,'\0',sizeof(struct icmphdr));

icmph->type = ICMP_ECHO;
icmph->code = 0;
icmph->un.echo.id = 0;
icmph->un.echo.sequence = 0;
icmph->checksum = 0;
icmph->checksum = in_cksum((unsigned short *)icmph,sizeof(struct icmphdr));

bzero(packet, sizeof(struct iphdr));
iphdr->ip_v = 4;
iphdr->ip_hl = 5;
iphdr->ip_len = packet_size;
iphdr->ip_off = 0;
iphdr->ip_ttl = IPDEFTTL;
iphdr->ip_p = IPPROTO_ICMP;
iphdr->ip_src = saddr;
iphdr->ip_dst = daddr;
iphdr->ip_sum = (unsigned short)in_cksum((unsigned short *)iphdr, sizeof(struct ip));


remote.sin_family = AF_INET;
remote.sin_addr = daddr;


from.sin_family = AF_INET;
from.sin_addr.s_addr = INADDR_ANY;
memset(&from.sin_port, '\0', sizeof(from.sin_port));

if( (res = sendto(sockfd, packet, packet_size, 0x0, (struct sockaddr *)&remote, sizeof(from))) != -1 )
printf("Sent!\n" );
else
{
perror("sendto");
exit(1);
}

if ( recvfrom( sockfd, recv, sizeof(struct icmphdr)+sizeof(struct ip), 0x0, (struct sockaddr *)&from, &size ) < 0 )
{
perror("recvfrom");
}

icmphr = (struct icmphdr *)(recv+sizeof(struct ip));

if( icmphr->type == ICMP_ECHOREPLY )
{
printf("We have a reply\n");
exit(0);
}

exit(0);
}

unsigned short in_cksum(unsigned short *addr,int len)
{
register int sum = 0;
u_short answer = 0;
register u_short *w = addr;
register int nleft = len;

while (nleft > 1) {
sum += *w++;
nleft -= 2;
}


if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}


sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return(answer);
}



The part i dont get is:

printf(" OK, receiving now\n" );
while((bytes = recvfrom(sockfd, packet, 65535, 0,(struct sockaddr *)&sock_raw, sizeof(sock_raw))) > 0 )
printf(" Done, receiving\n" );
if(icmph->code == ICMP_ECHOREPLY){ printf("REPLY!! :)\n"); exit(0);}
else {
printf("Nope.... sorry\n" );

And what i dont get is; where does icmph come from? i know i declared it before,
but this time i put data in it from the reply, so how does it 'know' icmph?
I mean, does the buffer 'packet' contain after the recvfrom() two headers? (IP and ICMP)?

and allthough it works and i dont know exactly what it does is because i copied it.

thanks,
encrypted

edit #1
Ok, i just found out that recvfrom() returns -1.
recvfrom: Bad Address
Im gonna figure this out now, if anyone has any tips or something? thanks
edit #2
Ok, wel i made an extra struct sockaddr_in for my own host information, but it still gives the same error.

struct sockaddr_in from;
from.sin_family = AF_INET;
from.sin_addr.s_addr = INADDR_ANY;
memset(&(from.sin_zero), '\0', 8);

No port since i use ICMP right?
thanks

mlampkin
05-24-2003, 10:09 PM
The recvfrom does not automatically fill in that structure... you must do it yourself via a copy... or by casting your recv buffer to the appropriate type...

There is one catch though which prevents you doing a direct copy from buff[0] over to your ICMP structure... the rcvfrom is actually returning the entire IP packet... which means you have to skip over the IP header to get to the actual data (the ICMP segment)...

The ip header has a value indicating its length (the length of the header only) located at byte 0, bits 4 thru 7 (inclusive) which simplifies things... just take your rcvd data and do the following:

int ip_hdr_len = 4 * ( buff[0] & 0x0f );

Btw, the 4 * part is because the length is given as the number of 32 bit blocks the header consumes (guess I could have mentioned that earlier ;-) )...

Now that you have the offset to the ICMP segment you can simply do a cast e.g. (struct icmp *) (buff + ip_hdr_len) and manipulate the data or do a memcpy (or similar) ...

Hope that helps...

Didn't look too closely at the rest of the code you posted... figure we'll cover this one step at a time ;-)

Michael

Encrypted
05-25-2003, 11:20 AM
Hi,

i dont really get it yet.

I mean, does it go like this:

char *packet;
struct icmphdr *received;

recvfrom(sockfd, packet, (sizeof(struct ip)+ sizeof(struct icmphdr), 0,(struct sockaddr *)&from, sizeof(from))

Now we have entire packet in the buffer 'packet'.

received = (struct icmphdr *) (packet + sizeof(struct ip));
/* and then we can refer to it like this: */
if( received->type == ICMP_ECHOREPLY )
printf("We have a reply!\n" );

Was that what you meant?

And btw, im getting a "Bad Address" with recvfrom all the time

struct sockaddr_in from;
from.sin_family = AF_INET;
from.sin_addr.s_addr = htonl(INADDR_ANY);
memset(&(from.sin_zero), '\0', 8);

...
...

recvfrom(sockfd, packet, 65535, 0,(struct sockaddr *)&from, sizeof(from))


recvfrom: Badd Address


thanks
encrypted

RobSeace
05-25-2003, 05:58 PM
The last arg of recvfrom() is supposed to be a POINTER to the
length, not the actual length... You set it to the max length you
have room to hold in your address struct, and on return it's set
to the real length used by whoever you received a message from...

mlampkin
05-26-2003, 12:04 AM
If buff was your input buffer and struct icmphdr is actually defined starting with the type variable (I normally define my own struct) and its type was void then doing:

struct icmphdr * hdr = (struct icmphdr *) (buff + ( 4 * ( buff[0] & 0x0f )));

type = hdr->type;

etc.

The calculation in the first line is used to determine how much space the ip header takes up and skips over it to point at the start of the icmp packet proper...


>>EDITED / REMOVED PREVIOUS COMMENT<<
>>DUE TO A MODERATOR BRAIN CRAMP<<

Lol... don't ask... just read some of the previously posted code wrong... :-)



Michael

mlampkin
05-26-2003, 01:15 AM
Ok... posted a comment in the previous message... then edited it out cause I thought I was confused... to be honest, I had read some of the code wrong in the checksum area and the point I made was invalid...

But I knew there was SOMETHING wrong with the checksum code... :-)


if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}


The last byte added during the summation, if there is an odd number of bytes in the packet, should be padded to the right (left shifted by 8 bits) first... that is unless my mind has completely rotted out while I wasn't paying attention...

Ok, assuming I am correct about the padding... then the given code will work correctly if you're running on a big endian machine... but does not provide correct padding on a little endian system...

Replacing the two lines with something like:

sum += ( ( uint32_t ) ( * (uint8_t *) w ) ) << 8;

should do the trick though...

Of course, I may have just #@$#'ed up again on the post... and could be completely off the mark... the ole brain really doesn't seem to be working at 100% today for some reason... perhaps it the lack of beer or something...

Michael

Encrypted
05-28-2003, 03:54 PM
Hi,


thanks for all the help,
but it still gives, recvfrom: bad address, and after that a segmentation fault.

if have this right now:

int size = sizeof(struct sockaddr);
....
....
recvfrom( sockfd, recv, sizeof(struct icmphdr)+sizeof(struct ip), 0x0, (struct sockaddr *)&from, &size )

I saw this in a tutorial, but still no differences.

And that segmentation fault comes after recvfrom(), so this piece of code has to contain it:

icmphr = (struct icmphdr *)(recv+sizeof(struct ip));

if( icmphr->type == ICMP_ECHOREPLY )
{
printf("We have a reply\n");
exit(0);
}

But thats the same way i initialize the received ICMP header as the ICMP header im sending out....

thanks,
encrypted

RobSeace
05-28-2003, 06:29 PM
Well, call me paranoid, but I really wouldn't use the name "recv"
for your buffer, as you appear to be doing, if I were you... Because,
of course, recv() is a valid function, so even if you forget to declare
your buffer with that name in a certain context, the compiler will
happily let you use the name, since it knows it's a function, so it'll
give you the location of that function as a pointer, and trying to use
that as a buffer would be quite bad... ;-)

And, is "from" a struct sockaddr_in, I assume? If so, you'd actually
want size to be "sizeof (struct sockaddr_in)", not "struct sockaddr"...
(Even though, those should generally be the same size, anyway...)
Easiest (and, more readable) just to do "sizeof (from)", anyway...

Loco
05-28-2003, 07:21 PM
Hi,

Looking at the original code, I see the following:

...
while((bytes = recvfrom(sockfd, packet, 65535, 0,(struct sockaddr *)&sock_raw, sizeof(sock_raw))) > 0 )
printf(" Done, receiving\n" );
...

But packet was malloc'ed like this:
packet_size = (sizeof(struct ip) + sizeof(struct icmphdr));
packet = malloc(packet_size);

Which by the way could be generating the segmentation fault...

However, I have seen in the other posts that you refer to this line as:

recvfrom( sockfd, recv, sizeof(struct icmphdr)+sizeof(struct ip), 0x0, (struct sockaddr *)&from, &size )

Which one is correct?

BTW, the recv variable is not defined anywhere in your code...

Why don't you update your code by editting the initial post so we can check the last code.

Regards

Encrypted
05-28-2003, 09:09 PM
Hi,

Sorry for not updating the code,
my code is a mess when im experimenting.
Anyway, its updated now to the last changes i made. (the first code i posted is updated)
and for the record, the program does send out an ICMP packet and it does get a reply (checked with tcpdump)
weird that i dont need a pseudo_header for ICMP :?, or do i?
at least tcpdump doesnt says 'bad checksum'....


thanks,
encrypted

mlampkin
05-29-2003, 12:27 AM
Someone please grab, compile and check the code at:

http://www.cognitus.net/html/tutorial/rawsock/socketfaq_icmp_0.c

I do NOT have access to a unix dev environment right now... especially not root access... so cannot check if it runs correctly... much less if it compiles...

Oh, and a side note... Cygwin STILL SUCKS... lol.

Actually did start to try it with that but no end to problems... including (among other things) no IP_HDRINCL flag for setsockopt (which is why, in case someone else wants to try it on Cygwin and help me out, that I am letting the system set the IP header segment instead of doing it manually etc.)... what seems like a complete lack of man pages describing Cygwin system specific behaviour... just to name two...

Michael

mlampkin
05-29-2003, 06:33 AM
I updated the code I posted...

Cause...

I found a couple errors... including the fact that doing a recvfrom under cygwin (and who knows what other systems) and specifying a buffer over a certain limit causes the size_t param (which I could have sworn was supposed to be UNSIGNED!) to roll over...

Hopefully this version will help with the questions.. again, I am letting the system create the header (so no control over ttl etc) since Cygwin does not give access to the setsockopt IP_HDRINCL option at all... i.e. you can't do the ip header yourself!

...and before anyone says anything... yeah, I used a couple deprecated functions... but I do this in my spare time... so live with it!!! ;-)


Michael

Loco
05-29-2003, 03:47 PM
HI,

I compiled your code, and it really does core dumps...

It is hapenning in the recvfrom() system call. I guess it is because of the "recv" variable not being initialized (it is never malloc'ed or referenced to any memory space).

I will check initializing it to some size...

I'll get back to you as soon as I check

Loco
05-29-2003, 03:59 PM
Well, in fact it was this small part of the code that was causing the problems...

I malloc'ed paccket_size bytes for "recv" pointer and it worked quite fine...

I ran it from my machine (192.168.0.1) and it did this:

[09:41:21][root@tstint:~/sources/c/tests]$ ./icmp_raw 192.168.0.100 192.168.0.1
Sent!
[09:41:30][root@tstint:~/sources/c/tests]$ ./icmp_raw 192.168.0.100 192.168.0.1
Sent!
[09:41:51][root@tstint:~/sources/c/tests]$ ./icmp_raw 192.168.0.1 192.168.0.3
Sent!
We have a reply
[09:42:08][root@tstint:~/sources/c/tests]$ ./icmp_raw 192.168.0.3 192.168.0.1
Sent!
[09:42:20][root@tstint:~/sources/c/tests]$ ./icmp_raw 192.168.0.1 192.168.0.1
Sent!
[09:43:21][root@tstint:~/sources/c/tests]$ ./icmp_raw 192.168.0.1 192.168.0.2
Sent!
We have a reply

And the "tcpdump icmp" command showed this:

09:39:51][root@tstint:~/sources/c/tests]$ tcpdump icmp
Kernel filter, protocol ALL, TURBO mode (575 frames), datagram packet socket
tcpdump: listening on all devices
09:40:02.124912 lo > 192.168.0.100 > myhost.my-domain.com: icmp: echo request
09:40:02.124912 lo < 192.168.0.100 > myhost.my-domain.com: icmp: echo request
09:40:24.704912 eth1 < 192.168.0.110 > myhost.my-domain.com: icmp: 192.168.0.110 udp port 1705 unreachable
09:40:24.704912 eth1 < 192.168.0.110 > myhost.my-domain.com: icmp: 192.168.0.110 udp port 1705 unreachable
09:40:24.704912 eth1 < 192.168.0.110 > myhost.my-domain.com: icmp: 192.168.0.110 udp port 1705 unreachable
09:41:00.614912 lo > localhost > localhost: icmp: localhost udp port biff unreachable (DF) [tos 0xc0]
09:41:00.614912 lo < localhost > localhost: icmp: localhost udp port biff unreachable (DF) [tos 0xc0]
09:41:00.744912 lo > localhost > localhost: icmp: localhost udp port biff unreachable (DF) [tos 0xc0]
09:41:00.744912 lo < localhost > localhost: icmp: localhost udp port biff unreachable (DF) [tos 0xc0]
09:41:30.644912 lo > 192.168.0.100 > myhost.my-domain.com: icmp: echo request
09:41:30.644912 lo < 192.168.0.100 > myhost.my-domain.com: icmp: echo request
09:41:51.384912 lo > 192.168.0.100 > myhost.my-domain.com: icmp: echo request
09:41:51.384912 lo < 192.168.0.100 > myhost.my-domain.com: icmp: echo request
09:42:08.404912 eth1 > myhost.my-domain.com > other-host.my-domain.com: icmp: echo request
09:42:08.404912 eth1 < other-host.my-domain.com > myhost.my-domain.com: icmp: echo reply
09:42:20.494912 lo > other-host.my-domain.com > myhost.my-domain.com: icmp: echo request
09:42:20.494912 lo < other-host.my-domain.com > myhost.my-domain.com: icmp: echo request
09:43:21.014912 lo > myhost.my-domain.com > myhost.my-domain.com: icmp: echo request
09:43:21.014912 lo < myhost.my-domain.com > myhost.my-domain.com: icmp: echo request
09:43:21.014912 lo > myhost.my-domain.com > myhost.my-domain.com: icmp: echo reply (DF)
09:43:21.014912 lo < myhost.my-domain.com > myhost.my-domain.com: icmp: echo reply (DF)
09:43:33.584912 eth1 > myhost.my-domain.com > yet-another-one.my-domain.com: icmp: echo request
09:43:33.594912 eth1 < yet-another-one.my-domain.com > myhost.my-domain.com: icmp: echo reply
09:43:46.564912 eth1 < 192.168.0.110 > myhost.my-domain.com: icmp: 192.168.0.110 udp port 1956 unreachable

24 packets received by filter


This is a live network, so some of the ICMP packets you see there are probably generated by the traffic...

BTW, myhost is 192.168.0.1, other-host is 192.168.0.3 and yet-another-one is 192.168.0.2... But I think you'd guess it anyway...

Hope this helps

Encrypted
05-29-2003, 04:57 PM
Thanks, that certainly helped,

it works here too now ;)
you have no idea how much this forum helped me 8)


encrypted

mlampkin
05-29-2003, 11:15 PM
Thanks are always welcome... but cash is always better...

Ok ok... before Rob / Loco etc. come after me... that WAS A JOKE... :-)

Michael

Loco
05-30-2003, 01:16 AM
Yes Michael, we all know you do this for the money, booze, fame and women!!!

But wait a second, from all the people I have helped (or not at all) I have received nothing but "thank you", so I think I'll have to switch to another forum...

I used to post a lot of replies (most of them helpful) in the SUN Java forum, and there I used to earn a lot of points and got to the second place in my good times... Anyway, after some time, someone from SUN sent me a complimentary book about JAVA (and a very good one).

I really wish this forum could send us something!!!!

Just kidding, I am trying to increase my number of posts by posting nonsense... (learned from Michael)

mlampkin
05-30-2003, 01:56 AM
That the forum and the folks running it could send you a book or something... try these easy steps and see if it works:

0) Go to bookstore and buy a book you like.
1) Package it up and put your mailing address on the outside.
2) Go to the post office and mail it.
3) Wait until your book from the forum arrives.

See! Wasn't that easy!

Its kinda pointless when we're the ones running / administering this thing (w/o a corporate sponsorship of any type)... though I must admit I have been tempted to send letters of appreciation to myself in the past... ;-)

Michael

Loco
05-30-2003, 04:36 PM
:D

Pretty funny...

I will send myself something, let's see, ummm... How about a nice digital camera? OK, I think it is quite fine...

So I won't complain again, the forum has given me a nice gift!!!

RobSeace
05-30-2003, 05:16 PM
That's nothing, the forum (incarnated as me, for this story) just
recently bought me a brand new computer! ;-) (My old P2/400
was just getting too aged for me, finally... So, I managed to scrape
enough together to get a nice Athlon XP 2700+ system, in a
hellishly spiffy aluminum case, with a side-window... ;-) Way too
much money, but oh well, I deserved it... ;-))