Page 1 of 1

Using the MIB_IF_ROW2 structure - Windows Vista and up

Posted: Thu Feb 07, 2013 3:57 pm
by SFSxOI
This is for Windows Vista, Windows 7, and now Windows 8

Someone asked me about using the MIB_IF_ROW2 structure and setting it up so I provided the below to them as an example and decided i'd post it here also in case anyone else wanted use info on the structure. The rest of the structre can be read as well for the items indicated by their names.

Code: Select all

; setup for x 86 this implementation but should work fine on x64 and x86 with minor changes if needed at all
; this is basic first draft example POC code
; this implementation for hardware NIC's
; example use only

Prototype PRtlZeroMemory(mem, sze) : Prototype PGetIfEntry2(pIfRow) : Prototype PGetIfTable2(pRow)
Prototype PFreeMibTable(pIfRow) : Prototype PStringFromGUID2(rguid, lpsz, cchMax)
Global ZeroMemory.PRtlZeroMemory : Global GetIfEntry2.PGetIfEntry2 : Global GetIfTable2.PGetIfTable2 : Global FreeMibTable.PFreeMibTable
Global StringFromGUID2.PStringFromGUID2

OpenLibrary(1, "Kernel32.dll") : OpenLibrary(2, "iphlpapi.dll") : OpenLibrary(3,"Ole32.dll")
ZeroMemory.PRtlZeroMemory=GetFunction(1, "RtlZeroMemory") : GetIfEntry2.PGetIfEntry2=GetFunction(2,"GetIfEntry2")
GetIfTable2.PGetIfTable2=GetFunction(2,"GetIfTable2") : FreeMibTable.PFreeMibTable=GetFunction(2,"FreeMibTable")
StringFromGUID2.PStringFromGUID2=GetFunction(3,"StringFromGUID2")

Enumeration ;#NET_IF_DIRECTION_TYPE
   #NET_IF_DIRECTION_SENDRECEIVE
   #NET_IF_DIRECTION_SENDONLY
   #NET_IF_DIRECTION_RECEIVEONLY
   #NET_IF_DIRECTION_MAXIMUM
 EndEnumeration
 
 Enumeration ; NET_IF_MEDIA_CONNECT_STATE
  #MediaConnectStateUnknown
  #MediaConnectStateConnected
  #MediaConnectStateDisconnected
EndEnumeration

Enumeration ; IF_OPER_STATUS - the type in MIB_IF_ROW2
  #IfOperStatusUp = 1
  #IfOperStatusDown
  #IfOperStatusTesting
  #IfOperStatusUnknown
  #IfOperStatusDormant
  #IfOperStatusNotPresent
  #IfOperStatusLowerLayerDown
EndEnumeration
 
#IF_MAX_STRING_SIZE = 256
#IF_MAX_PHYS_ADDRESS_LENGTH = 32
#MAX_ADAPTER_NAME = 128
#MIB_IF_ADMIN_STATUS_UP = 1
#MIB_IF_ADMIN_STATUS_DOWN = 2
#MIB_IF_ADMIN_STATUS_TESTING = 3

Structure MIB_IF_ROW2_FLAGS ;->Structure MIB_IF_ROW2_FLAGS
  HardwareInterface.b ;index 0
  FilterInterface.b;index 1
  ConnectorPresent.b;index 2
  NotAuthenticated.b;index 3
  NotMediaConnected.b;index 4
  Paused.b;index 5
  LowPower.b;index 6
  EndPointInterface.b;index 7
EndStructure

Structure NET_LUID  ;->Structure NET_LUID
  Reserved.q[24]
  NetLuidIndex.q[24]
  IfType.q[16]
EndStructure

Structure MIB_IF_ROW2  ;->Structure MIB_IF_ROW2
  StructureUnion ;-> NET_LUID - is another name for IF_LUID
    Value.q 
    *Info.NET_LUID
  EndStructureUnion
  InterfaceIndex.l
  InterfaceGuid.b[16]
  Alias.w[#IF_MAX_STRING_SIZE +1]
  Description.w[#IF_MAX_STRING_SIZE +1]
  PhysicalAddressLength.l
  PhysicalAddress.b[#IF_MAX_PHYS_ADDRESS_LENGTH]
  PermanentPhysicalAddress.b[#IF_MAX_PHYS_ADDRESS_LENGTH]
  Mtu.l
  Type.l
  TunnelType.l
  MediaType.l
  PhysicalMediumType.l
  AccessType.l
  DirectionType.l
  *InterfaceAndOperStatusFlags.MIB_IF_ROW2_FLAGS
  OperStatus.l
  AdminStatus.l
  MediaConnectState.l
  NetworkGuid.b[16]
  ConnectionType.l
  pad.l ; need 4 byte pad here on x86 systems to read quad values correctly below ConnectionType member - may not need on x64 but not tested as only need x86 in this use 
  TransmitLinkSpeed.q
  ReceiveLinkSpeed.q
  InOctets.q
  InUcastPkts.q
  InNUcastPkts.q
  InDiscards.q
  InErrors.q
  InUnknownProtos.q
  InUcastOctets.q
  InMulticastOctets.q
  InBroadcastOctets.q
  OutOctets.q
  OutUcastPkts.q
  OutNUcastPkts.q
  OutDiscards.q
  OutErrors.q
  OutUcastOctets.q
  OutMulticastOctets.q
  OutBroadcastOctets.q
  OutQLen.q ; not used
EndStructure

Structure MIB_IF_TABLE2  ;->Structure MIB_IF_TABLE2
  NumEntries.l
  Table.MIB_IF_ROW2[256]
EndStructure

Procedure.s ReadMIBRowBitField(number.l, index.l, length) 
  Protected bitfield$ 
  bitfield$=RSet(Bin(number), 32, "0")
  ProcedureReturn Mid(bitfield$, 32-index, length)
EndProcedure

Procedure.s GetMACs(mib_mac_ifindex.i, rettype.i = 0)
  Protected a.i, retval.i, ifIndex.i
  Protected hardwareIf.b, MIBconnectorpresent.b, MIBmediaconnected.b, MIBifendpoint.b
  retval = 0
  ifIndex = mib_mac_ifindex 
  *pIfRowmac.MIB_IF_ROW2 = AllocateMemory(SizeOf(MIB_IF_ROW2))
  
  ; zero memory to make sure clean from previous enumeration
  ZeroMemory(*pIfRowmac, SizeOf(MIB_IF_ROW2))
  
  *pIfRowmac\InterfaceIndex = ifIndex ; the index of the interface for which we need MACs/info
  
  retval = GetIfEntry2(*pIfRowmac) 
  If retval = #NO_ERROR
    retval = #NO_ERROR
  ElseIf *pIfRowmac
    FreeMemory(*pIfRowmac)
  EndIf
  
  ; we only want actual hardware interface adapters and not interface software/logical constructs
  ; interface adapters are hardware, interfaces and seperate non-hardware adapters are software/logical constructs
  ; ... with actual connector to connect with (for example) lan cable from NIC to router, cable modem, etc...
  ; (InterfaceAndOperStatusFlags, 0, 1)) = #True if actual hardware interface adapter
  ; (InterfaceAndOperStatusFlags, 2, 1)) = #True if has an actual connector for lan cable from NIC to router, cable modem, etc...
  If Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 0, 1)) = #True And Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 2, 1)) = #True
    ; ... that are actually in use and connected to an active network/internet/LAN etc... ... that are actually true interface that actually connect to network/internet/LAN etc...
    ;(InterfaceAndOperStatusFlags, 4, 1) = #False if actually connected to network media (e.g. actually in use and connected to an active network/internet/LAN etc...)
    ;(InterfaceAndOperStatusFlags, 7, 1) = #False If true interface that actually connects to a network
    If Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 4, 1)) = #False And Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 7, 1)) = #False
       
       Description$ = PeekS(@*pIfRowmac\Description, -1, #PB_Unicode)
       IfIndex$ = Str(*pIfRowmac\InterfaceIndex) ; the LUID network index
       ConnectionAlias$ = PeekS(@*pIfRowmac\Alias, -1, #PB_Unicode)
      ; perm MAC address is the one burned into the NIC and does not change
      macPermPhysicalAddress$=""
      For a=0 To *pIfRowmac\PhysicalAddressLength-1
        If a
          macPermPhysicalAddress$+":"
        EndIf
        byte.b=PeekB(@*pIfRowmac\PermanentPhysicalAddress+a)
        If byte>=0
          macPermPhysicalAddress$+RSet(Hex(byte),2,"0")
        Else
          macPermPhysicalAddress$+RSet(Hex(byte+256),2,"0")
        EndIf
      Next
      
      ; physical MAC address is the one that can be set or changed and will be the one in actual use
      ; overrides perm MAC address use and causes IP address change upon reboot in (most) DHCP assigned systems
      ; if same as perm MAC address then either set the same or not set/changed
      ; set in registry at > [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\(registry index key)]
      ; then set its value to the new MAC address e.g. "NetworkAddress"="001B21A22B0A" , without any spaces or other characters such as ":"
      macPhysicalAddress$=""
      For a=0 To *pIfRowmac\PhysicalAddressLength -1
        If a
          macPhysicalAddress$+":"
        EndIf
        byte.b=PeekB(@*pIfRowmac\PhysicalAddress+a)
        If byte>=0
          macPhysicalAddress$+RSet(Hex(byte),2,"0")
        Else
          macPhysicalAddress$+RSet(Hex(byte+256),2,"0")
        EndIf
      Next
      
      If macPermPhysicalAddress$ = ""
        macPermPhysicalAddress$ = " None"
      Else
        macPermPhysicalAddress$ = macPermPhysicalAddress$
      EndIf
      
      If macPhysicalAddress$ = ""
        macPhysicalAddress$ = " None"
      Else
        macPhysicalAddress$ = macPhysicalAddress$
      EndIf
      
      TxLinkSpdSet$ = Str(PeekQ(@*pIfRowmac\TransmitLinkSpeed)) + " bits per second"
      RxLinkSpdSet$ = Str(PeekQ(@*pIfRowmac\ReceiveLinkSpeed)) + " bits per second"
      
      Select *pIfRowmac\DirectionType
        Case #NET_IF_DIRECTION_SENDRECEIVE
          directiontyp$ = "Send and Receive" 
        Case #NET_IF_DIRECTION_SENDONLY
          directiontyp$ = "Send only" 
        Case #NET_IF_DIRECTION_RECEIVEONLY
          directiontyp$ = "Receive only" 
        Default
          directiontyp$ = "Direction is unspecific, unknown, or not defined. If you see this you should check for issues or updates."
      EndSelect
      
      Select *pIfRowmac\AdminStatus
        Case #MIB_IF_ADMIN_STATUS_UP
          adminstat$ = "Interface is initialized and enabled but not necessarily ready to transmit/receive data because it depends on the operational use status."
        Case #MIB_IF_ADMIN_STATUS_DOWN
          adminstat$ = "Interface is down and cannot be used to transmit or receive network data. "
        Case #MIB_IF_ADMIN_STATUS_TESTING
          adminstat$ = "Interface is in a test mode and no network data can be transmitted or received."
        Default
          adminstat$ = "Interface Admin status is unknown"
      EndSelect
      
      Select *pIfRowmac\MediaConnectState
        Case #MediaConnectStateUnknown
          ConnectorPlugged$ = "Connector status is unknown."
        Case #MediaConnectStateConnected
          ConnectorPlugged$ = "Connector is plugged in."
        Case #MediaConnectStateDisconnected
          ConnectorPlugged$ = "Connector is not plugged in. (On wireless connections may indicate not connected to a network.)"
        Default
          ConnectorPlugged$ = "Unable to detect Connector status or presence."
      EndSelect
      
      Select *pIfRowmac\OperStatus
        Case #IfOperStatusUp ;= 1
          oper_status$ = "Interface is up and able to pass packets."
        Case #IfOperStatusDown ;= 2
          oper_status$ = "Interface is down and not in a condition to pass packets."
        Case #IfOperStatusTesting ;= 3
          oper_status$ = "Interface is in testing mode."
        Case #IfOperStatusUnknown ;= 4
          oper_status$ = "Interface operational status is unknown."
        Case #IfOperStatusDormant ;= 5
          oper_status$ = "Interface is not up, but is in a pending state waiting for some external event"
        Case #IfOperStatusNotPresent ;= 6
          oper_status$ = "Interface is down specifically because some component is not present in the managed system."
        Case #IfOperStatusLowerLayerDown ;= 7
          oper_status$ = "Interface runs on top of one or more other interfaces and this interface is down because one or more lower-layer interfaces are down"
        Default
          oper_status$ = "Interface operational status is unknown, or interface is in a fault or malfunction condition."
      EndSelect
      
      ; small mention blurp in rfc2863 - http://www.ietf.org/rfc/rfc2863.txt - but applicapable to hardware interface adapteres only
       If Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 0, 1)) = 1
         If *pIfRowmac\AdminStatus = 1 And *pIfRowmac\OperStatus <> 1
           int_adapFault$ + "The interface is possibly in a fault condition."
         Else
           int_adapFault$ = "The interface does not appear to be in a fault condition."
         EndIf
       EndIf
       
       If Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 0, 1)) = 1 
         MIBhardwareinterface$ = "Interface is a hardware interface"
       Else
         MIBhardwareinterface$ = "Interface is a software/logical construct. "
       EndIf
       
       If Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 1, 1)) = 1 
         MIBfilterinterface$ = "Interface is a Filter Interface"
       Else
         MIBfilterinterface$ = "Interface is not a Filter Interface"
       EndIf
       
       If Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 2, 1)) = 1 
         MIBconnectorpresent$ = "Interface has a connector present"
       Else
         MIBconnectorpresent$ = "Interface does not have a connector present"
       EndIf
       
       If Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 3, 1)) = 1
         MIBportauthenticated$ = "Interface port is not authenticated. If this is a wireless device this may indicate not connected to network."
       Else
         MIBportauthenticated$ = "Interface port is authenticated."
       EndIf
       
       If Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 4, 1)) = 1
         MIBmediaconnected$ = "Interface is not media connected."
       Else
         MIBmediaconnected$ = "Interface is media connected."
       EndIf
       
       If Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 5, 1)) = 1
         MIBintfacepaused$ = "Interface is paused."
       Else
         MIBintfacepaused$ = "Interface is not paused."
       EndIf
       
       If Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 6, 1)) = 1
         MIBintfacelowpower$ = "Interface is at low power."
       Else
         MIBintfacelowpower$ = "Interface is not at low power."
       EndIf
       
       If Val(ReadMIBRowBitField(*pIfRowmac\InterfaceAndOperStatusFlags, 7, 1)) = 1
         MIBifendpoint$ = "Interface is an End Point device (for example, a smart phone) and not a true interface that connects to a network."
       Else
         MIBifendpoint$ = "Interface is not an End Point device (for example, a smart phone) and is a true interface that connects to a network."
       EndIf
            
      If ConnectionAlias$ = ""
        ConnectionAlias$ = "  No Alias Assigned" ; because its possible to have a NIC installed and active but not in use and assigned to a connection
      Else
        ConnectionAlias$ = ConnectionAlias$
      EndIf
      
      If Description$ = ""
        Description$ = "  No Description/Name Assigned" ; because its possible to have a NIC installed and active but the NIC not have a name/description (Windows tracks via GUID and index anyway)
      Else
        Description$ = Description$
      EndIf
      MacAddr$ = IfIndex$ + "\" + Description$ + "\" + ConnectionAlias$ + "\" + macPermPhysicalAddress$ + "\" + macPhysicalAddress$
    EndIf
  EndIf
  
  *BufInterfaceGuid = AllocateMemory(80)
  StringFromGUID2(@*pIfRowmac\InterfaceGuid, *BufInterfaceGuid, 80)
  Ifguid$ = PeekS(*BufInterfaceGuid, -1, #PB_Unicode)
  FreeMemory(*BufInterfaceGuid)
  
  If retval = #NO_ERROR And *pIfRowmac > 0
    FreeMemory(*pIfRowmac)
  EndIf
  
  Select rettype
    Case 0
      getmac_return$ = MacAddr$
    Case 1
      getmac_return$ = macPermPhysicalAddress$
    Case 2
      getmac_return$ = macPhysicalAddress$
    Case 3
      getmac_return$ = ConnectionAlias$
    Case 4
      getmac_return$ = Description$
    Case 5
      getmac_return$ = Ifguid$
    Case 6
      getmac_return$ = IfIndex$ ; this is the LUID index
    Case 7
      getmac_return$ = TxLinkSpdSet$
    Case 8
      getmac_return$ = RxLinkSpdSet$
    Case 9
      getmac_return$ = directiontyp$
    Case 10
      getmac_return$ = ConnectorPlugged$
    Case 11
      getmac_return$ = oper_status$
    Case 12
      getmac_return$ = adminstat$
    Case 13
      getmac_return$ = int_adapFault$
    Case 14
      getmac_return$ = MIBhardwareinterface$
    Case 15
      getmac_return$ = MIBfilterinterface$
    Case 16
      getmac_return$ = MIBconnectorpresent$
    Case 17
      getmac_return$ = MIBportauthenticated$
    Case 18
      getmac_return$ = MIBmediaconnected$
    Case 19
      getmac_return$ = MIBintfacepaused$
    Case 20
      getmac_return$ = MIBintfacelowpower$
    Case 21
      getmac_return$ = MIBifendpoint$
    Default
      getmac_return$ = MacAddr$
  EndSelect
  ProcedureReturn getmac_return$
  
EndProcedure

Procedure GetNumEntries() ; the number of entries total which includes both hardware and logical/software constructs
  *TableNum.MIB_IF_TABLE2 = #Null
  GetIfTable2(@*TableNum)
  If *TableNum\NumEntries >= 1
    NumEntries.i = *TableNum\NumEntries
    FreeMibTable(*TableNum)
  Else
    NumEntries.i = 0
    FreeMibTable(*TableNum)
  EndIf
  ProcedureReturn NumEntries  
EndProcedure

numentry = GetNumEntries()

For x = 1 To numentry
  If GetMACs(x) <> ""
    Debug "**************************************************************"
    Debug "Description : " + GetMACs(x, 4)
    Debug "Network GUID : " + GetMACs(x, 5)
    Debug "Connection Alias : " + GetMACs(x, 3)
    Debug "Is interface an end point device? : " + GetMACs(x, 21)
    Debug "Is this a hardware or software/logical construct interface? : " + GetMACs(x, 14)
    Debug "Is this a filter interface? : " + GetMACs(x, 15)
    Debug "Interface Authentication : " + GetMACs(x, 17)
    Debug "Is interface paused? : " + GetMACs(x, 19)
    Debug "Is interface at low power? : " + GetMACs(x, 20)
    Debug "Administrative Status : " + GetMACs(x, 12)
    Debug "Operational Status : " + GetMACs(x, 11)
    Debug "Fault Status : " + GetMACs(x, 13)
    Debug "Is connector present? : " + GetMACs(x, 16)
    Debug "Is connector plugged in? : " + GetMACs(x, 10)
    Debug "Is interface connected to media (e.g. network)? : " + GetMACs(x, 18)
    Debug "Direction : " + GetMACs(x, 9)
    Debug "Permanant MAC Address : " + GetMACs(x, 1)
    Debug "In use physical MAC Address : " + GetMACs(x, 2)
    Debug "Transmit Link Speed Setting : " + GetMACs(x, 7)
    Debug "Receive Link Speed Setting : " + GetMACs(x, 8)
    Debug "**************************************************************"
  EndIf
Next


CloseLibrary(1)
CloseLibrary(2)
CloseLibrary(3)
note: updated the original post and code per request