You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 18:24:54