ORT BRAUDE COLLEGE OF ENGINEERING Electronic and Electrical Engineering Department Client Server Systems 31261 PROJECT 3: TCP/IP packet modelling * Work in pairs is OK. * Due date is: Sunday 23:00, May 19, 2013 See below how to submit the project * According to the course program, projects are mandatory, and will form an integral part of the course grade (30%) For motivation, each project will include extra bonus problems that will increase this weight by an additional factor (details will be released later) * You have to design two classes in this project: IpDatagram, TcpSegment. * The purpose of these classes is to parse a Packet (IP datagram, TCP segment) and be able to display its fields in a clear and readable way. For example, an IP sequence number should be displayed in a decimal notation, and IP addresses should be displayed in their normal form (192.168.10.12), etc. * The precise requirements and characterization is specified in the following problems. * Make sure to include a documentation before each class and each method that clearly states what you are trying to do. Please be short and precise. * IMPORTANT: Please stick to the required names of classes, methods, and functions !!! Do not invent you own name or try to modify the names in any way !!! Your work will be checked and graded by automatic scripts on top of your code. So if you do not use the correct names for classes, methods, and functions, you may loose valuable points for your grade! * Before you start, please review the class lecture notes and any other required materials from the course web site, or Google search. Use tutorials on the internet - read all you need about the IP datagram and TCP segment structure. * Python has several modules for dealing with hexadecimal strings, binary strings, and conversions from one format to another. Read about Python's bin, hex, int, bool functions. You may also need to use string functions such as string.join() and split(). If you want to produce good looking printings, look at the 'sys' module (it has a sys.stdout.write() function which can be used to print small fields with no newline). Make sure to solve the first problem and make a wise use with the hex2bin and bin2hex functions in your work (you can base all your class methods on a binary representation instead of the hexadecimal representation which is harder to manipulate). * How to submit your work? You should send your work to: samyz.ort.braude@gmail.com as one zip file: proj3.zip The zip file should contain: proj3/README.txt proj3/code.py proj3/test.py README.txt: This file should contain your names (one or two partners), id, email, and phones, and any other special information that you think ought to be there. code.py: should contain all your classes, functions, and any other global parameter needed for solving this project (for all problems). This is the only file that will be checked test.py: This file should include all your tests of the classes and functions in the file code.py This file is not going to be checked ar graded, but it maybe needed for reference on how you are using your classes and functions. * Wait a few days before you print or copy this file. It may get updated several times. Please read it carefully. You are encouraged and welcome to submit any errors or comments that you may find suitable. Send them to: samyz.ort.braude@gmail.com Please ask any question that bothers you and report anything which is not completely clear. Just state the line number in this file and what is the error or unclear part. * Make sure to insert clear and short comments that explain what you do. * Use consistent coding style. * Except for accuracy and code correctness, your code will also be checked and credited according to readabilty, simplicity, function length, run time, efficiency, and beauty! - Bad function or variable names are against Python philosophy! - Complicated and very long lines or paragraphs are a clear indication of design problems! ################################################################################## #================================================================================# ################################################################################## --------- PROBLEM 1 --------- [5 points] Try to understand what the following code is doing bin_hex_dict = { '0000': '0', '0001': '1', '0010': '2', '0011': '3', '0100': '4', '0101': '5', '0110': '6', '0111': '7', '1000': '8', '1001': '9', '1010': 'a', '1011': 'b', '1100': 'c', '1101': 'd', '1110': 'e', '1111': 'f', } def bin2hex(binary_string): n = len(binary_string) if not n%4 == 0: raise Exception("n must be a multiple of 4") hex_digits = [] for k in range(0,n,4): nibble = binary_string[k:k+4] h = bin_hex_dict[nibble] hex_digits.append(h) return string.join(hex_digits, "") Based on this code, write an inverse function hex2bin(hex_string) which converts a hexadecimal string to a binary representation. Example: bin2hex('111101110010110100010110') = 'f72d16' hex2bin('f72d16') = '111101110010110100010110' --------- PROBLEM 2 --------- [5 points] Write a Python function 'bits_to_ip_address(bits)' which accepts a 32 bits internet address and converts it to a normal IP address in decimal form. Example: bits_to_ip_address("11000111110010111001100000001010") = "199.203.152.10" Note that the 32 bits address is assumed to be in string form (not as a binary number!) --------- PROBLEM 3 --------- [40 points] Design a Python class IpDatagram with the following interface. Class Constructor: IpDatagram(hexstr) * hexstr is a hexadecimal string representation of an IP packet * Make sure to remove any white space from the hex string * In many examples, the hex string is divided to words and lines for convenience, so you accept this types of strings and remove all redundant white space from them (inside the __init__ method). A combination of string.join() and split() should help here. * Except of this, the constructor should take care of initializing all the other members such as: version, ihl, ttl, identification, protocol, etc ... Class members: version - IP version ihl - IP header length. Decimal. (in word units) tos - Type Of Service. Binary string. (like: 011001) ttl - Time To Live field. Decimal. identification - Packet identification number. Decimal. DF - Don't fragment bit (boolean) MF - More fragments bit (boolean) fragment_offset - Fragment offset (decimal) protocol - Protocol number (decimal) checksum - get the checksum field in hex as it appears in the header (don't compute it!) bits - this is the full datagram as a long binary string can beused to extract small parts of the datagra like in: p.bits[14:19] Class methods: size() - IP packet size (in bytes) source() - IP source address in canonical form (192.123.456.789) destination() - IP destination address in canonical form (192.123.456.789) bytes(i,j) - List of bytes from byte i to byte j (not including j) as hex strings (example: ['3f', 'a7', '54']) words(i,j) - List of words from word i to word j (not including j) as hex string header() - IP header part (hex string with options but no data) data() - IP data part (hex string) options() - options list (list of hex strings, one per option) This is not required to do, but if you do it, you will get extra credit compute_checksum() - Compute the IP header checksum You must implement the checksum algorithm as presented in class! Do not use any Python module for the checksum, write the algorithm yourself Example: -------- hexstr = """ 49 00 00 2a 92 cc 00 00 38 06 e4 04 92 95 ba 14 a9 7c 15 95 c4 07 6f 2c 75 b2 c5 c5 07 6a 2d 75 b3 c5 00 00 62 72 61 75 64 65 00 00 """ # Constructing an IP packet object p p = IpDatagram(hexstr) # Invoking class members of p p.version => 4 p.ihl => 9 p.ttl => 56 p.protocol => 6 p.MF => False p.DF => False p.checksum => e404 p.compute_checksum() => Is different than p.checksum !! (you need to find the correct checksum !) p.bits[32:36] => 1001 p.bytes(5,8) => ['cc', '00', '00'] p.words(2,4) => ['3806e404', '9295ba14'] . . . # Class methods: p.size() => 40 (bytes) p.source() => 146.149.186.20 p.destination() => 169.124.21.149 p.header() => 4900002a92cc00003806e4049295ba14a97c1595c4076f2c75b2c5c5076a2d75b3c50000 p.data() => 627261756465 . . . Hints: ------ * Read about the Python functions: int(), bin(), oct() Note that int() can also get two arguments (what is the second argument?) For example to convert a decimal number n=5319 to a 16 digits binary string (padded with zeros at if needed) You will have to something like 0001010011000111 = bin(5319)[2:].zfill(16) * It is not required for the project, but it can help a lot (for debugging and visualization convenience) if you add a display() method to your class! This is also a good programming exercise! p.display() should print: --------------------------------------------------------------------------------------------------------- | 0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15 | 16 17 18 19 20 21 22 23 | 24 25 26 27 28 29 30 31 | --------------------------------------------------------------------------------------------------------- 1 | 0 1 0 0 0 1 0 1 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 1 1 0 1 1 0 0 | 2 | 1 0 0 1 0 0 1 0 | 1 1 0 0 1 1 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 3 | 0 0 1 1 1 0 0 0 | 0 0 0 0 0 1 1 0 | 1 1 1 0 0 1 0 0 | 0 0 0 0 0 1 0 0 | 4 | 1 0 0 1 0 0 1 0 | 1 0 0 1 0 1 0 1 | 1 0 1 1 1 0 1 0 | 0 0 0 1 0 1 0 0 | 5 | 1 0 1 0 1 0 0 1 | 0 1 1 1 1 1 0 0 | 0 0 0 1 0 1 0 1 | 1 0 0 1 0 1 0 1 | 6 | 1 1 0 0 0 1 0 0 | 0 0 0 0 0 1 1 1 | 0 1 1 0 1 1 1 1 | 0 0 1 0 1 1 0 0 | 7 | 0 1 1 1 0 1 0 1 | 1 0 1 1 0 0 1 0 | 1 1 0 0 0 1 0 1 | 1 1 0 0 0 1 0 1 | 8 | 0 0 0 0 0 1 1 1 | 0 1 1 0 1 0 1 0 | 0 0 1 0 1 1 0 1 | 0 1 1 1 0 1 0 1 | 9 | 1 0 1 1 0 0 1 1 | 1 1 0 0 0 1 0 1 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | --------------------------------------------------------------------------------------------------------- --------- PROBLEM 4 --------- [30 points] Design a Python class TcpSegment with the following interface. Class Constructor: TcpSegment(hexstr) - hexstr is a hexadecimal string representation of a Tcp packet hexstr may contain spaces and newlines, so you need to clean them before you use it. Please see the previous IpDatagram class for more details. Class members: source - Source Port destination - Destination Port sequence - TCP Segment sequence number ack - Acknowledgement number thl - TCP Header Length (decimal) data - data part (hex string) window - Windows size field (decimal) checksum - checksum field (hex) urgent_pointer - Urgent pointer field (hex) URG - Urgent bit (boolean) ACK - Acknowledgement bit (boolean) PSH - Push bit (boolean) RST - Reset connection bit (boolean) SYN - Synchronization bit (boolean) FIN - Finished sending data bit (boolean) bits - this is the full segment as a long binary string can beused to extract small parts of the datagram like in: p.bits[14:19] Class methods: size() - TCP Segment size. Decimal (in bytes). header() - TCP header (hex string with options but no data) data() - TCP data part (hex string) bytes(i,j) - List of bytes from byte i to byte j (not including j) as hex strings (example: ['3f', 'a7', '54']) words(i,j) - List of words from word i to word j (not including j) as hex string options() - options list (list of hex strings, one per option) This is not required to do, but if you do it, you will get extra credit Example: hexstr = """ 05 32 00 17 00 00 00 01 00 00 00 00 50 02 07 ff 00 00 00 00 62 72 61 75 64 65 00 00 """ ### Constructing a TcpSegment object p p = TcpDatagram(hexstr) ### Invoking class members of p p.size() = 28 p.source = 1330 p.destination = 23 p.sequence = 1 p.ack = 0 p.thl = 5 p.URG = False p.ACK = False p.PSH = False p.RST = False p.SYN = True p.FIN = False p.window = 2047 p.checksum = 0000 p.urgent_pointer = 0000 p.header() = 053200170000000100000000500207ff00000000 p.data() = 6272617564650000 p.bits[32:36] = 0000 p.bytes(5,8) = ['00', '00', '01'] p.words(2,4) = ['00000000', '500207ff'] For Extra Credit: Which class method you need to add to get the following effect: print p 05 32 00 17 00 00 00 01 00 00 00 00 50 02 07 ff 00 00 00 00 62 72 61 75 64 65 00 00 --------- PROBLEM 5 --------- [20 points] IP/TCP Headers: Write a Python program "tcpip_info(hexdump)" which accepts an IP datagram (hex dump) which encapsulates a TCP segment, and prints all the IP and TCP header field in the exact order as they appear in the header, in the following form: tcpip_info(hexdump) => IP(version) = 4 IP(ttl) = 128 IP(identification) = 18762 IP(fragment_offset) = 8200 IP(protocol) = 17 IP(source) = 192.31.76.5 IP(destination) = 192.31.76.6 ... etc ... TCP(source) = 25 TCP(destination) = 23193 TCP(sequence) = 5719 TCP(ack) = 91761 ... etc ... NOTES: ------ * You must extract the TCP segment from the IP datagram first. How would you do that? * To test your code try it on the following example. Use your program to find out all the above information for the following IP datagram which contans a TCP segment: hexstr = """ 45 20 03 c5 78 06 00 00 34 06 ca 1f d1 55 ad 71 c0 a8 01 7e 00 50 9a 03 3e 64 e5 58 df d0 08 b3 80 18 00 de 00 02 00 00 """ * Please stick to the exact naming conventions to make it easier to check your solution. Use exactly the same names as in classes above. --------- PROBLEM 6 --------- [20 points] Bonus problem. Solving this problem will credit you extra 20 points. IP Fragmentation. A 2400-byte IP datagram is sent into a network that has an MTU of 700 bytes. MTU = Max Transfer Unit. That means, the network cannot handle packets larger than 700 bytes. The original datagram identification number is 422, and it does not have any IP options. (a) How many fragments are generated? What are the fields that are changed in the fragments, and what are their new values in the various fields in the IP datagram(s) generated related to fragmentation? (b) Write a Python function datagram_fragments(datagram, mtu) which accepts an IP datagram and and an mtu size and returns the resulting datagrams after fragmenting the original datagram to the fragments. * Note that your function accepts an IpDatagram object as its first argument and returns a list of IpDatagram objects !! * The purpose of this problem is to educate us on the benefits of the object oriented design. * Hint: You need to extend your IpDatagram class by three new methods: set_fragment_offset(new_offset) set_MF(boolean) set_length(boolean)