calculate IP checksum in python

19,960

Solution 1

You can use the solution directly from checksum udp calculation python, which results in the expected checksum value of zero.

import struct

data = "45 00 00 47 73 88 40 00 40 06 a2 c4 83 9f 0e 85 83 9f 0e a1"

def carry_around_add(a, b):
    c = a + b
    return (c & 0xffff) + (c >> 16)

def checksum(msg):
    s = 0
    for i in range(0, len(msg), 2):
        w = ord(msg[i]) + (ord(msg[i+1]) << 8)
        s = carry_around_add(s, w)
    return ~s & 0xffff

data = data.split()
data = map(lambda x: int(x,16), data)
data = struct.pack("%dB" % len(data), *data)

print ' '.join('%02X' % ord(x) for x in data)
print "Checksum: 0x%04x" % checksum(data)

Results:

45 00 00 47 73 88 40 00 40 06 A2 C4 83 9F 0E 85 83 9F 0E A1
Checksum: 0x0000

Solution 2

You've got two issues here.

First, your call to struct.unpack_from only unpacks 4 16-bit values (that is, 8 bytes) from the buffer. If you want to unpack the whole header, you'll need to do something like struct.unpack_from("!nH"), where n is the number of shorts you want to unpack. You can generate the appropriate format string with struct.unpack_from("!%dH"%(len(data)/2), data), assuming that data contains nothing but the IP header.

Second, once you do that, you'll find that the checksum now works out to 0. This is the correct result for a packet that already has its checksum set, as this one does. (You highlighted the A2 and C4 bytes in the packet above.) To calculate the correct checksum for a packet from scratch, you need to set the checksum bytes to 0. (See the start of step 2 in RFC1071: "To generate a checksum, the checksum field itself is cleared".)

Share:
19,960
Simon
Author by

Simon

Updated on June 04, 2022

Comments

  • Simon
    Simon almost 2 years

    I need to calculate the checksum of an IP packet as described in http://www.faqs.org/rfcs/rfc1071.html.

    I have already the following code:

    #!/usr/bin/python
    import struct
    
    data = "45 00 00 47 73 88 40 00 40 06 a2 c4 83 9f 0e 85 83 9f 0e a1"
    
    # a test for the checksum calculation
    
    def _checksum(data):
        #calculate the header sum
        ip_header_sum = sum(struct.unpack_from("6H", data))
        #add the carry
        ip_header_sum = (ip_header_sum & 0xFFFF) + (ip_header_sum >> 16 & 0xFFFF)
        #invert the sum, python does not support inversion (~a is -a + 1) so we have to do
        #little trick: ~a is the same as 0xFFFF & ~a
        ip_header_sum = ~ip_header_sum & 0xFFFF
    
        return ip_header_sum #should return 0 if correct
    
    data = data.split()
    data = map(lambda x: int(x,16), data)
    data = struct.pack("%dB" % len(data), *data)
    
    print " ".join(map(lambda x: "0x%02x" % ord(x), data))
    print "Checksum: 0x%04x" % _checksum(data)
    

    It works with a package that I have captured with wireshark and which should have the correct checksum and should therefore evaluate to 0

    Unfortunately the result is 0x6524. It is also interesting, that the result is always 0x6524 for every correct packet...

    Who spots the error?

    Edited to make the error more clear *Edited a second time*

  • mykhal
    mykhal over 13 years
    You highlighted the ac and c4 bytes in the packet above - i don't think Simon intentionally highlighted any bytes of the sample packet, StackOverflow engine did, thinking it's some code
  • Simon
    Simon over 13 years
    The data parameter contains the whole packet. Also I do not unpack the checksum so therefore it should give the checksum (but maybe byte swapped )
  • Simon
    Simon over 13 years
    I have done what nickm has told, but still this does not work, see the updated question
  • Simon
    Simon over 13 years
    Doesn't work, too. It also gives the result 0x6524. I believe the error is not in the calculation, but in the unpacking. But the checksum is calculated out of the first 96 bits of the package, isn't it?
  • Kevin Jacobs
    Kevin Jacobs over 13 years
    Edited to demonstrate that it does indeed work, providing a checksum result of zer0.