now that we can fully decode the ip layer of any sniffed packetswe have to be able to decode the icmp responses that our scanner will elicit from sending udp datagrams to closed ports icmp messages can vary greatly in their contentsbut each message contains three elements that stay consistentthe typecodeand checksum fields the type and code fields tell the receiving host what type of icmp message is arrivingwhich then dictates how to decode it properly for the purpose of our scannerwe are looking for type value of and code value of this corresponds to the destination unreachable class of icmp messagesand the code value of indicates that the port unreachable error has been caused refer to figure - for diagram of destination unreachable icmp message figure - diagram of destination unreachable icmp message as you can seethe first bits are the type and the second bits contain our icmp code one interesting thing to note is that when host sends one of these icmp messagesit actually includes the ip header of the originating message that generated the response we can also see that we will doublecheck against bytes of the original datagram that was sent in order to make sure our scanner generated the icmp response to do sowe simply slice off the last bytes of the received buffer to pull out the magic string that our scanner sends let' add some more code to our previous sniffer to include the ability to decode icmp packets let' save our previous file as sniffer_with_icmp py and add the following code--snip --class ip(structure)--snipclass icmp(structure)_fields_ ("type"c_ubyte)("code"c_ubyte)("checksum"c_ushort)("unused"c_ushort)("next_hop_mtu"c_ushortdef __new__(selfsocket_buffer)return self from_buffer_copy(socket_bufferdef __init__(selfsocket_buffer)pass --snipprint "protocol% % -% (ip_header protocolip_header src_ addressip_header dst_address