如何在Linux下用Python获取无IP直连以太网设备的MAC地址
在Linux下用Python获取直连以太网设备的MAC地址(无IP场景)
你的现有代码通过ioctl调用获取的是本地网卡自身的MAC地址,无法获取直连设备的MAC——因为当设备未分配IP时,系统ARP缓存中不会有对应条目,必须主动发起链路层探测才能让设备响应。以下是几种可行方案:
方案1:主动发送ARP请求(依赖Scapy库)
这是最通用的方法,通过广播ARP请求强制直连设备响应,即使它没有IP地址。
- 先安装Scapy:
pip install scapy
- Python代码示例:
from scapy.all import ARP, Ether, srp def get_connected_mac(iface="eth0"): # 构造ARP请求,目标IP设为0.0.0.0(针对直连无IP设备),广播MAC地址 arp = ARP(pdst="0.0.0.0") ether = Ether(dst="ff:ff:ff:ff:ff:ff") packet = ether / arp # 发送数据包,等待2秒响应,关闭冗余输出 result = srp(packet, iface=iface, timeout=2, verbose=0)[0] # 提取响应中的MAC地址 connected_devices = [] for sent, received in result: # 无IP设备的psrc可能为空或0.0.0.0,重点取hwsrc connected_devices.append({ "ip": received.psrc, "mac": received.hwsrc }) return connected_devices if __name__ == "__main__": devices = get_connected_mac("eth0") print("直连设备信息:") for dev in devices: print(f"IP: {dev['ip']}, MAC: {dev['mac']}")
方案2:读取系统ARP缓存(仅适用于曾通信过的设备)
如果目标设备之前和主机有过网络交互,系统ARP缓存会保留其MAC记录,可以直接读取/proc/net/arp文件:
def get_arp_cache(iface="eth0"): valid_entries = [] with open("/proc/net/arp", "r") as f: # 跳过表头行,遍历有效条目 for line in f.readlines()[1:]: parts = line.strip().split() ip_addr = parts[0] mac_addr = parts[3] interface = parts[5] # 过滤当前网卡的有效MAC(排除全0无效值) if interface == iface and mac_addr != "00:00:00:00:00:00": valid_entries.append({ "ip": ip_addr, "mac": mac_addr }) return valid_entries if __name__ == "__main__": entries = get_arp_cache("eth0") print("ARP缓存中的直连设备:") for entry in entries: print(f"IP: {entry['ip']}, MAC: {entry['mac']}")
注意:此方法局限性大,仅能获取曾通信过的设备MAC,无法探测首次连接的无IP设备。
方案3:Raw Socket链路层探测(无需第三方库)
直接构造以太网帧发送探测请求,无需依赖第三方库,但需要root权限运行:
import socket import struct def probe_connected_mac(iface="eth0"): # 创建RAW Socket,绑定到指定网卡 sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0003)) sock.bind((iface, 0)) # 获取本地网卡MAC地址 local_mac = sock.getsockname()[4] # 构造ARP请求帧 # 以太网帧头部:广播MAC + 本地MAC + ARP协议类型 eth_header = b'\xff\xff\xff\xff\xff\xff' + local_mac + b'\x08\x06' # ARP请求内容:硬件类型、协议类型、地址长度、操作码、发送端信息、目标端信息 arp_payload = ( b'\x00\x01' + # 硬件类型:以太网 b'\x08\x00' + # 协议类型:IPv4 b'\x06' + b'\x04' + # MAC长度6,IP长度4 b'\x00\x01' + # 操作码:ARP请求 local_mac + b'\x00\x00\x00\x00' + # 本地IP设为0.0.0.0 b'\xff\xff\xff\xff\xff\xff' + # 目标MAC广播 b'\x00\x00\x00\x00' # 目标IP设为0.0.0.0 ) # 组合完整帧 frame = eth_header + arp_payload # 发送探测帧 sock.send(frame) # 监听响应 try: while True: data = sock.recvfrom(1024)[0] # 解析以太网帧头部 recv_src_mac = data[6:12] recv_eth_type = data[12:14] # 只处理ARP响应 if recv_eth_type == b'\x08\x06' and data[20:22] == b'\x00\x02': mac_str = ':'.join('%02x' % b for b in recv_src_mac) print(f"探测到直连设备MAC:{mac_str}") break except KeyboardInterrupt: pass finally: sock.close() if __name__ == "__main__": probe_connected_mac("eth0")
内容的提问来源于stack exchange,提问作者user9734041
相关产品推荐
相关产品推荐

