我们可以通过调用系统API函数去获取机器上所有网卡的信息,可以获取到网卡上配置的IP、网关及DNS等信息。调用系统API可以获取最佳路由网卡,可以遍历系统路由表中的条目,可以看到默认路由和添加的策略路由。
主要是通过调用系统API函数GetAdaptersInfo来获取网卡上配置的IP、网关、DNS等信息,相关代码如下:
// 获取所有网卡信息
void GetNetAdaptersInfo
{
// IP路由表
ULONG ulOutBufLen = NULL;
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
GetIpForwardTable( pIpForwardTable, &ulOutBufLen, TRUE );
pIpForwardTable = (PMIB_IPFORWARDTABLE)malloc( ulOutBufLen );
if ( NULL != pIpForwardTable )
{
// 找到最佳路由,读出对应的IP索引
if ( NO_ERROR == GetIpForwardTable( pIpForwardTable, &ulOutBufLen, FALSE ) )
{
}
}
DWORD dwRetVal = 0;
PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *) malloc( sizeof(IP_ADAPTER_INFO) );
unsigned long ulOutBufLen = sizeof(IP_ADAPTER_INFO);
// 试探以下buffer长度够不够
if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) != ERROR_SUCCESS)
{
free(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);
}
PIP_ADAPTER_INFO pAdapter = NULL;
if ((dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
{
int nIndex = 0;
pAdapter = pAdapterInfo;
while (pAdapter != NULL )
{
// 1、读出网卡名称
CString szAdapter;
szAdapter.Format( _T("第%d块网卡:"), nIndex + 1);
szAdapter += pAdapter->Description;
// 2、网卡上所有IP信息
IP_ADDR_STRING *pIPStr = &(pAdapter->IpAddressList);
for( ; pIPStr != NULL; )
{
szAdapter += _T("\r\n");
s8* byIpAddr = pIPStr->IpAddress.String;
szAdapter += _T("IP: ");
szAdapter += byIpAddr;
szAdapter += _T(" ");
szAdapter += STRING_MASK;
s8 *byMaskAddr = pIPStr->IpMask.String;
szAdapter += byMaskAddr;
// 寻找IP对应的跳数
for ( u32 dw = 0; dw < pIpForwardTable->dwNumEntries; dw++ )
{
CString szRoute;
IN_ADDR inDest;
inDest.S_un.S_addr = pIpForwardTable->table[dw].dwForwardDest;
if ( inet_ntoa( ntohl(inDest.S_un.S_addr) ) == (CString)byIpAddr )
{
CString szMetric;
szMetric = _T(" ");
CString strTemp;
strTemp.Format( _T("跳数:"), pIpForwardTable->table[dw].dwForwardMetric1 );
szMetric += strTemp;
szAdapter += szMetric;
}
}
pIPStr = pIPStr->Next;
}
// 3、网卡上的网关信息
IP_ADDR_STRING *pGatewayStr = &(pAdapter->GatewayList);
for( ; pGatewayStr != NULL; )
{
szAdapter +=_T( " " );
s8* byGwAddr = pGatewayStr->IpAddress.String;
szAdapter += STRING_GATEWAY;
szAdapter += byGwAddr;
pGatewayStr = pGatewayStr->Next;
}
// 4、网卡上的DNS信息:
TCHAR achDnsInfo[MAX_PATH] = {0};
CString szSubKey = _T("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\");
szSubKey += CopyUtf8ToCStringT( pAdapter->AdapterName );
HKEY hKey = NULL;
LONG lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, (LPCTSTR)szSubKey,
0, KEY_READ, &hKey );
if ( lRet == ERROR_SUCCESS )
{
DWORD dwSize = MAX_PATH;
DWORD dwType = REG_SZ;
lRet = RegQueryValueEx( hKey, _T("NameServer"), NULL, &dwType,
(LPBYTE)achDnsInfo, &dwSize );
CString szDns;
CString strDnsInfo = achDnsInfo;
if ( _tcscmp( strDnsInfo, _T("") ) != 0 )
{
szDns.Format( _T("DNS服务器: %s"), strDnsInfo );
}
szAdapter += szDns;
RegCloseKey( hKey );
}
pAdapter = pAdapter->Next;
}
}
if ( pIpForwardTable != NULL )
{
free( pIpForwardTable );
}
if ( pAdapterInfo != NULL )
{
free( pAdapterInfo );
}
}
调用系统API函数GetBestInterface,传入要访问的目标IP,如下所示:
// 获取最佳路由对应的网卡索引号
DWORD dwResult = GetBestInterface(inet_addr(pDestIp), &dwBestIndex);
GetBestInterface函数返回后,返回的dwBestIndex值,就是最佳路由网卡的序号。
获取最佳路由网卡,一般是用在多网卡的机器上,比如一张是连外网的网卡,一张是连局域网的网卡。在Windows系统中,不管插有多少张网卡,只能设置一个默认网关,即只能在一个网卡上设置网关,其他网卡不能设置网关。没设置网关的,可以通过添加策略路由去解决路由问题。
有一点需要注意的是,系统选择的最佳路由可能是有问题的,比如我访问一个内网的地址,结果系统选择走外网的网卡,这就需要我们人为地去干预了。
在Windows系统中,可以在cmd中输入route print命令查看系统的路由表,如下所示:
我们可以通过调用系统API函数GetIpForwardTable去遍历路由表中的条目。比如如下的代码,代码中通过访问的目标地址,到路由表中找一个对应的路由:
// 传入要访问的目标IP,在路由表条目中找到最佳路由
BOOL FindBestRouteEntry( DWORD dwDestIp)
{
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
DWORD dwActualSize = 0;
DWORD dwRst = NO_ERROR;
// 获取系统路由表
dwRst = ::GetIpForwardTable( pIpForwardTable, &dwActualSize, TRUE );
if( NO_ERROR != dwRst)
{
if ( ERROR_INSUFFICIENT_BUFFER == dwRst)
{
pIpForwardTable = (PMIB_IPFORWARDTABLE)malloc(dwActualSize);
if (NO_ERROR != GetIpForwardTable( pIpForwardTable, &dwActualSize, TRUE))
{
free(pIpForwardTable);
return FALSE;
}
}
else
{
return FALSE;
}
}
else
{
assert(FALSE);
}
// 遍历系统路由表条目,根据目标地址确定使用哪条路由,然后获取该条路由
// 对应的网关
for(DWORD i = 0; i < pIpForwardTable->dwNumEntries; i++)
{
DWORD dwForwardDest = pIpForwardTable->table[i].dwForwardDest;
DWORD dwForwardMask = pIpForwardTable->table[i].dwForwardMask;
DWORD dwForwardNextHop = pIpForwardTable->table[i].dwForwardNextHop;
// 将0.0.0.0这条默认路由过滤掉
if ( 0 == dwForwardMask )
{
continue;
}
// 判断目标IP地址与路由条目中的IP和子网掩码是否在同一子网中
// 在一个子网中,则使用该路由条目
if ( ( dwForwardDest & dwForwardMask ) == ( dwDestIp & dwForwardMask ) )
{
dwDefaultGate = dwForwardNextHop;
free(pIpForwardTable);
return TRUE;
}
}
free(pIpForwardTable);
return FALSE;
}
对于人为添加的策略路由,也大概是通过上述代码的方法找到对应的策略路由的。