1

I am trying to implement a ping server in Python, and I am going through Pyping's source code as a reference: https://github.com/Akhavi/pyping/blob/master/pyping/core.py

I am not being able to understand the calculate_checksum function that has been implemented to calculate the checksum of the ICMP echo request. It has been am implemented as follows:

def calculate_checksum(source_string):

    countTo = (int(len(source_string) / 2)) * 2
    sum = 0
    count = 0

    # Handle bytes in pairs (decoding as short ints)
    loByte = 0
    hiByte = 0
    while count < countTo:
        if (sys.byteorder == "little"):
            loByte = source_string[count]
            hiByte = source_string[count + 1]
        else:
            loByte = source_string[count + 1]
            hiByte = source_string[count]
        sum = sum + (ord(hiByte) * 256 + ord(loByte))
        count += 2

    # Handle last byte if applicable (odd-number of bytes)
    # Endianness should be irrelevant in this case
    if countTo < len(source_string): # Check for odd length
        loByte = source_string[len(source_string) - 1]
        sum += ord(loByte)

    sum &= 0xffffffff # Truncate sum to 32 bits (a variance from ping.c, which
                      # uses signed ints, but overflow is unlikely in ping)

    sum = (sum >> 16) + (sum & 0xffff)  # Add high 16 bits to low 16 bits
    sum += (sum >> 16)                  # Add carry from above (if any)
    answer = ~sum & 0xffff              # Invert and truncate to 16 bits
    answer = socket.htons(answer)

    return answer

sum &= 0xffffffff is used for truncating the sum to 32 bits. However, what happens to the extra bit (the 33rd bit). Shouldn't that be added to the sum as a carry? Also, I am not being the able to understand the code after this.

I read the RFC1071 documentation (http://www.faqs.org/rfcs/rfc1071.html) that explains how to implement the checksum, but I haven't been able to understand much.

Any help would be appreciated. Thanks!

mangom
  • 467
  • 1
  • 3
  • 20

1 Answers1

0

I was finally able to figure out the working of the calculate_checksum function, and I have tried to explain it below.

The checksum calculation is as follows (as per RFC1071):

  • Adjacent octets in the source_string are paired to form 16-bit integers, and the 1's complement sum of these integers is calculated. In case of odd number of octets, pairs are created out of the n-1 octets and added, and the remaining octet is added to the sum.

  • The resulting sum is truncated to 16-bits (carry bits are to be taken care of) and the checksum is calculated by taking it's 1's complement. The final checksum should be 16-bits long.

Let's take an example.

If the checksum is to be computed over the sequence of octets [A, B, C, D, E], the pairs created would be [A, B] and [C, D], with the remaining octet E. The pairs [a, b] can be computed as follows:

a*256+b where a and b are the octets

Say if a is 11001010 and b is 00010001, a*256+b = 1100101000010001 thus giving us the concatenated results of the octets.

The 1's complement sum is thus computed as follows:

sum = [A+B] +' [C+D] +' E where +' represents 1's complement addition

Now coming back to the code, everything before the line sum &= 0xffffffff calculates the 1's complement sum that we have calculated before.


sum &= 0xffffffff

is used for truncating the sum to 32-bits, although the sum exceeding is unlikely in ping as the size of the source_string is not very large

(source_string = header(8 bytes) + payload (variable length)).


sum = (sum >> 16) + (sum & 0xffff)

This piece of code is implemented for the case when the sum is greater than 16-bits. The sum is broken down into 2 parts:

(sum >> 16): the higher order 16-bits

(sum & 0xffff): the lower order 16-bits

and then these two parts are added. The final result can be 16-bits ogreater than 16-bits


sum += (sum >> 16)

This line is used in case the resulting sum from the previous computation is longer than 16-bits and is used to take care of the carry, similar to the previous line.


Finally, the 1's complement is calculated and truncated to 16 bits. The socket.htons() function is used for maintaining the arrangement of bytes sent to the network based on the architecture of your device (Little endian and big endian).

Community
  • 1
  • 1
mangom
  • 467
  • 1
  • 3
  • 20