1

I was working on a mini module to have low level network interface access for my university assignmets (not that this is an actual assignment, just to clarify) in python. The actual assignment was done in C, but after that I decided to get some practice with ctypes and started the lib, targeting Linux and Python 2.7.

I already know that Python 3.3 exposes a lot of the functionality I've tried to cover, and that it already has a fcntl.ioctl module since arround version 2.3 or so (it does not really matter the exact version, just that it is present in 2.7), but this task was meant for learning.

The issue I ran into is very simple. I've got the following classes defined, based on the layout of the C counterparts( for the sake of clarity, bear with the long snippet ):

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),]

And the following function:

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]

The output that this gives is -6c:39:-1b:XX:XX:XX, wich does not correspond with the real MAC, that is 94:39:e5:XX:XX:XX. One clue may be that some elements are correct, but as you can see, the output is just wrong. I can not figure why.

Surprisingly, I found a similar question in SO that aims to do almost the same, but uses packed structures, and python's own ioctl. It just works: Getting MAC Address. Still, my problem is how to correctly represent and use ctype Structures.

Community
  • 1
  • 1
  • 1
    Have you looked at the [source](http://hg.python.org/cpython/file/2.7/Modules/fcntlmodule.c) to see how Python's ioctl works? Not that this will directly answer your question, but it's pretty handy. – abarnert Feb 07 '13 at 23:22
  • Thanks. I'll have a look. It never hurts to look at the real thing. –  Feb 07 '13 at 23:25
  • Sorry, it looks like I linked to the 2.7 source instead of the 3.3. But hopefully you can find your way around from there. Anyway, I don't see anything obviously wrong with your code, except… I don't see how `info[18:24]` could be the same bytes as `ifr_hwaddr.sa_data[0:6]`. So if the answer you linked is right… Are you sure you're looking in the same field as that answer? – abarnert Feb 07 '13 at 23:32
  • Hold on, never mind, I've got it. – abarnert Feb 07 '13 at 23:32
  • 1
    Side note: You almost never need, or want, to use backslash continuations in Python. You can continue a parenthesized/brackets expression just by indenting the continuation line. – abarnert Feb 07 '13 at 23:40
  • Oops, I'll remember. Ty for the tip. –  Feb 08 '13 at 07:30

1 Answers1

0

You've got the right answer, you're just not formatting it right:

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

That's displaying signed bytes. The result you want is this:

94:39:e5:XX:XX:XX

That's the exact same values as unsigned bytes. Compare:

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

You didn't show how you're defining struct_sockaddr. You could fix it there, by just using an unsigned type, like c_uint8 * 14 in place of c_char * 14.

Alternatively, you can cast while formatting:

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

Or 30 other ways to skin the same problem.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • And that's why code reviews are really helpful :) Thank you very much again. I have to admit that it was a I was desesperate. Didn't even considered the minus signs appearing in the output, geez. –  Feb 08 '13 at 07:27