| /* Taken from Etherboot */ |
| |
| #include "libopenbios/ipchecksum.h" |
| |
| unsigned short ipchksum(const void *data, unsigned long length) |
| { |
| unsigned long sum; |
| unsigned long i; |
| const unsigned char *ptr; |
| union { |
| unsigned char byte[2]; |
| unsigned short word; |
| } u; |
| |
| /* In the most straight forward way possible, |
| * compute an ip style checksum. |
| */ |
| sum = 0; |
| ptr = data; |
| for(i = 0; i < length; i++) { |
| unsigned long value; |
| value = ptr[i]; |
| if (i & 1) { |
| value <<= 8; |
| } |
| /* Add the new value */ |
| sum += value; |
| /* Wrap around the carry */ |
| if (sum > 0xFFFF) { |
| sum = (sum + (sum >> 16)) & 0xFFFF; |
| } |
| } |
| u.byte[0] = (unsigned char) sum; |
| u.byte[1] = (unsigned char) (sum >> 8); |
| return (unsigned short) ~u.word; |
| } |
| |
| unsigned short add_ipchksums(unsigned long offset, unsigned short sum, unsigned short new) |
| { |
| unsigned long checksum; |
| sum = ~sum & 0xFFFF; |
| new = ~new & 0xFFFF; |
| if (offset & 1) { |
| /* byte swap the sum if it came from an odd offset |
| * since the computation is endian independant this |
| * works. |
| */ |
| new = (new << 8) | (new >> 8); |
| } |
| checksum = sum + new; |
| if (checksum > 0xFFFF) { |
| checksum -= 0xFFFF; |
| } |
| return (~checksum) & 0xFFFF; |
| } |