How do I find the MAC address in C on different UNIX platforms?

I need to find the MAC address of the ethernet cards on the host machine from the C language. I have found a way to do this on Linux using socket(), ioctl() and the ifreq structure. But this does not seem to work on AIX, HP/UX and probably the others I need (Solaris, SCO, Alpha etc).

Is there a standard way to do this in UNIX?
Why do the UNIX ifreq structures not have a MAC address field like Linux?
Or and I missing something here? It would be fine with me if I need to to it differently for each platform, if I have to. I just need to know what calls to use for each type of UNIX.

Please help!

Thank you.

On Solaris, there is a struct member ifru_enaddr in ifreq, which holds the 6 byte ethernet address. I think, it is also present in other unix implementations.

On GNU/Linux, use the SIOCGIFHWADDR ioctl.

Thank you both for answering my question. I had to be taken off of this project for a while and now I am back, and able to see what I can do based on your suggestions.

Thanks again for your help.

Hi, I had a chance to continue with this project. But I did not have much luck. In case someone has any more advice here is what I found.

On AIX I have a ifreq struct like this:

struct    ifreq {
#define    IFNAMSIZ    16
    char    ifr_name[IFNAMSIZ];        /* if name, e.g. "en0" */
    union {
        struct    sockaddr ifru_addr;
        struct    sockaddr ifru_dstaddr;
        struct    sockaddr ifru_broadaddr;
        __ulong32_t    ifru_flags;
        int    ifru_metric;
        caddr_t    ifru_data;
        u_short    ifru_site6;
#ifdef    _AIX
        __ulong32_t   ifru_mtu;
        int     ifru_baudrate;
#endif
    } ifr_ifru;

Notice that there is no enaddr or anything similar. I find something very similar on HP/UX and SCO but on Solaris I have:

struct    ifreq {
#define    IFNAMSIZ    16
    char    ifr_name[IFNAMSIZ];        /* if name, e.g. "en0" */
    union {
        struct    sockaddr ifru_addr;
        struct    sockaddr ifru_dstaddr;
        char    ifru_oname[IFNAMSIZ];    /* other if name */
        struct    sockaddr ifru_broadaddr;
        int    ifru_index;        /* interface index */
        short    ifru_flags;
        int    ifru_metric;
        char    ifru_data[1];        /* interface dependent data */
        char    ifru_enaddr[6];
        int    if_muxid[2];        /* mux id's for arp and ip */

        /* blah blah blah more stuff */
    } ifr_ifru;

The dates on these header files tend to be around 1998 or 1999 too.

Thanks to the comments from hergp I was able to get the solaris version to compile but then at run time it complained about being unable to link to socket.o so I tried linking it to libsocket.a (because we like to have everything statically linked), but then other references in libsocket.a were unfound, and I had no idea where they are located. So, in the end I was still not able to do this. On Linux it all works fine.

Some of these platforms are using GNU but others are using the native C/C++ compiler.

I was hoping there was a standard way to handle this going back to the System V and 1980s but it appears not.

Any suggestions? Any other methods of doing this quickly and accurately.

Thanks!

libsocket.so on Solaris has these dependencies:

$ ldd libsocket.so
        libnsl.so.1 =>   /lib/libnsl.so.1
        libc.so.1 =>     /lib/libc.so.1
        libmp.so.2 =>    /lib/libmp.so.2
        libmd.so.1 =>    /lib/libmd.so.1
        libscf.so.1 =>   /lib/libscf.so.1
        libdoor.so.1 =>  /lib/libdoor.so.1
        libuutil.so.1 =>         /lib/libuutil.so.1
        libgen.so.1 =>   /lib/libgen.so.1
        libm.so.2 =>     /lib/libm.so.2

Which libraries you really need when you link your program statically depends on your code. But when you try to add the static versions of these libraries one by one, you can find out, which of them you really need to resolve all your unresolved symbols. Or you just add all of those libraries and rely on ld to link just those objects it needs.

Thank you for your help.

I did what you did, but on my platform. I did ldd libsocket.so to see the list in my context. It was different but I continued.

I tried what you said and linked with
/lib/libsocket.a and /lib/libnsl.a
then I had only a few left and they seemed related to lindl so I tried linking to libdl.a too but discovered there was no libdl.a only a lindl.so so I tried linking to this in hopes that it would resolve the static linkage but then find libdl.so and link dynamically at run time. As I mentioned, I normally avoid .so files to avoid run time linkage issues, but I tried it.

The executable linked but when I ran it all the calls to ioctl returned -1 (error). Keep in mind that when I make these same calls (or similar) on Linux they work. I suspect the linkage more than the calling code. All I am doing at the point I get the error is calling ioctl() with
a handle value of 8, SIOCGIFADDR and IfReq.ifr_name set to "eth0"
The handle is from a call to
socket(AF_INET, SOCK_DGRAM, IP_PROTOIP)

Any other ideas?

Can you tell me in a nutshell how the so system works. For example, when I link with a .so file, does it resolve the static linkage issue and the at runtime go out looking for the same .so file to dynamically link to it?
If this is the case, then what are the rules for finding the .so file (in case the location of the .so file on my compiling platform is different that the end users platform)?
Could I have inked with libsocket.so directly? Is this any better? Or is it just a matter of preference?
My concern is of course two things:

  • why did it link and the fail to run properly (assuming my code is OK)?
  • how can I be sure my code will run reliably using .so files if I do get it to work?

Thank you for your insights.

Dynamic linking in Solaris is explained here: 3.Runtime Linker (Linker and Libraries Guide) - Sun Microsystems