Page 1 of 1
Windows and Checksums...
Posted: Tue Apr 24, 2007 2:09 am
by pcmattman
I continue to have problems with checksums in my OS...
When I receive a UDP packet from my OS, none of my checksums (I tried multiple variations, such as only header, header and data, only data, full packet and pseudo header) that I try to get off the received packet match.
This is really frustrating, and Windows just drops packets with invalid checksums - apparently even a checksum of 0 doesn't work...
This is what I use for UDP checksums:
Code: Select all
// calculates a UDP checksum - http://www.netfor2.com/udpsum.htm
uint16_t udpChecksum( uint16_t udplen, char* udpdat, unsigned int srcIp, unsigned int destIp )
{
// sum
uint32_t sum = 0;
// 16-bit pointer
uint16_t* ptr = (uint16_t*) udpdat;
// make 16-bit words and add them all
int i;
for( i = 0; i < udplen; i++ )
{
sum += *ptr++;
}
// add the ip stuff
sum += srcIp + destIp;
// add the protocol and udp length
sum += ( (short) 0x11 ) + udplen;
// keep last 16 bits of 32 bit calculated sum, add carries
while( sum >> 16 )
sum = ( sum & 0xFFFF ) + ( sum >> 16 );
// return the one's complement
return (uint16_t) ~sum;
}
Edit: neither TCP or UDP work. They can tell me when a packet comes and handle them but after that the sending fails. The sniffer picks up the packets so I'm led to assume that it must be the checksum, especially since in the ICMP layer I had the same problem until I changed my checksum code.
Now this checksum code, working from RFC768, doesn't work... Packets arriving have their checksum printed, then their checksum field is changed to 0. After all this I print out my calculated checksum, which always comes out unequal... Any ideas?
Posted: Tue Apr 24, 2007 3:06 am
by mystran
From RFC 768: User Datagram Protocol:
Checksum is the 16-bit one's complement of the one's complement sum of a
pseudo header of information from the IP header, the UDP header, and the
data, padded with zero octets at the end (if necessary) to make a
multiple of two octets.
The pseudo header conceptually prefixed to the UDP header contains the
source address, the destination address, the protocol, and the UDP
length. This information gives protection against misrouted datagrams.
This checksum procedure is the same as is used in TCP.
Code: Select all
0 7 8 15 16 23 24 31
+--------+--------+--------+--------+
| source address |
+--------+--------+--------+--------+
| destination address |
+--------+--------+--------+--------+
| zero |protocol| UDP length |
+--------+--------+--------+--------+
If the computed checksum is zero, it is transmitted as all ones (the
equivalent in one's complement arithmetic). An all zero transmitted
checksum value means that the transmitter generated no checksum (for
debugging or for higher level protocols that don't care).
I'm not quite sure what the hell is the exception in the last paragraph trying to say (it seems more or less nonsense to me), and whether it's relevant these days. But basicly you take the normal "internet checksum" over the UDP header and the data, plus the extra pseudo-header stuff.
edit: actually now that I think of it, the exception makes sense: if you get either 0x0000 or 0xFFFF as the checksum, you send it as 0xFFFF, which frees 0x0000 for a special case of no checksum
Posted: Tue Apr 24, 2007 3:13 am
by mystran
Actually, RFC1122 (which is a collection of the host-relevant TCP/IP networking requirements) clarifies the issue:
Code: Select all
IMPLEMENTATION:
There is a common implementation error in UDP
checksums. Unlike the TCP checksum, the UDP checksum
is optional; the value zero is transmitted in the
checksum field of a UDP header to indicate the absence
of a checksum. If the transmitter really calculates a
UDP checksum of zero, it must transmit the checksum as
all 1's (65535). No special action is required at the
receiver, since zero and 65535 are equivalent in 1's
complement arithmetic.
Posted: Tue Apr 24, 2007 3:23 am
by pcmattman
Well, I set the 'no checksum' option on the sockets (in Windows) that I'm working with, but I still don't get the data in Windows...
Posted: Tue Apr 24, 2007 3:28 am
by mystran
Well Windows will drop any packet with an invalid checksum, so at best you can control whether it gives you packets without any checksum at all..
But like... I'd say fix your checksum validation first. When you can successfully check that the UDP packets your OS receives have proper checksum, you can then use the same code to calculate checksums for packets you are sending, except you just special case the checksum of 0x0000.
Posted: Tue Apr 24, 2007 3:30 am
by pcmattman
That's the problem. I have no idea what's wrong with my checksum code, but it still doesn't give me the right one...
The code in my first post is what I'm using.
Edit: example of runtime:
Code: Select all
PCI: BIOS32 Service Directory found, entry: 0xfa050 19:55:12 24/ 4/ 7
Intel - PCI & Memory 8086:1237
irq: 0, iobase 0x0
Intel - PIIX3 PCI-to-ISA Bridge (Triton II) 8086:7000
irq: 0, iobase 0x0
Realtek - RTL8029 (ne2000 Compliant) ethernet nic 10ec:8029
irq: 11, iobase 0xc100
PCI: BIOS Version 2.10 Present, 3 devices attached.
Initialising Floppy driver... Enhanced controller found
FAT12
NE 2000 Ethernet Card Driver
Copyright 2007 Anthony Lineberry
Licensed under the GPL
NE2K Comapatible NIC, iobase=0xc100, irq 11, [b0:c4:20:0:0:0]
Ethernet: New network adapter, index 0
Becoming 192.168.1.109: OK
Recevied UDP packet from 192.168.1.110
Ports: [7671]:[1025]
Length: 10
Checksum: 19697
Data:
hi
-- This is where I've calculated it --
Checksum is 29000 (wrong)
Posted: Tue Apr 24, 2007 5:19 am
by mystran
There are two things: first, is udplen the number of bytes, or the number of 16-bit words? If it is the number of bytes, you are checksumming twice as much data as you should. If the number is in words, you've got the wrong length in the "pseudo-header" stuff. I'd say you're running over twice as much memory as you should.
Second, you should special case for the possibility that the length of the packet is odd, and you need to add a zero-byte to pad it to complete 16-bit words..
Finally, this is the exact same checksumming you are using for ICMP and IP checksumming, so you should make one common routine (I call it inet checksum) and just use that in all protocols to reduce the potential for bugs.
Posted: Tue Apr 24, 2007 5:33 pm
by pcmattman
udplen is meant to be the 16-bit count... Oops!!! I keep sending the
byte count...
I'll try it out and post here the results.
Edit: nope, still the same problem:
Code: Select all
Becoming 192.168.1.109: OK 10:23:51 25/ 4/ 7
Recevied UDP packet from 192.168.1.110
Ports: [1288]:[1025]
Length: 10
Checksum: 15370
Data: hi
len is 10
Checksum is 22255
Recevied UDP packet from 192.168.1.110
Ports: [1289]:[1025]
Length: 10
Checksum: 15114
Data: hi
len is 10
Checksum is 21999
Recevied UDP packet from 192.168.1.110
Ports: [1290]:[1025]
Length: 10
Checksum: 14858
Data: hi
len is 10
Checksum is 21743
Checksum code:
Code: Select all
// calculates a UDP checksum - http://www.netfor2.com/udpsum.htm
uint16_t udpChecksum( uint16_t udplen, char* udpdat, unsigned int srcIp, unsigned int destIp )
{
// sum
unsigned long sum = 0;
// 16-bit pointer
uint16_t* ptr = (uint16_t*) udpdat;
// length of the packet
uint16_t baklen = udplen;
// make 16-bit words and add them all
while( udplen > 1 )
{
sum += *ptr++;
udplen -= sizeof( uint16_t );
}
// check for odd bits
if( udplen > 0 )
sum += *ptr++;
// psuedo ip header
// add the ip stuff
sum += srcIp + destIp;
// tell me
kprintf( "len is %d\n", baklen );
// add the protocol and udp length
sum += 17 + baklen;
// 32-bit --> 16-bit
while( sum >> 16 )
sum = ( sum & 0xFFFF ) + ( sum >> 16 );
// return the one's complement
return (uint16_t) ~sum;
}
There's
always a difference of 6,885!
Posted: Tue Apr 24, 2007 11:11 pm
by pcmattman
OK, I figured it out. ICMP doesn't have a checksum field, so it needs to have a full packet checksum in the IP header. However, UDP and TCP do, so they can't have the full packet checksum. I just removed that feature and now there's no problem - I don't have to worry about the checksum (at least not for UDP anyway...).