cmv has asked for the wisdom of the Perl Monks concerning the following question:

Folks-

I have a perl program that needs to get both the current, and permanent MAC addresses of the motherboard ethernet interfaces, when running under windows.

After doing some pretty extensive research into this, I believe this is possible by using communicating directly to the device drivers via NDIS using the following OIDs:
OID_802_3_PERMANENT_ADDRESS
OID_802_3_CURRENT_ADDRESS

see: http://msdn2.microsoft.com/en-us/library/bb314146.aspx
http://msdn2.microsoft.com/en-us/library/bb314406.aspx

A very smart man once told me:
The basic strategy is to use SetupDi to enumerate: hDeviceInfoSet = SetupDiGetClassDevs( &GUID_NDIS_LAN_CLASS, NULL, // Define no enumerator (global) NULL, // Define no (DIGCF_PRESENT | // Only Devices present DIGCF_PROFILE | // In Current Hardware Profile DIGCF_INTERFACEDEVICE) // Function class devices. ); Then SetupDiEnumDeviceInterfaces and finally SetupDiGetDeviceInterface +Detail on each interface. Gets kind of messy. You will finally get the information you need to make the call on IOCTL_NDIS_QUERY_GLOBAL_STATS to fetch the permanent address.

I am looking to get access to this information from perl. The closest thing that I've found to date is Win32::NetPacket, however it requires the winpcap Packet Driver API (Packet.dll), whose copyright is not compatible with my use, and there is a bug in WinPcap 3.1 that is fixed in a later version that this module doesn't work with (yet). Also, this is very much overkill for my purposes.

I wonder if there is an easier/better/existing way to do this?

I have no clue on how to figure out if an interface exists on the motherboard or not.

Any help is much appreciated (now that I'm at novice level, I can actually bestow XP points)!

Thanks

-Craig

Update: The solution I went with can be found in my code snippet WIN32: Permanent & Current MAC Addresses. Thanks to all for the great help!

Replies are listed 'Best First'.
Re: Getting MAC Address(s) on Windows PCs
by erroneousBollock (Curate) on Sep 14, 2007 at 17:25 UTC
    Can you use Win32::API to call the API you mentioned?

    -David

      David-

      Nice suggestion! In reading through Win32::API (my head swims with confusion), I believe it may be theoretically possible to achieve this goal. However, for me to personally be able to make this happen, large quantities of head-scratching and brain-juice will be required. If there is an easier way, I would so much, rather take it. If not, bring on the brain-juice!

      I apologize if I gave any impression at all, of understanding Windows, or NDIS, or OID ioctl's in my original post.

      -Craig

Re: Getting MAC Address(s) on Windows PCs
by almut (Canon) on Sep 14, 2007 at 22:38 UTC

    Win32API::File seems to provide the required low-level calls. So, I played around a bit, essentially reverse engineering some C code I found here, and something like this does work for me (tested on Win XP):

    use Win32API::File qw(CreateFile DeviceIoControl :FILE_SHARE_ :Misc); use Win32::TieRegistry; sub IOCTL_NDIS_QUERY_GLOBAL_STATS () { 0x17 << 16 | 2 }; sub OID_802_3_PERMANENT_ADDRESS () { 0x01010101 }; sub OID_802_3_CURRENT_ADDRESS () { 0x01010102 }; sub NDIS_Query { my ($handle, $oid) = @_; my $nBytes = 0; my $buf = "\0"x10; DeviceIoControl($handle, IOCTL_NDIS_QUERY_GLOBAL_STATS(), pack("L", $oid), 0, $buf, length($buf), $nBytes, [] ); return join "-", unpack("(a2)*", unpack("H*", $buf) ); } my $key= $Registry->Open("LMachine/SOFTWARE/Microsoft/Windows NT/Curre +ntVersion/NetworkCards/2", { Access => "KEY_READ", Delimiter => "/" } ); my $adapterName = $key->{ServiceName}; print "Adapter name = $adapterName\n"; my $hMAC = CreateFile("//./$adapterName", 0, FILE_SHARE_READ(), [], OPEN_EXISTING(), 0, []) +; for ( [ "permanent" => OID_802_3_PERMANENT_ADDRESS() ], [ "current " => OID_802_3_CURRENT_ADDRESS() ], ) { my ($type, $oid) = @$_; my $mac = NDIS_Query($hMAC, $oid); print "MAC $type = $mac\n"; }

    On my machine this prints (which is the same info that the mentioned C program reports):

    Adapter name = {0AA29800-521D-4B20-888F-9D3CB9E64E96} MAC permanent = 00-0c-29-ee-47-65 MAC current = 00-0c-29-ee-47-65

    The cryptic network card name is being looked up in the registry. You might have to experiment a little with the lookup path (in particular the final "2", which is the card number). Good luck.

      Fantastic response almut! This is one of the key pieces you have cracked. Once you have an ethernet device to query, this is how get the information using these OID's.

      I believe using these OIDs to get the information is better than the other methods of getting the MAC address for a couple of reasons:
      1.) It will show you both the currently-being-used MAC address of the device, and the permanent MAC address of the device that is burned into the hardware.
      2.) This information is coming directly from the running device driver (Is that true? That is my current understanding).

      If that is true, then is it also true that manipulating it is a bit more difficult?

      Now, the remaining pieces of the puzzle are, discovering all ethernet devices (even those that are not being used), and discovering which of those are on the motherboard (by that I mean not easily removable).

      I'm compiling all the bits of information as they come in, and will post the results, once I have things working the best way for my situation.

      Many thanks for all the help

      -Craig

Re: Getting MAC Address(s) on Windows PCs
by goibhniu (Hermit) on Sep 14, 2007 at 19:15 UTC

    I'm wondering if there's a way to do this with WMI. On my Win2003, I can:
    C:\chas_sandbox>wmic nic list /?
    and it looks like MACAddress and PermanentAddress are values you can get on the "full" listing. When I do it, though, all the PermanentAddress-es are empty, and I'm not sure how you'd distinguish that you had the NIC you wanted (the one on the motherboard).

    In Re^3: killing on win32 I found Win32::Process::Info::WMI, but I haven't found one related to NICs. Perhaps DBD::WMI? but then you'd have to craft your own WMI query.


    I humbly seek wisdom.
Re: Getting MAC Address(s) on Windows PCs
by planetscape (Chancellor) on Sep 15, 2007 at 20:37 UTC
      Thanks for the pointers planetscape!

      The first link, about using wmic is also what goibhniu refers to in id:639059, and remains very interesting to me. It is new to me, and may help with this or other issues I'm looking to solve.

      The second link, about accessing motherboard information, is something that will show me information about the motherboard, not devices attached to the motherboard. However, this was a good pointer that eventually led me to accessing on-board devices, which should show me what I'm hoping for. Alas, on my IBM T60, the only on-board-device listed is the IBM Embedded Security hardware. This could be making sense, as the ethernet is (I believe) a teeny-tiny PCI board not really part of the motherboard.

      I'm slowly realizing it might be hard to identify hardware-devices-not-really-part-of-the-motherboard-but-residing-inside-the-computer-well-enough-that-it-is-somewhat-difficult-to-remove.

      -Craig