I'm on x64 machine. Here is how I'm calculation the checksum for ICMP:
unsigned short in_checksum(unsigned short *ptr, int n_bytes)
{
register long sum;
u_short odd_byte;
register u_short ret_checksum;
while (n_bytes > 1)
{
sum += *ptr++;
n_bytes -= 2;
}
if (n_bytes == 1)
{
odd_byte = 0;
*((u_char *) & odd_byte) = * (u_char *) ptr;
sum += odd_byte;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
ret_checksum = ~sum;
return ret_checksum;
}
When I sniff the sent packets by wireshark, I always says the checksum is incorrect for each icmp packet. What's up with this?
You forgot to initialize
register long sum;
to 0. Passing option -W to gcc would have told you.
...: In function 'in_checksum':
...: warning: 'sum' may be used uninitialized in this function
Related
I have been using raw sockets to create custom TCP packets in C. To verify, I sent them to the loopback interface, and when I checked the received packets using TCPDUMP, the checksum did not match for the TCP packet. Here are the fields in the TCP header :
tcp->th_sport = temp_port; // The TCP structure. The source port, spoofed, we accept through the command line
tcp->th_dport = atoi(argv[2]); // The destination port, we accept through command line
tcp->th_seq = htonl(random_id()%1000);
tcp->th_ack = htonl(random_id()%1000);
tcp->th_off = 5;
tcp->th_flags = TH_SYN;
tcp->th_win = 10000;
tcp->th_sum = 0;
tcp->th_urp = 0;
//pseudo header for TCP checksum calculation
p_hdr->source = t1->s_addr;
p_hdr->dest = t2->s_addr;
p_hdr->reserved = 0;
p_hdr->protocol = IPPROTO_TCP; //TCP
p_hdr->tcp_size = sizeof(struct tcphdr);
memcpy(buffer2 + sizeof(struct pseudo_tcp_header) , tcp , sizeof(struct tcphdr) );
tcp->th_sum = htons(csum((unsigned short *) (buffer2 ), sizeof(struct tcphdr) + sizeof(struct pseudo_tcp_header)));
This is the random_id function :
int random_id()
{
int lower = 1, upper = 65535,number;
number = (rand() % (upper - lower + 1)) + lower;
return number;
}
And the checksum is computed by the function,
unsigned short csum(unsigned short *buf, int len)
{
unsigned long sum;
for(sum=0; len>0; len--)
sum += *buf++;
sum = (sum >> 16) + (sum &0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}
Is there a default function to compute the tcpchecksum in C ?
I understand that there is no default function for calculating the ip/tcp checksum. It can be found by the following code :
unsigned short csum(unsigned short *buf, int len)
{
unsigned long sum;
for(sum=0; len>0; len--)
sum += *buf++;
while (sum>>16)
sum = (sum >> 16) + (sum &0xffff);
return (unsigned short)(~sum);
}
Where the input, buf if the data with the pseudo header.
This calculation is only required if the data packet only traverses through the localhost. If it passes the network card to an external network, leave the sum field blank. The network card will automatically compute the header value.
I am trying to use a 16-bit Fletcher checksum here. Basically, my program simulates traffic over the physical layer by "sending" and "receiving" packets between two virtual entities. I am printing out the packets at both sides and they do match, but I am getting a different checksum calculated at the receiving end.
Packet structure:
#define MESSAGE_LENGTH 20
struct pkt {
int seqnum;
int acknum;
int checksum;
char payload[MESSAGE_LENGTH];
};
This is the code I'm using to compute the checksum of each packet:
/*
* Computes the fletcher checksum of the input packet
*/
uint16_t calcChecksum(struct pkt *packet) {
/* the data for the checksum needs to be continuous, so here I am making
a temporary block of memory to hold everything except the packet checksum */
size_t sizeint = sizeof(int);
size_t size = sizeof(struct pkt) - sizeint;
uint8_t *temp = malloc(size);
memcpy(temp, packet, sizeint * 2); // copy the seqnum and acknum
memcpy(temp + (2*sizeint), &packet->payload, MESSAGE_LENGTH); // copy data
// calculate checksum
uint16_t checksum = fletcher16((uint8_t const *) &temp, size);
free(temp);
return checksum;
}
/*
* This is a checksum algorithm that I shamelessly copied off a wikipedia page.
*/
uint16_t fletcher16( uint8_t const *data, size_t bytes ) {
uint16_t sum1 = 0xff, sum2 = 0xff;
size_t tlen;
while (bytes) {
tlen = bytes >= 20 ? 20 : bytes;
bytes -= tlen;
do {
sum2 += sum1 += *data++;
} while (--tlen);
sum1 = (sum1 & 0xff) + (sum1 >> 8);
sum2 = (sum2 & 0xff) + (sum2 >> 8);
}
/* Second reduction step to reduce sums to 8 bits */
sum1 = (sum1 & 0xff) + (sum1 >> 8);
sum2 = (sum2 & 0xff) + (sum2 >> 8);
return sum2 << 8 | sum1;
}
I don't know much about checksums and I copied that algorithm off a page I found, so if anyone can understand why the checksums for two otherwise identical packets are different I would greatly appreciate it. Thank you!
The problem occurs because you are not passing the address of temp data to the checksum function but rather the address to where the variable temp is stored on stack.
You should change
uint16_t checksum = fletcher16((uint8_t const *) &temp, size);
to
uint16_t checksum = fletcher16((uint8_t const *) temp, size);
^ no & operator
i'm trying to calculate the checksum of an ip address header(without options), following the algorithm: divide the header in 16 bit words, sum all the words, apply NOT operator on the result to obtain the checksum, but i still got wrong results, sniffing the packets with wireshark i can see that they are wrong, for example, this is my method:
void compute_ip_checksum(struct ip_hdr* ip){
unsigned short* begin = (unsigned short*)ip;
unsigned short* end = begin + (IP_NOPT_HEADER_LENGTH / 2);
unsigned short checksum = 0;
ip->checksum = 0;
for (; begin != end; begin++){
checksum += *begin;
}
ip->checksum = htons(~checksum);
}
the ip header i build is:
ip.version_and_length = (IPV4 << 4) | (IP_NOPT_HEADER_LENGTH/4);
ip.type_of_service = 0;
ip.total_length = htons(IP_NOPT_HEADER_LENGTH + TCP_NOPT_HEADER_LENGTH);
ip.frag_id = 0;
ip.flags_and_frag_offset = htons(DONT_FRAGMENT << 13);
ip.time_to_live = 128;
ip.protocol = TCP_PAYLOAD;
ip.src_ip = inet_addr("1.1.1.1");
ip.dst_ip = inet_addr("1.1.1.2");
Since i'm converting all the values to the network byte order, i'm not doing any conversions in the checksum sum, only after the NOT operation, cause i'm almost sure that my windows is LITTLEENDIAN, and if thats the case the result will be put in this byteorder. the result of my functions is: 0x7a17 and the wireshark result is 0x7917 for this header. Can someone explain what is wrong here? my references are: RFC 791 and How to Calculate IpHeader Checksum
So after reading this link: wikipedia i could see that checksum is a little bit more tricky than expected, now this is the code that works for me:
void compute_ip_checksum(struct ip_hdr* ip, struct ip_options* opt){
unsigned short* begin = (unsigned short*)ip;
unsigned short* end = begin + IP_NOPT_HEADER_LENGTH / 2;
unsigned int checksum = 0, first_half, second_half;
ip->checksum = 0;
for (; begin != end; begin++){
checksum += *begin;
}
first_half = (unsigned short)(checksum >> 16);
while (first_half){
second_half = (unsigned short)((checksum << 16) >> 16);
checksum = first_half + second_half;
first_half = (unsigned short)(checksum >> 16);
}
ip->checksum = ~checksum;
}
as you can see, there is no need for conversion after the NOT operation, i've put the carry calculation in a loop because i don't know how many time i have to do this step, i think that in my case it dont exceed one.
I am writing a little POSIX program and I need to compute the checksum of a TCP segment, I would like use an existing function in order to avoid to writing one myself.
Something like (pseudocode) :
char *data = ....
u16_integer = computeChecksum(data);
I searched on the web but I did not find a right answer, any suggestion ?
Here, it's taken more or less directly from the RFC:
uint16_t ip_calc_csum(int len, uint16_t * ptr)
{
int sum = 0;
unsigned short answer = 0;
unsigned short *w = ptr;
int nleft = len;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}
I am trying to build a TCP packet using libnet library. I use '0' for autocomputation of checksum value in the libnet_build_tcp function. However, it seems the checksum ignores the pseudo-header when being computed resulting in a useless packet because of checksum error. Does anyone know how to solve this?
As far as I can see in the code, as long as you haven't used libnet_toggle_checksum(l, ptag, 1); your 0 in the sum parameter of libnet_build_tcp() should be causing it to autocompute a checksum for you by calling libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM).
I couldn't really tell you how to solve it but since you are the one building the packet maybe you could opt for creating your own checksum?
The TCP pseudoheader is a 12 byte field containing the source and destination IP addresses which are both 4 bytes each, a reserved field which is 2 bytes and always set to zeros, and a 4 byte TCP segment length which is equal to the size of the TCP header + the payload length.
You could create something like this:
First create your variables:
int bytesRead;
int pseudoHeaderLength;
int payloadLength;
int tcpHdrLength;
int tcpSegmentLength;
int ipHdrLength;
int headerLengths;
int ethIPHdrLengths;
struct ethhdr *ethHdr;
struct iphdr *ipHdr;
struct tcphdr *tcpHdr;
u_int8_t *pkt_data;
u_int8_t pseudoHeader[12];
u_int8_t packetBuffer[1600];
u_int16_t newChecksum;
Then create the actual function where b->len is the total size of the packet (just add all the headers + the payload and get the total size) and you would just have to memcpy your header and data to pkt_data:
ethHdr = (struct ethhdr*)pkt_data;
ipHdr = (struct iphdr*)(pkt_data + ETH_HLEN);
ipHdrLength = ipHdr->ihl * 4;
ethIPHdrLengths = ETH_HLEN + ipHdrLength;
tcpHdr = (struct tcphdr*)(pkt_data + ethIPHdrLengths);
tcpHdrLength = tcpHdr->doff * 4;
headerLengths = ethIPHdrLengths + tcpHdrLength;
payloadLength = b->len - headerLengths;
tcpSegmentLength = tcpHdrLength + payloadLength;
pseudoHeaderLength = tcpSegmentLength + 12;
pseudoHeader[0] = pkt_data[30];
pseudoHeader[1] = pkt_data[31];
pseudoHeader[2] = pkt_data[32];
pseudoHeader[3] = pkt_data[33];
pseudoHeader[4] = pkt_data[26];
pseudoHeader[5] = pkt_data[27];
pseudoHeader[6] = pkt_data[28];
pseudoHeader[7] = pkt_data[29];
pseudoHeader[8] = 0x00;
pseudoHeader[9] = 0x06;
pseudoHeader[10] = (tcpSegmentLength >> 8) & 0xFF;
pseudoHeader[11] = tcpSegmentLength & 0xFF;
bytesRead = 0;
for(i = 0; i < 12; i++) {
packetBuffer[bytesRead] = pseudoHeader[i];
bytesRead++;
}
for(i = ethIPHdrLengths; i < headerLengths; i++) {
packetBuffer[bytesRead] = pkt_data[i];
bytesRead++;
}
for(i = b->len - payloadLength; i < b->len; i++) {
packetBuffer[bytesRead] = pkt_data[i];
bytesRead++;
}
newChecksum = checksum((uint16_t *)packetBuffer, pseudoHeaderLength);
And just use the checksum function provided by https://www.rfc-editor.org/rfc/rfc1071 to calculate the checksum over the buffer:
uint16_t checksum(uint16_t *addr, int len)
{
int count = len;
register uint32_t sum = 0;
uint16_t answer = 0;
while (count > 1) {
sum += *(addr++);
count -= 2;
}
if (count > 0) {
sum += *(uint8_t *) addr;
}
while (sum >> 16) {
sum = (sum & 0xffff) + (sum >> 16);
}
answer = ~sum;
return (answer);
}
I use this in a realistic environment to calculate checksums at 5 million PPS.