Experiment 1: The IPv4 Header and Internet Checksum

Objectives

  1. Become familiar with some fields of the IPv4 header and IP addresses.

  2. Develop a program to check the integrity of data using the Internet Checksum.

  3. Become familiar with C programming, some bit- wise operations, compilation and testing a program.

Background

In this experiment we’ll analyze IPv4 header data, extract some of the fields and check the integrity using the Internet Checksum. The IPv4 headers are read from a file. The file format is groups of two octets in hex format, separated by commas, one header per line. To simplify program, headers length is fixed to 20 octets (10 groups). Example:

4500, 0034, 3e01, 4000, 3606, f60c, 4127, 0e81, c0a8, 0066
4500, 015a, 1a02, 0000, ff17, 9377, c0a8, 6c03, e000, 0324
4500, 015a, 1a02, 0000, ff17, 9377, c0a8, 6c03, e000, 0067

The format of the IPv4 header is the following (from RFC 791):

.0                   1                   2                   3
.0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|  IHL  |Type of Service|          Total Length         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Identification        |Flags|      Fragment Offset    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Time to Live |    Protocol   |         Header Checksum       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Source Address                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Destination Address                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The checksum field is the sixth group of two octets (f60c in the first line). It is practical to work with hexadecimal notation rather than decimal. Each hexadecimal digit corresponds to 4 bits. In C we can denote hexadecimal constants by adding the prefix 0x to the hexadecimal number, for example 0xf60c.

The first step in analyzing the header is to check the integrity of the data using the checksum. If the checksum fails, the header is discarded because none of the information in the other fields can be trusted.

Checksum Calculation

To calculate the checksum of each header, all groups of two octets are summed and if the result has more than 16 bits, the lower 16 bits are summed to the upper bits shifted down by 16 positions (that is, bit 17 becomes bit 1). The checksum is one’s complement of this sum, truncated to 16 bits.

Note that we can not get more than 20 bits by adding 10 numbers with 16 bits:

0xffff * 0xA = 0x9fff6

This means we have enough bits to calculate the checksum using 32-bit unsigned integer variables in C. To do the calculation in C, we’ll need to use the >> operator to shift bits down, the & operator for bit-wise and (to select bits in a variable), and the ~ operator to perform bit-wise one’s complement. The printf format string that we’ll use is “%x” to print unsigned integers in hexadecimal format. The steps to calculate the checksum are listed below:

  1. Read header data from file. For convenience the data can be stored in an unsigned int array

  2. Add all values in the array on an accumulator

  3. If the result has more than 16 bits (what happens if not?), add the lower 16 bits and the upper bits, shifted down 16 positions:

    low16 = accum & 0xffff; // store the 16 least significant bits
    high16 = accum >> 16;   // store upper bits shifted down 16 positions
    
  4. Calculate checksum:

    checksum = ~(low16 + high16);
    
  5. Print checksum in hex format:

    printf("Checksum = 0x%x\n", checksum);
    

Extraction of Header Data

If the checksum result is zero, the header is validated and we can proceed to extract the data. Otherwise, the program discards the header. The data to be extracted for each valid header is listed below:

  1. Packet length in bytes, in decimal format

  2. Source and destination addresses in dot-decimal format

To extract this information, you will need to use some bit-wise operations as described in the checksum calculation. To print unsigned int variables in decimal format, use the %u format string (use %d for regular int). For example, assuming the dot-decimal bytes of an address are stored in b3, b2, b1, b0, the corresponding statement would look similar the following:

printf("Source address: %u.%u.%u.%u\n", b3, b2, b1, b0);

Sample program output:

/--- Header number in file
|      /-------- Packet length in bytes
|      |       /--------- Source Address
|      |       |               /---- Destination Address
|      |       |               |
V      V       V               V
1:    52,     65.39.14.129,   192.168.0.102
2:    Checksum failed = 0xfd42
3:    346,    192.168.108.3,  224.0.0.103

Your program does not need to exactly produce this output, but all requested fields should be present. A template code with an outline of the program flow to get you started is included in the Appendix Section. Note that checksum is not calculated and initialized to 1, so the if statement in the main program is always false (!checksum could be replaced with checksum == 0).

References

Procedure

  1. Read the background section before starting.

  2. Open a command window (cmd.exe). The current directory should be set to c:\users\engineer. Run experiment1 in that window as shown below. That script sets up the working directory and the environment variables to use the compiler. Do not close this window: you need it to compile and run the program.

  3. We’ll assume in the following that the work directory is c:\users\engineering\exp1 and the source code file is saved as ipv4check.c. Your directory name may not be exactly the same but that should not be a problem. Type notepad++ ipv4check.c to simultaneously open the notepad++ editor and create the source code file (answer yes to create new file). Paste the code from the Appendix into the editor and save into the work directory. Also set language to C in Notepad++ editor. Save your file.

  4. Return to the command line window. Typing dir should list your saved C source file. Compile the program as shown below. The -Wall option tells the compiler to print all warnings. This is useful to detect problems with the source code. The executable file is called a.exe by default. You can change this name by using the -o flag when compiling. For example:

    gcc -Wall -o ipv4check.exe ipv4check.c
    

    The program should compile with no errors/warnings. Every time you make changes to the source code you must re-compile.

  5. Typing the data into the program every time you want to test is too time consuming. The code can be run more conveniently by redirecting the standard input to a file with numbers. This means that the program reads the input from a file instead of the keyboard. Open a new file in notepad++, type the numbers shown below and save the file as data.hex in the working directory:

    4500, 0034, 3e01, 4000, 3606, f60c, 4127, 0e81, c0a8, 0066
    4500, 015a, 1a02, 0000, ff17, 9377, c0a8, 6c03, e000, 0324
    4500, 015a, 1a02, 0000, ff17, 9377, c0a8, 6c03, e000, 0067
    

    Run the program as follows:

    ipv4check.exe < data.hex
    

    The program should run, but of course checksums are not calculated and the output of the program should look as shown below:

    Environment setup, compilation of source code using MinGW-w64 and program running.
  6. Now implement the checksum calculation inside the body of the testChecksum() function. After this is done, you should be able to compile the program and detect all headers with correct checksums (result should match example given in Background section).

  7. Now implement in the main function the instructions to print the packet length and the source and destination addresses. (result should match example given in Background section).

  8. The lab instructor will provide a different file to each group. If everything is correct, your program should be able to process the header data in the supplied file.

Report preparation and questions

  1. Prepare a formal report summarizing this experiment in pdf format and submit it to the lab instructor. Report writing rules:

    • One report per group

    • All students are responsible for the contents of the report, but one student in the group must coordinate, write and submit the report for the experiment. Each student in a group must prepare at least one of the five reports in the term.

    • Clearly state in the report cover the name of all students in the group and indicate who prepared the report

  2. Include a copy of your source code in your report. Your source code should be properly indented, with updated comments and consistent variables and function names.

  3. Show the output generated by your program.

  4. Answer why all (valid) headers start with 0x45

  5. Comment difficulties (if any) and conclusions about this experiment.

Appendix

Program template:

/*
  Test checksum of packet headers stored in file and print some fields
  if checksum is valid.
*/

#include <stdio.h>

unsigned int testChecksum(unsigned int header[])
{
  unsigned int checksum = 1;
  // do some calculation ...
  return checksum;
}

int main()
{
  // Array to store header bytes (in groups of 16 bits)
  unsigned int header[10];
  // header number counter
  int hnumber = 0;
  while(scanf("%x, %x, %x, %x, %x, %x, %x, %x, %x, %x",
            header, header+1, header+2, header+3, header+4,
            header+5, header+6, header+7, header+8, header+9) == 10) {
    hnumber++;

    unsigned int checksum = testChecksum(header);
    if (!checksum) {
      // Extract and print header information
      printf("%d:\t something\n", hnumber);
      }
    else {
      // Checksum failed, discard header and print message
      printf("%d:\tChecksum failed = 0x%x\n", hnumber, checksum);
    }
  }
  return 0;
}