Tutorial 4 - DNS & Wireshark
Submission process:
- Submission deadline is December 11, 14:00 CET (before the lecture) .
- Commit and push your solution as separate notebook files per subtask via git as ./tutorial/tutorial4/tutorial4_2.ipynb. Please take care of the correct subfolder/filename since submission is denied otherwise.
- During the first lecture after the deadline we will discuss a sample solution in class.
- Afterwards, you have time until December 16, 16:00 CET (before the lecture) to submit a corrected version of your submission:
- Rework your solution according to our discussion in class.
- Commit and push the corrected version as a separate file per subtask via git as ./tutorial/tutorial4/tutorial4_2.ipynb. Please take care of the correct filename since submission is denied otherwise.
Remarks:
- Grading is done based on both versions of your submission.
- If the first submission is missing or contains major flaws a deduction of up to 50% of the achieved points will be applied
- A sample solution is provided after December 16, 16:00 CET eventually.
- Do NOT duplicate cells or change the type of cells, otherwise you might receive zero points for your submission
- Please use acn@net.in.tum.de for questions regarding lecture, tutorial, and project of ACN.
This cell is used to verify the structure of the submission
DO NOT DELETE OR CHANGE THIS CELL, otherwise the task will be graded with 0 pointsProblem 2 Wireshark (5 credits)
We consider the following hexdumps. It is known that these dumps represent Ethernet (IEEE 802.3u) frames including L2 headers but without FCS. In the following, we will dissect the whole frames.
To print the hexdump we use the hexdump Python module, which is not installed by default. Run the following cell to install it.
!pip3 install hexdump
Requirement already satisfied: hexdump in /root/ACN/venv/lib/python3.13/site-packages (3.3)
import binascii
from hexdump import hexdump
def prtyprnt(dump):
hexdump(dump)
# Here is an example how you can compare fields of a bytearray
# bytearray(b'\x01\x02\x03\x04')[2:4] == bytearray(b'\x03\x04')
dump_ipv4 = bytearray(b'\xfc\xe9\x98\x97\xec\xea\x44\xd9\xe7\x00\x40\x01\x08\x00\x45\x00\x00\x38\x00\x00\x00\x00\xf1\x01\x8c\x2b\x3e\x9a\x59\x2e\xac\x13\xf9\xbd\x0b\x00\xbf\x50\x00\x00\x00\x00\x45\x00\x00\x3c\x15\xb2\x00\x00\x01\x11\xea\x81\xac\x13\xf9\xbd\x81\xbb\x91\xf1\xd4\x0f\x82\xbe\x00\x28\xde\xb8')
dump_ipv6 = bytearray(b'\x33\x33\xff\xd7\x6d\xa0\xe0\x3e\x44\x7a\xa7\x34\x86\xdd\x60\x00\x00\x00\x00\x20\x3a\xff\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x25\x90\xff\xfe\x54\x73\x9a\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xd7\x6d\xa0\x87\x00\x19\xc9\x00\x00\x00\x00\x20\x01\x4c\xa0\x20\x01\x00\x11\x02\x25\x90\xff\xfe\xd7\x6d\xa0\x01\x01\x90\xe2\xba\x7a\xa7\x34')
print('IPv4 packet:')
prtyprnt(dump_ipv4)
print('\nIPv6 packet:')
prtyprnt(dump_ipv6)
IPv4 packet: 00000000: FC E9 98 97 EC EA 44 D9 E7 00 40 01 08 00 45 00 ......D...@...E. 00000010: 00 38 00 00 00 00 F1 01 8C 2B 3E 9A 59 2E AC 13 .8.......+>.Y... 00000020: F9 BD 0B 00 BF 50 00 00 00 00 45 00 00 3C 15 B2 .....P....E..<.. 00000030: 00 00 01 11 EA 81 AC 13 F9 BD 81 BB 91 F1 D4 0F ................ 00000040: 82 BE 00 28 DE B8 ...(.. IPv6 packet: 00000000: 33 33 FF D7 6D A0 E0 3E 44 7A A7 34 86 DD 60 00 33..m..>Dz.4..`. 00000010: 00 00 00 20 3A FF FE 80 00 00 00 00 00 00 02 25 ... :..........% 00000020: 90 FF FE 54 73 9A FF 02 00 00 00 00 00 00 00 00 ...Ts........... 00000030: 00 01 FF D7 6D A0 87 00 19 C9 00 00 00 00 20 01 ....m......... . 00000040: 4C A0 20 01 00 11 02 25 90 FF FE D7 6D A0 01 01 L. ....%....m... 00000050: 90 E2 BA 7A A7 34 ...z.4
a) [0.5 credits] Briefly explain which purpose the FCS serves and how it is computed.
The frame check sequence (FCS) is appended at the end of each frame. Its purpose is to detect bit errors that occur during transfer. For Ethernet the FCS is computed using cyclic redundancy check (CRC). Depending on the CRC polynomial different kinds of errors can be detected. However, burst errors longer than the checksum can never be detected reliably as the error may be a multiple of the CRC polynomial. Note that CRC is normally not used for error correction although very special types of errors are indeed recoverable using CRC. Error correcting codes such has Hamming codes, Reed Solomon Codes, etc. are used by line codes on the physical layer.
b) [0.1 credits] Write the body of the given function extend_hexdump(). The function gets a hexdump without the FCS as an input and should return the extended hexdump with the FCS at the correct position and with the correct length. extend_hexdump() may not modify the original hexdump. You do not need to calculate the correct FCS but can set all bits of the FCS to '1'.
def extend_hexdump(dump):
# begin insert code
return dump + bytearray(b'\xFF\xFF\xFF\xFF')
# end insert code
return dump
prtyprnt(extend_hexdump(dump_ipv4))
00000000: FC E9 98 97 EC EA 44 D9 E7 00 40 01 08 00 45 00 ......D...@...E. 00000010: 00 38 00 00 00 00 F1 01 8C 2B 3E 9A 59 2E AC 13 .8.......+>.Y... 00000020: F9 BD 0B 00 BF 50 00 00 00 00 45 00 00 3C 15 B2 .....P....E..<.. 00000030: 00 00 01 11 EA 81 AC 13 F9 BD 81 BB 91 F1 D4 0F ................ 00000040: 82 BE 00 28 DE B8 FF FF FF FF ...(......
c) [0.2 credits] Write two functions which return the source and destination MAC of a given Ethernet frame.
- getSrcMAC() shall return a bytearray containing the source MAC address
- getDstMAC() shall return a bytearray containing the destination MAC address
def getSrcMAC(dump):
# begin insert code
# src is the second address in the ethernet frame
return dump[6:12]
# end insert code
return dump
prtyprnt(getSrcMAC(dump_ipv6))
00000000: E0 3E 44 7A A7 34 .>Dz.4
def getDstMAC(dump):
# begin insert code
# dst is the first address in the ethernet frame
return dump[0:6]
# end insert code
return dump
prtyprnt(getDstMAC(dump_ipv6))
00000000: 33 33 FF D7 6D A0 33..m.
- Dst MAC: 33:33:ff -> IPv6 Multicast -- No associated company
- Src MAC: 90:e2:ba -> Intel Corp
Taken from http://standards-oui.ieee.org/oui.txt
e) [0.6 credits]
Without knowing that the given hexdump is an Ethernet frame, we would not be able to decode it as we had no clue what the data fields represent. Knowing that it is Ethernet enables us to parse the first header.
- Write a function that is able to check for a given hexdump whether the frame contains an IPv4 packet as its payload or not.
- Write a function that is able to check for a given hexdump whether the frame contains an IPv6 packet as its payload or not.
- Write a function that returns the type of the layer 3 payload. It should return 4/6 for IPv4/IPv6 and None otherwise.
Note: You can assume Ethernet frames without VLANs, i.e., no IEEE 802.3q.
def isIPv4(dump):
# begin insert code
# Ethertype has fixed position => cut out respective bytes
ethertype = dump[12:14]
# length check (nice to have but not strictly required for this problem)
if len(ethertype) == 0:
print('frame too short')
return False
# check ethertype value
# 0x0800 => IPv4
# A list of standardized Ethertypes is maintained by IANA
# (Internet Assigned Numbers Authority)
# https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
if ethertype == bytearray(b'\x08\x00'):
return True
# This piece of code only handles Layer 2 not Layer 3.
# You may additionally check the IP version field but it is not required
# for this exercise. Checking only the Layer 3 version field would not
# suffice as we do not know the header of the Layer 3 protocol without
# checking Layer 2 first.
# end insert code
return False
print('Does dump_ipv4 contain a valid IPv4 packet?', isIPv4(dump_ipv4))
Does dump_ipv4 contain a valid IPv4 packet? True
def isIPv6(dump):
# begin insert code
# Ethertype has fixed position => cut out respective bytes
ethertype = dump[12:14]
# length check (nice to have but not strictly required for this problem)
if len(ethertype) == 0:
print('frame too short')
return False
# check ethertype value
# 0x86DD => IPv4
# A list of standardized Ethertypes is maintained by IANA
# (Internet Assigned Numbers Authority)
# https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
if ethertype == bytearray(b'\x86\xDD'):
return True
# This piece of code only handles Layer 2 not Layer 3.
# You may additionally check the IP version field but it is not required
# for this exercise. Checking only the Layer 3 version field would not
# suffice as we do not know the header of the Layer 3 protocol without
# checking Layer 2 first.
# end insert code
return False
print('Does dump_ipv6 contain a valid IPv6 packet?', isIPv6(dump_ipv6))
Does dump_ipv6 contain a valid IPv6 packet? True
def getL3Type(dump):
# begin insert code
# check if IPv4
if isIPv4(dump):
return 4
# check if IPv6
if isIPv6(dump):
return 6
# return None otherwise
return None
# end insert code
return None
print('dump_ipv4 contains IP version {}'.format(getL3Type(dump_ipv4)))
print('dump_ipv6 contains IP version {}'.format(getL3Type(dump_ipv6)))
dump_ipv4 contains IP version 4 dump_ipv6 contains IP version 6
f) [0.5 credits] How can the beginning of the payload be determined for Ethernet frames?
For traditional Ethernet there is a fixed header length (Dst MAC address 6 Byte, Src MAC address 6 Byte, Ethertype 2 Byte). Since the header has a fixed length, there is no need to indicate the beginning of the payload itself.
Later in the lecture we present VLAN (IEEE 802.1Q).
For frames following that standard the header length varies.
If no VLAN tag is present, the traditional header is used.
In case of VLAN tagged frames, the location of the original Ethertype contains a 0x8100, followed by the Tag Control Information (TCI, 16 bit).
After that the Ethertype of the payload is defined and after that the payload begins.
The header size for frames of this kind is 4 Bytes longer.
According to IEEE 802.1ad (also known as QinQ) it is possible to define two VLANs.
There the TCI of the first VLAN is followed by 0x8100 and a second TCI, before the Ethertype of the payload starts.
After that the payload begins.
The header size for frames of this kind is 4 Bytes longer than IEEE 802.1Q tagged frames.
g) [0.6 credits] How can the beginning of the payload be determined for IP packets? What is the difference between IPv4 and IPv6?
IPv4 has a variable header length and IPv6 can have extension headers after a fixed size header, so the begin of the payload is not fixed.
- IPv4: header length is defined in the IP header length (IHL) field, these 4 Bit determine the length of the IP header in 32 bit words [RFC 791]
- IPv6: next header field, defines the following header which can either be the payload, e.g., TCP or an extension header. The extension headers itself have a next header field and a length forming a chain of extension headers. The payload starts after this chain of extension headers. [RFC 8200]
h) [0.6 credits] Write three functions:
- cutL2PDU() shall return a bytearray containing the Layer 2 PDU
- cutL2SDU() shall return a bytearray containing the Layer 2 SDU
- cutIPPDU() shall return a bytearray containing the Layer 3 PDU.
cutIPPDU() should only cut out valid IPv4 or IPv6 packets. In case no IPv4 or IPv6 packet is found an empty bytearray should be returned.
All functions get an Ethernet hexdump as input with the FCS excluded. The functions should work not only on the given hexdump but on an arbitrary packet hexdump. For this exercise you can assume correct packets, i.e. your functions do not need to validate the given data.
def cutL2PDU(dump):
# begin insert code
# Validation of packet data was omitted intentionally.
# The protocol data unit (PDU) contains the header and the payload.
# No changes required for the given input data.
# end insert code
return dump
prtyprnt(cutL2PDU(dump_ipv4))
00000000: FC E9 98 97 EC EA 44 D9 E7 00 40 01 08 00 45 00 ......D...@...E. 00000010: 00 38 00 00 00 00 F1 01 8C 2B 3E 9A 59 2E AC 13 .8.......+>.Y... 00000020: F9 BD 0B 00 BF 50 00 00 00 00 45 00 00 3C 15 B2 .....P....E..<.. 00000030: 00 00 01 11 EA 81 AC 13 F9 BD 81 BB 91 F1 D4 0F ................ 00000040: 82 BE 00 28 DE B8 ...(..
def cutL2SDU(dump):
# begin insert code
# Validation of packet data was omitted intentionally.
# The service data unit (SDU) contains only the payload data
# This requires the header to be removed from the layer 2 frame
return dump[14:len(dump)]
# end insert code
return dump
prtyprnt(cutL2SDU(dump_ipv4))
00000000: 45 00 00 38 00 00 00 00 F1 01 8C 2B 3E 9A 59 2E E..8.......+>.Y. 00000010: AC 13 F9 BD 0B 00 BF 50 00 00 00 00 45 00 00 3C .......P....E..< 00000020: 15 B2 00 00 01 11 EA 81 AC 13 F9 BD 81 BB 91 F1 ................ 00000030: D4 0F 82 BE 00 28 DE B8 .....(..
def cutIPPDU(dump):
# begin insert code
# L2 SDU == L3 PDU
# check if there is IPv4
if isIPv4(dump) or isIPv6(dump):
return cutL2SDU(dump)
else:
return bytearray()
# end insert code
return dump
prtyprnt(cutIPPDU(dump_ipv4))
00000000: 45 00 00 38 00 00 00 00 F1 01 8C 2B 3E 9A 59 2E E..8.......+>.Y. 00000010: AC 13 F9 BD 0B 00 BF 50 00 00 00 00 45 00 00 3C .......P....E..< 00000020: 15 B2 00 00 01 11 EA 81 AC 13 F9 BD 81 BB 91 F1 ................ 00000030: D4 0F 82 BE 00 28 DE B8 .....(..
i) [0.2 credits] Write a function which helps to identify the type of payload an IPv4 or IPv6 packet is carrying. You can assume that the identifyPayploadIP() gets an Ethernet hexdump as input with the FCS excluded. It should return a bytearray containing the L4 payload identifier or an empty bytearray if neither an IPv4 nor an IPv6 payload was found.
Hint: you can assume that there are no Extension Headers within the IPv6 packets.
def identifyPayloadIP(dump):
# begin insert code
ip_pdu = cutIPPDU(dump)
if getL3Type(dump) == 4:
# Protocol field contains protocol of payload
return ip_pdu[9:10]
elif getL3Type(dump) == 6:
# Next Header field contains protocol of payload in this case
return ip_pdu[6:7]
else:
# Return an empty byterarry otherwise
return bytearray()
# end insert code
return dump
prtyprnt(identifyPayloadIP(dump_ipv4))
prtyprnt(identifyPayloadIP(dump_ipv6))
00000000: 01 . 00000000: 3A :
j) [0.2 credits] Based on your answer of i), what are the protocols contained in the two given hexdumps.
Protocol numbers are also maintained by the IANA. The IPv4 header contains a protocol field. Its value 0x01 identifies the L3-SDU as ICMPv4. The IPv6 header contains a next header field. Its value 0x3a identifies the L3-SDU as ICMPv6.
k) [0.5 credits] Explain the payload (header fields and the content type) of the IPv4 packet identified in j).
Knowing that the L3 SDU is ICMPv4, we can identify the content using the ICMPv4 type and code header fields yielding Time Exceeded / TTL expired in transit. For those ICMP messages the following 4 B are unused and thus set to zero. The payload consists in the IPv4 header of the original message that timed out plus the first 8 B of the following header.
We can thus parse this IPv4 header and find that the payload must be UDP as the protocol field is 0x11. The last 8 B are thus a UDP header containing in particular source and destination port numbers.
The IPv6 dump contains the ICMPv6 NDP neighbor solicitation message for 2001:4ca0:2001:11:225:90ff:fed7:6da0. As the Source does not know the destinations MAC address it sends it to the IPv6 multicast address.
Advanced Computer Networking by Prof. Dr.-Ing. Georg Carle
Teaching assistants: Christian Dietze, Sebastian Gallenmüller, Marcel Kempf, Lorenz Lehle, Nikolas Gauder, Patrick Dirks