shon
shon

Reputation: 149

How can I get the arp table from a windows machine using python?

I want to get the arp table from a windows system using Python. I saw that the library python_arptable is compatible only to linux and I don't want to use the arp -a command. Is there any python library that will allow me to extract it? Or maybe using ctypes?

Upvotes: 2

Views: 6499

Answers (1)

Mark Tolonen
Mark Tolonen

Reputation: 178115

Why not use arp -a? If nothing else, you can capture with os.popen and parse:

>>> import os
>>> with os.popen('arp -a') as f:
...  data = f.read()
...
>>> import re
>>> for line in re.findall('([-.0-9]+)\s+([-0-9a-f]{17})\s+(\w+)',data):
...  print(line)
...
('192.168.1.1', 'cc-40-d0-19-73-e8', 'dynamic')
('192.168.1.2', 'e0-89-7e-15-f3-c0', 'dynamic')
('192.168.1.4', 'd0-d2-b0-12-c0-d1', 'dynamic')
('192.168.1.7', '00-11-d9-66-13-9d', 'dynamic')
('224.0.0.22', '01-00-5e-00-00-16', 'static')
('224.0.0.251', '01-00-5e-00-00-fb', 'static')
('224.0.0.252', '01-00-5e-00-00-fc', 'static')
('224.0.2.60', '01-00-5e-00-02-3c', 'static')
('239.255.255.250', '01-00-5e-7f-ff-fa', 'static')
('255.255.255.255', 'ff-ff-ff-ff-ff-ff', 'static')

Otherwise, here's a ctypes solution that is not so straight-forward (Python 3.6+):

from ctypes import *
from ctypes import wintypes as w
import struct

MAXLEN_PHYSADDR = 8

TYPE = {1:'other',2:'invalid',3:'dynamic',4:'static'}

class MIB_IPNETROW(Structure):
    _fields_ = (('dwIndex',w.DWORD),
                ('dwPhysAddrLen',w.DWORD),
                ('bPhysAddr',w.BYTE * MAXLEN_PHYSADDR),
                ('dwAddr',w.DWORD),
                ('dwType',w.DWORD))
    def __repr__(self):
        ip = struct.pack('<L',self.dwAddr)
        ip = f'{ip[0]}.{ip[1]}.{ip[2]}.{ip[3]}'
        mac = bytes(self.bPhysAddr)[:self.dwPhysAddrLen]
        mac = '-'.join(f'{b:02x}' for b in mac)
        return f"MIB_IPNETROW({self.dwIndex},{mac},{ip},{TYPE[self.dwType]})"

# ctypes doesn't do variable size-d structures well.
# This helper generates an IPNETTABLE class with a table
# array equal to "n".
def TABLE(n):
    class _MIB_IPNETTABLE(Structure):
        _fields_ = (('dwNumEntries',w.DWORD),
                    ('table',MIB_IPNETROW * n))
    return _MIB_IPNETTABLE

MIB_IPNETTABLE = TABLE(0) # general version to use with pointers.

dll = WinDLL('iphlpapi')
dll.GetIpNetTable.argtypes = POINTER(MIB_IPNETTABLE),w.PULONG,w.BOOL
dll.GetIpNetTable.restype = w.ULONG

# Query with a buffer size of zero to get actual size
size = w.DWORD(0)
dll.GetIpNetTable(None,byref(size),True)

# Generate buffer of correct size and pointer type.
buf = cast(create_string_buffer(b'',size=size.value),POINTER(MIB_IPNETTABLE))

# Get the table and cast to the specific table size
dll.GetIpNetTable(buf,byref(size),True)
buf = cast(buf,POINTER(TABLE(buf.contents.dwNumEntries)))

# Display the table entries (leverages __repr__ defined above)
for t in buf.contents.table:
    if t.dwType != 2 and t.dwPhysAddrLen: # not invalid and has an address
        print(t)
MIB_IPNETROW(11,cc-40-d0-19-73-e8,192.168.1.1,dynamic)
MIB_IPNETROW(11,e0-89-7e-15-f3-c0,192.168.1.2,dynamic)
MIB_IPNETROW(11,d0-d2-b0-12-c0-d1,192.168.1.4,dynamic)
MIB_IPNETROW(11,00-11-d9-66-13-9d,192.168.1.7,dynamic)
MIB_IPNETROW(11,ff-ff-ff-ff-ff-ff,192.168.1.255,static)
MIB_IPNETROW(11,01-00-5e-00-00-16,224.0.0.22,static)
MIB_IPNETROW(11,01-00-5e-00-00-fb,224.0.0.251,static)
MIB_IPNETROW(11,01-00-5e-00-00-fc,224.0.0.252,static)
MIB_IPNETROW(11,01-00-5e-7f-ff-fa,239.255.255.250,static)
MIB_IPNETROW(11,ff-ff-ff-ff-ff-ff,255.255.255.255,static)
MIB_IPNETROW(16,01-00-5e-00-00-16,224.0.0.22,static)

Upvotes: 6

Related Questions