Python - 使用C类型和本机ioctl()获取Mac地址会产生未知结果

时间:2013-02-07 23:11:41

标签: python python-2.7 ctypes ioctl

我正在开发一个迷你模块,为我的大学分配(不是这是一个实际的任务,只是为了澄清)在python中具有低级网络接口访问。实际的分配是在C中完成的,但之后我决定用ctypes进行一些练习并启动lib,目标是Linux和Python 2.7。

已经知道Python 3.3暴露了我试图涵盖的许多功能,并且它已经有一个fcntl.ioctl模块,因为arround版本2.3左右(它不是真的确切的版本,只是它存在于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。一个线索可能是某些元素是正确的,但正如您所看到的,输出是错误的。我无法理解为什么。

令人惊讶的是,我在SO中发现了一个类似的问题,目的是做几乎相同的事情,但使用的是打包结构,以及python自己的ioctl。它只是有效:Getting MAC Address。不过,我的问题是如何正确表示和使用ctype结构。

1 个答案:

答案 0 :(得分: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种方法来解决同样的问题。