Find Wi-Fi Connected Devices with Python
This article provides a step by step approach to building a Python script for identifying devices connected to a Wi-Fi network. By leveraging Address Resolution Protocol (ARP) scanning and MAC address vendor lookup tools, the solution automates device discovery and enhances network visibility. Below, we explore the theory and steps, and optimization hacks for building a robust network monitoring tool.
Latest from python
Bookmark This Article
Your browser doesn't support automatic bookmarking. You can:
- Press Ctrl+D (or Command+D on Mac) to bookmark this page
- Or drag this link to your bookmarks bar:
Clicking this bookmarklet when on any page of our site will bookmark the current page.
Technical Foundations of Network Device Discovery
ARP Protocol and Network Scanning
The Address Resolution Protocol (ARP) resolves IP addresses to MAC addresses within a local network. When a device joins a Wi-Fi network, it broadcasts ARP requests to establish communication with other devices. By analyzing ARP traffic or actively sending ARP probes, we can compile a list of connected devices^1.
Python’s scapy
library simplifies ARP packet crafting and response parsing. For example, scapy.ARP(pdst=ip_range)
generates ARP requests for a specified IP range, while scapy.srp()
sends and receives packets at the data link layer^2.
Automating Interface and Subnet Detection
Manually specifying the network interface and IP range is error-prone. Instead, the netifaces
library programmatically retrieves the default gateway’s IP and subnet mask. The netifaces.gateways()
function identifies the active interface, while netifaces.ifaddresses()
extracts its IP configuration^4.
import netifaces
def get_default_gateway():
gateways = netifaces.gateways()
default = gateways.get('default', {})
return default.get(netifaces.AF_INET, (None, None))[^1]
This code snippet fetches the default interface and its IP, enabling dynamic adaptation to different network environments^5.
Step-by-Step Implementation
Step 1: Install Dependencies
Install required libraries using pip
:
pip install scapy netifaces mac-vendor-lookup
- scapy: Craft and send ARP packets^2.
- netifaces: Automate interface and IP detection^4.
- mac-vendor-lookup: Resolve MAC addresses to vendor names^7.
Step 2: Perform ARP Scan
The arp_scan()
function sends ARP requests to all IPs in the subnet and parses responses:
from scapy.all import ARP, Ether, srp
def arp_scan(ip_range, interface):
arp_request = ARP(pdst=ip_range)
broadcast = Ether(dst="ff:ff:ff:ff:ff:ff")
packet = broadcast/arp_request
answered, _ = srp(packet, iface=interface, timeout=2, verbose=False)
devices = []
for sent, received in answered:
devices.append({'ip': received.psrc, 'mac': received.hwsrc})
return devices
This function returns a list of dictionaries containing IP and MAC addresses^1.
Step 3: Resolve MAC Vendors
The mac-vendor-lookup
library queries a local copy of the IEEE OUI database to identify device manufacturers:
from mac_vendor_lookup import MacLookup
def get_vendor(mac):
try:
return MacLookup().lookup(mac)
except:
return "Unknown"
For accuracy, update the vendor list periodically using MacLookup().update_vendors()
^7.
Integrating Components into a Complete Script
Dynamic IP Range Calculation
Convert the gateway IP and subnet mask into a CIDR notation (e.g., 192.168.1.0/24
):
import ipaddress
def calculate_subnet(gateway_ip, netmask):
network = ipaddress.IPv4Network(f"{gateway_ip}/{netmask}", strict=False)
return str(network)
Full Script
Combine all components into a single script:
import netifaces
import ipaddress
from scapy.all import ARP, Ether, srp
from mac_vendor_lookup import MacLookup
def get_network_info():
gateways = netifaces.gateways()
default_gateway = gateways['default'][netifaces.AF_INET]
interface = default_gateway[^1]
gateway_ip = default_gateway[^0]
addresses = netifaces.ifaddresses(interface)[netifaces.AF_INET][^0]
netmask = addresses['netmask']
subnet = ipaddress.IPv4Network(f"{gateway_ip}/{netmask}", strict=False)
return interface, str(subnet)
def main():
interface, subnet = get_network_info()
print(f"Scanning {subnet} on interface {interface}...")
devices = arp_scan(subnet, interface)
print("\nConnected Devices:")
print("IP Address\t\tMAC Address\t\tVendor")
for device in devices:
vendor = get_vendor(device['mac'])
print(f"{device['ip']}\t\t{device['mac']}\t\t{vendor}")
if __name__ == "__main__":
main()
Optimization and Advanced Features
Asynchronous Scanning
To improve performance on large networks, implement asynchronous scanning using scapy
’s AsyncSniffer
:
from scapy.all import AsyncSniffer
async def async_arp_scan(ip_range, interface):
sniffer = AsyncSniffer()
sniffer.start()
# Send ARP requests and analyze responses asynchronously
Scheduled Scans
Use schedule
library to run scans periodically:
import schedule
import time
schedule.every(5).minutes.do(main)
while True:
schedule.run_pending()
time.sleep(1)
Conclusion and Recommendations
This script provides a foundational tool for network monitoring. For enterprise environments, consider integrating with SNMP for deeper insights or adding alerts for unauthorized devices. Always ensure compliance with local network policies, as ARP scanning may trigger security alerts. Future enhancements could include GUI dashboards or integration with intrusion detection systems.
By combining low-level packet manipulation with vendor databases, Python enables flexible and powerful network analysis tailored to specific operational needs.