Re: wrong checksum when receiving ip rr option through raw socket

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Dec 05, 2002 at 09:00:46PM -0800, RuiJH wrote:

> hi,
> 
> Could u send me a copy what you have done.In my view maybe 
> there is wrong with your code. :)

Hello,

thanks for replying.  I can't send you the original code, it's spread
over a bunch of c++ classes anyway so you would probably have hard time
debugging it.  Instead, I quickly hacked a minimal example that can
demonstrate the problem, see the attachment.  Compile it with

gcc -o rrchksumbug rrchksumbug.c

and do the following:

1) launch a (bpf) sniffing tool that can look inside options (I use
ethereal) with appropriate filter ("host <your_host> and icmp" should be
OK)

2) launch ./rrchksumbug as root

3) launch ping -R <somewhere>

rrchksumbug is very minimal so it doesn't do any filtering - it accepts
whatever comes from the network and exits.  If you receive a lot of
ICMP's this could be annoying.

Anyway, once you manage to make it accept the ping's reply with recorded
route, you will see following:

- checksum reported by rrchksumbug will be the same as the one reported
  by ethereal (or whatever you use)
- the record route pointer reported by rrchksumbug will be greater by 4
  than the pointer reported by ethereal
- and you'll have to believe me (or check for yourself ;) that the
  recorded address list received by rrchksumbug is indeed augmented by
  IP address of the local host.

So, while the IP header has changed (RR pointer a RR address list), the
checksum hasn't - it's no wonder the packet fails the check.

BTW, if you do the same experiment, only leaving the "-R" out of ping's
options, you'll see that everything works OK.

	pvl

#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define RECV_BUFF_SIZE	2048

// paraphrased from mtr
struct IPHeader {
	unsigned char hdrlen:4;
	unsigned char version:4;
	unsigned char tos;
	unsigned short len;
	unsigned short id;
	unsigned short frag;
	unsigned char ttl;
	unsigned char protocol;
	unsigned short check;
	unsigned int saddr;
	unsigned int daddr;
};

// basically rfc1071
unsigned short checksum (unsigned char *addr, int count)
{
	unsigned int sum = 0;

	while( count > 1 )  {
	   /*  This is the inner loop */
	   sum += * ((unsigned short *) addr)++;
	   count -= 2;
	}

   /*  Add left-over byte, if any */
   if( count > 0 )
	   sum += * (unsigned char *) addr;

   /*  Fold 32-bit sum to 16 bits */
   while (sum>>16)
	   sum = (sum & 0xffff) + (sum >> 16);

   return ~sum;
}


int main ()
{
	unsigned char raw_packet[RECV_BUFF_SIZE];

	int recvsock = socket (PF_INET, SOCK_RAW, IPPROTO_ICMP);

	struct sockaddr_in dummy_src;
	socklen_t srcaddrsize;
	int len = recvfrom (recvsock, raw_packet, RECV_BUFF_SIZE, 0, 
			(struct sockaddr *)&dummy_src, &srcaddrsize);
	struct IPHeader *iphdr = (struct IPHeader * )raw_packet;

	printf ("received %d bytes\n", len);
	printf ("hdrlen: %d bytes\n", 4*iphdr->hdrlen);
	printf ("checksum: %#x, %s\n", ntohs (iphdr->check),
					checksum (raw_packet, 4*iphdr->hdrlen) ? "ERR" : "OK");
	printf ("rr pointer: %d\n", raw_packet[22]);
	return 0;
}

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux 802.1Q VLAN]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Git]     [Bugtraq]     [Yosemite News and Information]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux PCI]     [Linux Admin]     [Samba]

  Powered by Linux