getmac icon indicating copy to clipboard operation
getmac copied to clipboard

Call to get_mac_address hangs when called with unknown IP-address

Open postlund opened this issue 5 years ago • 6 comments

Describe the bug If I call get_mac_address and specify an IP-address for which the corresponding ARP entry doesn't exist on the system, the call will hang for a long time. It doesn't matter the value of network_request.

To Reproduce

import logging
logging.basicConfig(level=logging.DEBUG)
import getmac
getmac.getmac.DEBUG = 1000  # Something high
getmac.get_mac_address(ip='10.0.0.1', network_request=False)

Yields this:

DEBUG:getmac:Trying: '_read_arp_file' (to_find: '10.0.0.1')
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: '<lambda>' (to_find: '10.0.0.1')
DEBUG:getmac:Running: '/bin/ip neighbor show 10.0.0.1'
DEBUG:getmac:Output from '/bin/ip' command: b''
DEBUG:getmac:Exception: list index out of range
DEBUG:getmac:Traceback (most recent call last):
  File "/home/postlund/pyatv_dev/pyatv/lib/python3.6/site-packages/getmac/getmac.py", line 525, in _try_methods
    found = m(to_find)
  File "/home/postlund/pyatv_dev/pyatv/lib/python3.6/site-packages/getmac/getmac.py", line 485, in <lambda>
    .partition(x)[2].partition('lladdr')[2].strip().split()[0],
IndexError: list index out of range

DEBUG:getmac:Trying: 'arp 10.0.0.1'
DEBUG:getmac:Running: '/usr/sbin/arp 10.0.0.1'
DEBUG:getmac:Output from '/usr/sbin/arp' command: b'10.0.0.1 (10.0.0.1) -- no entry\n'
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp -an'
DEBUG:getmac:Running: '/usr/sbin/arp -an'
DEBUG:getmac:Output from '/usr/sbin/arp' command: <<stripped>>
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp -an 10.0.0.1'
DEBUG:getmac:Running: '/usr/sbin/arp -an 10.0.0.1'
DEBUG:getmac:Output from '/usr/sbin/arp' command: b'arp: in 40 entries no match found.\n'
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp 10.0.0.1'
DEBUG:getmac:Running: '/usr/sbin/arp 10.0.0.1'
DEBUG:getmac:Output from '/usr/sbin/arp' command: b'10.0.0.1 (10.0.0.1) -- no entry\n'
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp -a'
DEBUG:getmac:Running: '/usr/sbin/arp -a'
<<HANGS HERE>>

The address resolution probably takes all the time. Since the same command has already been run (with expected -n flag), the last call shouldn't even be needed.

Expected behavior Should return "fast".

System info (please complete the following information):

  • OS name: Debian Linux
  • OS Version: Some old version, like 7.1 maybe
  • Python version: 3.6.1 x64

Additional context The command takes long time to execute if I run it manually from a shell as well.

postlund avatar Oct 02 '19 09:10 postlund

Thank you for the detailed report!

Generally speaking, the case of a non-existent host is currently severely punished performance wise, due to hitting every command available. This is a known issue, and one that I'm currently in the progress of addressing, though a fix probably won't be out for a while (essentially, the core logic of how commands are checked and failures handled is being refactored completely). The redundant commands is a significant part of this issue, since currently it doesn't know for sure what command will work on what platform (e.g. differing syntax between versions of command included, distro-specific customization of output that appeared in X-release of the distro only, etc.). Once fixed, if it can't find the address in, say, the /proc/net/arp, it'll fail without trying the other commands.

That notwithstanding, in this specific case it's quite odd that arp 10.0.0.1 is being run twice, and I can't tell from a cursory glance of the code why that would happen. If you get a chance, could you provide some more information?

  • Version of net-tools: apt-cache show net-tools | grep "Version"
  • Version of getmac: getmac --version (or import getmac;print(getmac.__version__);)
  • lsb_release -a
  • uname -a

GhostofGoes avatar Oct 03 '19 03:10 GhostofGoes

One potential (though far from ideal) workaround is to execute the function in a thread and timeout after a short while, e.g. thread.join(timeout=0.5). Docs: https://docs.python.org/3.7/library/threading.html#threading.Thread.join

If you can think of a way to fix it in getmac though I'm all ears, just short on time for the next few months.

GhostofGoes avatar Oct 03 '19 03:10 GhostofGoes

Thanks for the fast reply! Yeah, I agree with you that all the redundant commands are a culprit here. I'm just gonna keep it short and say that these are the offending lines:


            # Darwin oddness
            (r'\(' + esc + r'\)\s+at\s+' + MAC_RE_DARWIN,
             0, 'arp', [to_find, '-a', '-a %s' % to_find]),

I tried removing them at it worked after that (and since Debian is not Darwin, that seems reasonable too). I don't have my Mac in front of me, so I can't check what arp accepts there (in case of a non-resolve flag). Maybe just include the Darwin-stuff in case of running on Darwin as a work-around?

Also, I would recommend hardcoding paths to commands to avoid potential PATH-hijacking:

https://www.hackingarticles.in/linux-privilege-escalation-using-path-variable/

It's a complete different issue, but i mention it so I don't forget.

postlund avatar Oct 03 '19 05:10 postlund

That seems reasonable enough. Pretty busy currently, so while I may be able to get to it Saturday, it'll probably be more like next Saturday since I need to find the old Mac I used for testing. If you're able to test a fix on your Mac though I'd be fine with accepting that as well.

Thanks for the heads up on PATH, definitely agree it's a potential issue. I'll have to look into how to mitigate it without breaking compatibility.

GhostofGoes avatar Oct 04 '19 03:10 GhostofGoes

I updated my findpi package to use this and I didn't want to have it hang, so I implemented this:

def ThreadId(ipaddress, macaddress):
    macaddress = get_mac_address(ip=ipaddress)
    if macaddress:
        if ("b8:27:eb" in str(macaddress.lower())) or ("dc:a6:32" in str(macaddress.lower())):
            print(f'Found pi: {ipaddress}')


def checkMacs(ip_address):
    """
    checks if mac address found from nmap that matches raspberry pi
    Accepts: ip_address var as string
    Returns: nothing
    Prints: found ip of pi if found
    """
    macaddress = str()
    th = threading.Thread(target=ThreadId, args=(ip_address, macaddress))
    #data = get_mac_address(ip=ip_address)
    th.start()
    th.join(timeout=0.5)
    return

based on the suggestion to use thread and setting a timeout. Seems to work fine and findpi continues to be faster than nmap to find pi's on a network. Thanks!

james-see avatar Jun 24 '20 17:06 james-see

Using getmac version 0.8.3 on:

$ uname -a
Linux hosanna 5.18.10-76051810-generic #202207071639~1659403207~20.04~cb5f582~dev-Ubuntu SMP PREEMPT_DY x86_64 x86_64 x86_64 GNU/Linux
$ lsb_release -a
LSB Version:    core-11.1.0ubuntu2-noarch:printing-11.1.0ubuntu2-noarch:security-11.1.0ubuntu2-noarch
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.5 LTS
Release:        20.04
Codename:       focal

I do not get this hang. Debug output:

>>> getmac.get_mac_address(ip='10.0.0.1', network_request=False)
DEBUG:getmac:Trying: '_read_arp_file' (to_find: '10.0.0.1')
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: '<lambda>' (to_find: '10.0.0.1')
DEBUG:getmac:Running: '/sbin/ip neighbor show 10.0.0.1'
DEBUG:getmac:Output from '/sbin/ip' command: b''
DEBUG:getmac:Exception: list index out of range
DEBUG:getmac:Traceback (most recent call last):
  File "/home/jkugler/.local/lib/python3.8/site-packages/getmac/getmac.py", line 590, in _try_methods
    found = m(to_find)
  File "/home/jkugler/.local/lib/python3.8/site-packages/getmac/getmac.py", line 537, in <lambda>
    lambda x: _popen("ip", "neighbor show %s" % x)
IndexError: list index out of range

DEBUG:getmac:Trying: 'arp 10.0.0.1'
DEBUG:getmac:Running: '/usr/sbin/arp 10.0.0.1'
DEBUG:getmac:Output from '/usr/sbin/arp' command: b'10.0.0.1 (10.0.0.1) -- no entry\n'
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp -an'
DEBUG:getmac:Running: '/usr/sbin/arp -an'
DEBUG:getmac:Output from '/usr/sbin/arp' command: b'? (192.168.1.2) at c0:56:27:d2:b0:98 [ether] on wlp4s0\n? (192.168.1.187) at
 f8:0f:f9:88:2c:2e [ether] on wlp4s0\n? (192.168.1.164) at c8:e0:eb:38:31:2b [ether] on wlp4s0\n? (192.168.122.70) at <incomplet
e> on virbr0\n? (192.168.1.185) at 84:ea:ed:90:0a:5f [ether] on wlp4s0\n? (192.168.1.1) at 98:5d:ad:d8:6d:8a [ether] on wlp4s0\n
'
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp -an 10.0.0.1'
DEBUG:getmac:Running: '/usr/sbin/arp -an 10.0.0.1'
DEBUG:getmac:Output from '/usr/sbin/arp' command: b'arp: in 6 entries no match found.\n'
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp 10.0.0.1'
DEBUG:getmac:Running: '/usr/sbin/arp 10.0.0.1'
DEBUG:getmac:Output from '/usr/sbin/arp' command: b'10.0.0.1 (10.0.0.1) -- no entry\n'
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp -a'
DEBUG:getmac:Running: '/usr/sbin/arp -a'
DEBUG:getmac:Output from '/usr/sbin/arp' command: b'? (192.168.1.2) at c0:56:27:d2:b0:98 [ether] on wlp4s0\nGoogle-Nest-Mini.aza
riah.com (192.168.1.187) at f8:0f:f9:88:2c:2e [ether] on wlp4s0\nariel.azariah.com (192.168.1.164) at c8:e0:eb:38:31:2b [ether] 
on wlp4s0\n? (192.168.122.70) at <incomplete> on virbr0\nRokuUltra.azariah.com (192.168.1.185) at 84:ea:ed:90:0a:5f [ether] on w
lp4s0\ngateway (192.168.1.1) at 98:5d:ad:d8:6d:8a [ether] on wlp4s0\n'
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp -a 10.0.0.1'
DEBUG:getmac:Running: '/usr/sbin/arp -a 10.0.0.1'
DEBUG:getmac:Output from '/usr/sbin/arp' command: b'arp: in 6 entries no match found.\n'
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: '_uuid_ip' (to_find: '10.0.0.1')
DEBUG:getmac:Result: None

DEBUG:getmac:Raw MAC found: None

jkugler avatar Oct 18 '22 20:10 jkugler

This should be resolved with version 0.9.0. If it's still occurring, please re-open the issue, and include log output of the issue using the 0.9.0 release.

GhostofGoes avatar Jan 23 '23 22:01 GhostofGoes