Python - 使用C类型和原生ioctl()获取Mac地址时结果未知

1 投票
1 回答
748 浏览
提问于 2025-04-17 15:10

我在做一个小模块,目的是为了在大学作业中实现低级别的网络接口访问(只是为了说明,这并不是一个真正的作业)。这个作业是用C语言完成的,但之后我决定练习一下ctypes,于是开始了这个库,目标是Linux和Python 2.7。

已经知道Python 3.3提供了很多我尝试覆盖的功能,而且从大约2.3版本开始就有了一个叫做fcntl.ioctl的模块(具体版本不重要,只要在2.7中存在就行),但这个任务是为了学习。

我遇到的问题非常简单。我定义了以下类,基于C语言对应的结构(为了清晰起见,请耐心看这个长代码片段):

class struct_ifmap(Structure):
    _fields_ = [
        ('mem_start', c_ulong),
        ('mem_end', c_ulong),
        ('base_addr', c_ushort),
        ('irq', c_ubyte),
        ('dma', c_ubyte),
        ('port', c_ubyte),]

class union_ifreq_anon(Union):
    _fields_ = [
        ('ifr_addr', struct_sockaddr), 
        ('ifr_dstaddr', struct_sockaddr),
        ('ifr_broadaddr', struct_sockaddr),
        ('ifr_netmask', struct_sockaddr),
        ('ifr_hwaddr', struct_sockaddr),
        ('ifr_flags', c_short),
        ('ifr_ifindex', c_int),
        ('ifr_metric', c_int),
        ('ifr_mtu', c_int),
        ('ifr_map', struct_ifmap),
        ('ifr_slave', c_char),
        ('ifr_newname', c_char),
        ('ifr_data', c_char_p),
        ('raw', c_short * 15),]

class struct_ifreq(Structure):
    __IFNAMSIZ = 16
    _anonymous_ = [
        ('u'),]
    _fields_ = [
        ('ifr_name', c_char * __IFNAMSIZ),
        ('u', union_ifreq_anon),]

还有以下这个函数:

def _getfahwdraddr(name):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
        socket.IPPROTO_IP)
    ifr = struct_ifreq()
    ifr.ifr_name = name
    result = _libc.ioctl(sock.fileno(), 0x8927, pointer(ifr))
    if result != 0:
        raise OSError(get_errno())
    return ''.join(['%02x:' % pair for pair in \
ifr.ifr_hwaddr.sa_data[0:6]])[:-1]

这个函数的输出是 -6c:39:-1b:XX:XX:XX,和真实的MAC地址94:39:e5:XX:XX:XX不符。一个线索是,有些元素是正确的,但如你所见,输出就是错的。我搞不清楚为什么。

令人惊讶的是,我在StackOverflow上找到了一个类似的问题,目标几乎相同,但使用了打包结构和Python自己的ioctl。那个方法可以正常工作:获取MAC地址。不过,我的问题是如何正确表示和使用ctypes结构。

1 个回答

0

你得到了正确的答案,只是格式没有弄对:

-6c:39:-1b:XX:XX:XX

这段代码显示的是带符号的字节。你想要的结果应该是这样的:

94:39:e5:XX:XX:XX

这些值和无符号字节是完全一样的。对比一下:

>>> hex(0x100-0x6c)
'0x94'
>>> hex(0x100-0x1b)
'0xe5'

你没有展示你是怎么定义 struct_sockaddr 的。你可以在这里修正,只需用无符号类型,比如用 c_uint8 * 14 替代 c_char * 14

另外,你也可以在格式化的时候进行转换:

return ''.join(['%02x:' % c_uint8(pair).value for pair in
                ifr.ifr_hwaddr.sa_data[0:6]])[:-1]

或者还有30种其他方法来解决同样的问题。

撰写回答