项目开发中有时候需要在Filter驱动中获取有效地Unicast地址,比如用来发送数据等。本来以为内核中需要OID那种强求来完成,结果发现OID_GEN_NETWORK_LAYER_ADDRESSES是不支持查询的。后来求助高人才发现:原来在Ndis6.x,IPHelper
API不仅可以在应用层使用,也可以在内核层使用,这里我们使用的函数是GetUnicastIpAddressTable :
NETIOAPI_API GetUnicastIpAddressTable( __in ADDRESS_FAMILY Family, __out PMIB_UNICASTIPADDRESS_TABLE *Table );
这里Family可以设置AF_INET, AF_INET6, and AF_UNSPEC。比如AF_INET是只包含ipv4的地址,具体参考文档。
查询的结果保存在MIB_UNICASTIPADDRESS_TABLE 数据结构中:
typedef struct _MIB_UNICASTIPADDRESS_TABLE { ULONG NumEntries; MIB_UNICASTIPADDRESS_ROW Table[ANY_SIZE]; } MIB_UNICASTIPADDRESS_TABLE, *PMIB_UNICASTIPADDRESS_TABLE;
NumEntries表示地址项的数目,具体信息保存在MIB_UNICASTIPADDRESS_ROW :
typedef struct _MIB_UNICASTIPADDRESS_ROW { SOCKADDR_INET Address; NET_LUID InterfaceLuid; NET_IFINDEX InterfaceIndex; NL_PREFIX_ORIGIN PrefixOrigin; NL_SUFFIX_ORIGIN SuffixOrigin; ULONG ValidLifetime; ULONG PreferredLifetime; UINT8 OnLinkPrefixLength; BOOLEAN SkipAsSource; NL_DAD_STATE DadState; SCOPE_ID ScopeId; LARGE_INTEGER CreationTimeStamp; } MIB_UNICASTIPADDRESS_ROW, *PMIB_UNICASTIPADDRESS_ROW;
我们要的地址保存在Address中:
typedef union _SOCKADDR_INET { SOCKADDR_IN Ipv4; SOCKADDR_IN6 Ipv6; ADDRESS_FAMILY si_family; } SOCKADDR_INET, *PSOCKADDR_INET;
接下来我们看看SOCKDDR_IN的结构:
struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; };
最后的地址保存在结构in_addr中,它是一个包含多个联合Union的结构:
typedef struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; } IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
这个就是你要的地址了。如果你使用的应用层程序,那么可以导入Winsock2.h,然后使用inet_ntoa()函数把 sin_addr 转换为字符信息。但是在内核,估计你只能自己逐
个数据读取了。
ADDRESS_FAMILY Family; PMIB_UNICASTIPADDRESS_TABLE Table = NULL; NETIOAPI_API NetIoApi;//没法使用NO_ERROR SOCKADDR_INET sockaddr_inet; SOCKADDR_IN Ipv4; Family = AF_INET; GetUnicastIpAddressTable(Family, (PMIB_UNICASTIPADDRESS_TABLE *)&(Table)); //本来需要加上判断比如 NetIoApi = GetUnicastIpAddressTable(...)因为NO_ERROR没法识别~ sockaddr_inet = Table->Table[iCount].Address; Ipv4 = sockaddr_inet.Ipv4; DEBUGP(DL_TEST,("The %dth address is %d.%d.%d.%d",iCount,Ipv4.sin_addr.S_un.S_un_b.s_b1,Ipv4.sin_addr.S_un.S_un_b.s_b2,Ipv4.sin_addr.S_un.S_un_b.s_b3,Ipv4.sin_addr.S_un.S_un_b.s_b4)); }
----------------------------------------------------------------------------------------------------------------------------
要使用GetUnicastIpAddressTable这个函数,要导入头文件:Netioapi.h文件,而且注意导入的顺序,必须是ndis.h之后。例如:
#include <ndis.h>
#include <filteruser.h>
#include "flt_dbg.h"
#include "filter.h"
#include <Netioapi.h>
其次要通过编译还要在source文件中TARGETLIBS添加如下:
$(DDK_LIB_PATH)\netio.lib
------------------------------------ ----------------------------------------------------------------------------------------
最后的效果就是:
最后非常感谢 Thomas 在 Filter driver:get ip address 的热心帮助。