Question

I'm looking for a way (with python) to obtain the layer II address from a device on my local network. Layer III addresses are known.

The goal is to build a script that will poll a databases of IP addresses on regular intervals ensuring that the mac addresses have not changed and if they have, email alerts to myself.

Was it helpful?

Solution

To answer the question with Python depends on your platform. I don't have Windows handy, so the following solution works on the Linux box I wrote it on. A small change to the regular expression will make it work in OS X.

First, you must ping the target. That will place the target -- as long as it's within your netmask, which it sounds like in this situation it will be -- in your system's ARP cache. Observe:

13:40 jsmith@undertow% ping 97.107.138.15
PING 97.107.138.15 (97.107.138.15) 56(84) bytes of data.
64 bytes from 97.107.138.15: icmp_seq=1 ttl=64 time=1.25 ms
^C

13:40 jsmith@undertow% arp -n 97.107.138.15
Address                  HWtype  HWaddress           Flags Mask            Iface
97.107.138.15            ether   fe:fd:61:6b:8a:0f   C                     eth0

Knowing that, you do a little subprocess magic -- otherwise you're writing ARP cache checking code yourself, and you don't want to do that:

>>> from subprocess import Popen, PIPE
>>> import re
>>> IP = "1.2.3.4"

>>> # do_ping(IP)
>>> # The time between ping and arp check must be small, as ARP may not cache long

>>> pid = Popen(["arp", "-n", IP], stdout=PIPE)
>>> s = pid.communicate()[0]
>>> mac = re.search(r"(([a-f\d]{1,2}\:){5}[a-f\d]{1,2})", s).groups()[0]
>>> mac
"fe:fd:61:6b:8a:0f"

OTHER TIPS

There was a similar question answered not too long ago on this site. As mentioned in the answer chosen by the asker of that question, Python doesn't have a built-in way to do it. You must either call a system command such as arp to get ARP information, or generate your own packets using Scapy.

Edit: An example using Scapy from their website:

Here is another tool that will constantly monitor all interfaces on a machine and print all ARP request it sees, even on 802.11 frames from a Wi-Fi card in monitor mode. Note the store=0 parameter to sniff() to avoid storing all packets in memory for nothing.

#! /usr/bin/env python
from scapy import *

def arp_monitor_callback(pkt):
    if ARP in pkt and pkt[ARP].op in (1,2): #who-has or is-at
        return pkt.sprintf("%ARP.hwsrc% %ARP.psrc%")

sniff(prn=arp_monitor_callback, filter="arp", store=0)

Not exactly what you're looking for, but definitely on the right track. Enjoy!

Sounds like you want to monitor ARP spoofers? In this case, all you need is arpwatch, available in every well-supplied Linux distribution near you. Download sources here: http://ee.lbl.gov/

for Unix based systems:

#!/usr/bin/env python2.7

import re
import subprocess
arp_out =subprocess.check_output(['arp','-lan'])

re.findall(r"((\w{2,2}\:{0,1}){6})",arp_out)

will return list of tuples with macs. scapy is an amazing tool , but seems to be overkill for this case

In Linux sometimems you miss the command line util "arp". A base yocto linux embedded environment image for instance.

An alternative way without the "arp" tool would be to read and parse the file /proc/net/arp:

root@raspberrypi:~# cat /proc/net/arp
IP address       HW type     Flags       HW address            Mask     Device
192.168.1.1      0x1         0x2         xx:xx:xx:xx:xx:xx     *        wlan0
192.168.1.33     0x1         0x2         yy:yy:yy:yy:yy:yy     *        wlan0

an easier way, if on linux:

print os.system('arp -n ' + str(remoteIP))

you will get:

    Address        HWtype  HWaddress           Flags Mask            Iface
    192.168.....   ether   9B:39:15:f2:45:51   C                     wlan0

A simple solution using scapy, to scan the 192.168.0.0/24 subnet is as follows:

from scapy.all import *

ans,unans = arping("192.168.0.0/24", verbose=0)
for s,r in ans:
    print("{} {}".format(r[Ether].src,s[ARP].pdst))

Try using the netifaces found here. netifaces

General update for Python 3.7. Remark: the option -n for arp does not provide the arp list on windows systems as provided with certain answers for linux based systems. Use the option -a as stated in the answer here.

from subprocess import Popen, PIPE

pid = Popen(['arp', '-a', ip], stdout=PIPE, stderr=PIPE)

IP, MAC, var = ((pid.communicate()[0].decode('utf-8').split('Type\r\n'))[1]).split('     ')
IP  =  IP.strip(' ')
MAC =  MAC.strip(' ')

if ip == IP:
    print ('Remote Host : %s\n        MAC : %s' % (IP, MAC))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top