Take the 2-minute tour ×

I am new to network programming. for the past couple of days I am learning that. today I tried to play with raw socket. Everything went well. But the Ip checksum didn't helped me well. Whatever the value I gave manually to the iph->check, while receiving the checksum something different.

Actually what I did was I created a raw socket C program that will create an TCP packet send to the loop back interface using raw socket. And I captured the packet with wireshark. But when I interpreting the received packet, I found that whatever value I gave for the check sum in my program, it shows some different constant value.

Here is my code

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
//#include <linux/in.h>

unsigned short calculate_check_sum (unsigned short *buf, int nwords) {
unsigned long sum;
for (sum = 0; nwords > 0; nwords--) {
    sum += *buf++;
sum += (sum >> 16);
return ~sum;

int main () {
int sockfd = socket (PF_INET, SOCK_RAW, 6);
if (sockfd < 0) {
    perror ("socket failed: ");
    return -1;
char packet[4096];

struct iphdr *iph = (struct iphdr *) packet;
struct tcphdr *tcph = (struct tcphdr *) packet + sizeof (struct iphdr);
//char *payload = packet + sizeof (struct iphdr) + sizeof (struct tcphdr);

memset (packet, 0, 4096);

//strcpy (payload, "helllo");

struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons (1330);
inet_pton (AF_INET, "", &(sin.sin_addr));

char var[123];
inet_ntop (AF_INET, &(sin.sin_addr), var, INET_ADDRSTRLEN);
printf ("%s\n", var);

iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = sizeof (struct iphdr) + sizeof (struct tcphdr); //+ strlen (payload);
iph->id = htons (1234);
iph->frag_off = 0;
iph->ttl = 225;
iph->protocol = 6;
iph->check = 0;
iph->saddr = inet_addr ("");
iph->daddr = sin.sin_addr.s_addr;

tcph->source = htons (1234);
tcph->dest = htons (1330);
tcph->seq = random ();
tcph->ack_seq = 0;
tcph->res1 = 0;
tcph->doff = 0;
tcph->syn = 1;
tcph->window = htons (1000);
tcph->check = 0;
tcph->urg_ptr = 0;

//iph->check = calculate_check_sum ((unsigned short *) packet, iph->tot_len >> 1);
iph->check = 0xffff;

int one = 1;
if (setsockopt (sockfd, 0, IP_HDRINCL, &one, sizeof (one)) < 0) {
    perror ("setsockopt faild: ");
    return -1;

int ret = sendto (sockfd, packet, iph->tot_len, 0, (struct sockaddr *) &sin, sizeof (sin));
if (ret < 0) {
    perror ("sendto failed: ");
    return -1;
return 0;

This is the stream captured by wireshark 00000000000000000000000008004500002804d20000e106*da5c*2b5850487f0000010000000000000000000000000000000000000000

Here the 5cda (between the astrics) is the checksum I'm getting.

Also instead of wireshark, I have created my own capturing program as follows,

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <string.h>
#include <errno.h>

int main () {
int fd = socket (PF_INET, SOCK_RAW, 6);
int len_msg;
if (fd < 0)
    perror ("socket failed: ");
char buf [8192];
memset (buf, 0, 8192);
struct sockaddr_in sock_addr;
inet_pton (AF_INET, "", &(sock_addr.sin_addr));
int len = sizeof (struct sockaddr);
printf ("%s\n",inet_ntoa(sock_addr.sin_addr.s_addr));
if (bind (fd, (struct sockaddr *) &sock_addr, len)) {
    perror ("bind failed: ");
    return -1;
while ((len_msg = recvfrom (fd, buf, 8192, 0, (struct sockaddr *) &sock_addr, (socklen_t *)&len)) != -1) {
    //printf ("%s\n", buf+sizeof(struct iphdr)+sizeof(struct tcphdr));
    int i;
    printf ("%d\n", len_msg);
    for (i = 0; i < len_msg; i += 1)
        printf ("%x ", (unsigned char) *(buf + i));
    printf ("\n");

    struct iphdr *iph = (struct iphdr *) buf;
    printf ("checksum: %x\n", iph->check);
return 0;

Still I am getting the same value of check. Regardless of whatever value I am inputing in the first program, as checksum, the captured packet having only the value 0x5cda as its checksum.

I am using CentOS. Does the kernel itself do any alteration in my packet? please help me.

Thanks in Advance

share|improve this question
add comment

1 Answer

up vote 2 down vote accepted

From your description, it looks like you are putting a bogus IP checksum in your sending code. This is not valid according to RFC 1122. I suspect that the Linux raw socket handling code is more lenient and will accept packets with invalid IP checksum will overwrite your checksum.

Take a look at:

http://lxr.linux.no/linux+v3.4.1/net/ipv4/raw.c#L458 (this what gets called when you send a message)

share|improve this answer
add comment

Your Answer


