romana / multi-ping

Python library to monitor one or many IP addresses via ICMP echo (ping) requests

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Host-unreachable replies

blueoak opened this issue · comments

Looks like replies to ICMP Echo-request messages (ping) which are not Echo-reply are not being handled properly in the receive method and can be interpreted as a positive reply. Specifically, pings to some addresses can return an ICMP host-unreachable message, which, for my specific purpose, should be interpreted as a non-reply. As a quick fix, I added an
else: pass
clause after the
if pkt[_ICMPV6_HDR_OFFSET] == _ICMPV6_ECHO_REPLY:
...
elif pkt[_ICMP_HDR_OFFSET] == _ICMP_ECHO_REPLY:
...
clauses, but I would also suggest a better check for IP4 vs. IP6 packets instead of looking at a byte at a particular location.

Agreed! treating "host unreachable" reply packets as a non-reply is essential. Based on the above, this is my version which also does a better check to distinguish IPv4 from IPv6 packets:

# Some offsets we use when extracting data from the header
_ICMP_VER_OFFSET       = 0

                try:
                    pkt_id = None
                    pkt_ident = None
                    pkt_ver = pkt[_ICMP_VER_OFFSET] >> 4
                    if pkt_ver == 6 and \
                       pkt[_ICMPV6_HDR_OFFSET] == _ICMPV6_ECHO_REPLY:

                        pkt_id = (pkt[_ICMPV6_ID_OFFSET] << 8) + \
                            pkt[_ICMPV6_ID_OFFSET + 1]
                        pkt_ident = (pkt[_ICMPV6_IDENT_OFFSET] << 8) + \
                            pkt[_ICMPV6_IDENT_OFFSET + 1]
                        payload = pkt[_ICMPV6_PAYLOAD_OFFSET:]

                    elif pkt_ver == 4 and \
                       pkt[_ICMP_HDR_OFFSET] == _ICMP_ECHO_REPLY:

                        pkt_id = (pkt[_ICMP_ID_OFFSET] << 8) + \
                            pkt[_ICMP_ID_OFFSET + 1]
                        pkt_ident = (pkt[_ICMP_IDENT_OFFSET] << 8) + \
                            pkt[_ICMP_IDENT_OFFSET + 1]
                        payload = pkt[_ICMP_PAYLOAD_OFFSET:]
                    
                    else:
                        # Silently ignore other ICMP packet types (not _ECHO_REPLY)
                        pass

Possibly could be improved further by extracting the pkt_id/pkt_ident for other ICMP packet types, and removing those from self._remaining_ids without setting the results dict? That would allow the function to return sooner in the event of non-ECHO_REPLY packets, instead of waiting for timeout.