I reused the raw socket snippet from this site and removed the TCP part and added my ICMP part to request an echo. Both my machines are on the same LAN, running Ubuntu 32 bit. I gave the checksum function my ICMP-Pointer but it failed to calculate a correct checksum:
icmph->icmp_sum = csum( (unsigned short *) datagram + sizeof(struct iphdr), sizeof(struct icmp_header));
Then I tried out some raw numbers as offsets until it worked out:
icmph->icmp_sum = csum( (unsigned short *) datagram + 10, sizeof(struct icmp_header));
I even checked the position of the pointers and the size of the IP-header-struct:
Datagram-Pointer: 0xbff94680
IP-Pointer: 0xbff94680
ICMP-Pointer: 0xbff94694
As you can see, the ICMP-Pointer is 20 away from the Datagram- and IP-Pointer. Why will 10 do the job?
Thank you in advance.
PS: Here is the code:
**/*
Raw TCP packets
Silver Moon ([email protected])
*/
#include<stdio.h> //for printf
#include<string.h> //memset
#include<sys/socket.h> //for socket ofcourse
#include<stdlib.h> //for exit(0);
#include<errno.h> //For errno - the error number
#include<netinet/tcp.h> //Provides declarations for tcp header
#include<netinet/ip.h> //Provides declarations for ip header
//ICMP Header
struct icmp_header
{
u_char icmp_type;
u_char icmp_code;
u_short icmp_sum;
u_short icmp_ident;
u_short icmp_seq;
};
/*
Generic checksum calculation function
*/
unsigned short csum(unsigned short *ptr,int nbytes)
{
register long sum;
unsigned short oddbyte;
register short answer;
sum=0;
while(nbytes>1) {
sum+=*ptr++;
nbytes-=2;
}
if(nbytes==1) {
oddbyte=0;
*((u_char*)&oddbyte)=*(u_char*)ptr;
sum+=oddbyte;
}
sum = (sum>>16)+(sum & 0xffff);
sum = sum + (sum>>16);
answer=(short)~sum;
return(answer);
}
int main (void)
{
//Create a raw socket
int s = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
if(s == -1)
{
//socket creation failed, may be because of non-root privileges
perror("Failed to create socket");
exit(1);
}
//Datagram to represent the packet
char datagram[sizeof(struct iphdr) + sizeof(struct icmp_header)] , source_ip[32];
//zero out the packet buffer
memset (datagram, 0, sizeof(struct iphdr) + sizeof(struct icmp_header));
//IP header
struct iphdr *iph = (struct iphdr *) datagram;
//TCP header
struct icmp_header *icmph = (struct icmp_header *) (datagram + sizeof (struct iphdr));
struct sockaddr_in sin;
//some address resolution
strcpy(source_ip , "10.0.2.5");
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr ("10.0.2.4");
//Fill in the IP Header
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = sizeof (struct iphdr) + sizeof (struct icmp_header);
iph->id = htonl (54321); //Id of this packet
iph->frag_off = 0;
iph->ttl = 255;
iph->protocol = 1; //Use ICMP afterwards
iph->check = 0; //Set to 0 before calculating checksum
iph->saddr = inet_addr ( source_ip ); //Spoof the source ip address
iph->daddr = sin.sin_addr.s_addr;
//Ip checksum
iph->check = csum ((unsigned short *) datagram, iph->tot_len);
//Fill in the ICMP Header
icmph->icmp_type = 8; //Ping Request
icmph->icmp_code = 0;
icmph->icmp_sum = 0; //Set to 0 before calculating checksum
icmph->icmp_ident = 0x4142; //Just some Numbers
icmph->icmp_seq = 0x4142; //Just some Numbers
icmph->icmp_sum = csum( (unsigned short *) datagram + 10, sizeof(struct icmp_header));//? Why 10 and not sizeof(struct iphdr)=20?
//For Debugging
printf("Datagram-Pointer: %p\n", (void*) datagram);
printf("IP-Pointer: %p\n", (void*) iph);
printf("ICMP-Pointer: %p\n\n", (void*) icmph);
printf("IP-Struct Size: %d\n", sizeof(struct iphdr));
//IP_HDRINCL to tell the kernel that headers are included in the packet
int one = 1;
const int *val = &one;
if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
{
perror("Error setting IP_HDRINCL");
exit(0);
}
//Senden
if (sendto (s, datagram, iph->tot_len , 0, (struct sockaddr *) &sin, sizeof (sin)) < 0)
{
perror("sendto failed");
}
//Data send successfully
else
{
printf ("Packet Send. Length : %d \n" , iph->tot_len);
}
return 0;
}
//Complete**
sizeof
gives you the size in bytes, not some arbitrary "word". That means you add an offset of 20
16-byte words in your first alternative.
You should divide the result of sizeof
by the size of unsigned short
:
icmph->icmp_sum = csum( (unsigned short *) datagram + sizeof(struct iphdr) / sizeof(unsigned short), sizeof(struct icmp_header));