Wireshark-bugs: [Wireshark-bugs] [Bug 3112] CDP Checksum Calculation Incorrect
Date: Tue, 23 Dec 2008 09:06:47 -0800 (PST)
https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=3112 --- Comment #7 from Greg Neujahr <wireshark@xxxxxxxxxxxxxxxxxxxxx> 2008-12-23 09:06:46 PDT --- Did a little more testing using the samples that Jaap provided (thanks). I'm going to hook my debugger up to them and figure out what's going on shortly. In the meantime, because it helps me think, here's just a basic rundown of what's going on: Raw bytes in network order are read from the card and stored in a pointer to an unsigned char. This means we get raw access to the bytes in the order of the network. In the 'real cdp' checksum function, we convert that pointer to an unsigned short. What this does, is automatically swap the bytes in the resulting short. As an example, here's some code that shows it: unsigned char t[] = {0xab, 0xcd}; unsigned short s = *((unsigned short *) t); unsigned char *p = (unsigned char *) &s; printf("unsigned short: 0x%x\n", s); printf(" raw bytes: 0x%x, 0x%x\n", p[0], p[1]); And the output: unsigned short: 0xcdab raw bytes: 0xab, 0xcd So, we know we put in 0xabcd, but the resulting short is 0xcdab. Following that logic, we need to make sure that all bytes are byte swapped for addition. Adding shorts of various byte orderings will cause nonsense to be generated at the end. If we have an odd number of bytes in the data we need to pad a single 0x00 onto the end. The outstanding question is whether or not this is byteswapped or left as is. Doing this: sum += htons(*((unsigned char *)d)); Converts our short pointer back into an unsigned char pointer, which will give us the data in raw network order again. Since the htons function is expecting a short, it will convert the raw 0xaa char into a 0xaa00 short (on a little endian machine). Since we have byte-swapped everything else, we should also byte-swap the last padding byte as well to keep the addition correct, so we use htons to do that. On a little endian machine, htons will swap the bytes and gives us back 0x00aa. We then add, adjust for carry, and then invert. This provides us with the correct checksum for CDP. Now, if we instead do: sum += *((unsigned char *)d); This converts our short back to a char in raw network order. Since sum is a short, the 0xaa that is stored in this new char pointer will need to be converted into a short, which will give us 0xaa00 as a short. If we add this in raw, we are adding in a non-swapped short with a bunch of swapped shorts, causing the checksum to be incorrect. Jaap, to confirm some of your findings, we take the raw data from the wire which is in network order, and convert it into host order by type casting the pointer to a short. We add everything up in host order until we get to the last byte in an odd length. Using the htons doesn't convert it back into network order, it converts the 0xaa00 raw short into host order by byte-swapping the pad and final byte (it could be rewritten to cast the short* into a char*, dereference it, then cast it back to a short, which would have the same effect [something akin to ((unsigned short) *((unsigned char *)d))]. So, in essence, we never change the ordering, we always keep it the same. The WS code seems to be the one that changes byte ordering for the odd length data streams. If we look at the actual code from WS and assume that everything is always aligned on an even boundry, and that we will never have more than one 'vector', then we can reduce the whole thing to this (removing all the unrolled loop bits -- note this wasn't tested, just a mental exercise): int in_cksum(const unsigned char *data, int len) { const unsigned short *w; int sum = 0; int mlen = 0; union { unsigned char c[2]; unsigned short s; } s_util; w = (const unsigned short *) data; mlen = len; while((mlen -= 2) >= 0) { sum += *w++; } if (mlen == -1) { s_util.c[0] = *(const unsigned char *)w; s_util.c[1] = 0; sum += s_util.s; } return (~sum & 0xffff); } So, it seems to do a fairly decent job. Takes in the raw network order data, typecasts to a short, which will change the ordering, adds everything up, and then, if there's a byte leftover at the end, it puts the byte in an array at position 0, and then puts the pad in position 1. Being a union and all, they share the same space. The trick here is that we aren't getting that final byteswap. Since we didn't do a funky typecast from char to short, the automatic swapping isn't going to happen (and why would it, since we already have a short in the union we access directly?). I think with that, we can see where the problem is. It would appear that conversion from char* to short* causes the byte swapping to happen when its derefenced. Since the WS code directly manipulates a short, the padding goes into the end byte, but it's never byte swapped like the rest of it is, so the checksum is incorrect. Looks like the last padding byte that is inserted would have to be byte-swapped if on a little endian machine. The WS code would appear to be doing so incorrectly by relying on a mechanism that doesn't work. It may have been that it once worked, but no longer does, but I can't say that for sure. Anyone else care to confirm or refute those conclusions? -- Configure bugmail: https://bugs.wireshark.org/bugzilla/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.
- References:
- [Wireshark-bugs] [Bug 3112] New: CDP Checksum Calculation Incorrect
- From: bugzilla-daemon
- [Wireshark-bugs] [Bug 3112] New: CDP Checksum Calculation Incorrect
- Prev by Date: [Wireshark-bugs] [Bug 2803] Wireshark build fails if c-ares version < 1.5
- Next by Date: [Wireshark-bugs] [Bug 3159] WPS Packet UUID value dissector
- Previous by thread: [Wireshark-bugs] [Bug 3112] CDP Checksum Calculation Incorrect
- Next by thread: [Wireshark-bugs] [Bug 3112] CDP Checksum Calculation Incorrect
- Index(es):