iphlpapi: Implement GetIpForwardTable() on top of nsi.

Signed-off-by: Huw Davies <huw@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Huw Davies 2021-08-02 09:19:06 +01:00 committed by Alexandre Julliard
parent 8f207fe7a8
commit 0515480d89
2 changed files with 145 additions and 76 deletions

View File

@ -2182,51 +2182,100 @@ DWORD WINAPI AllocateAndGetIpAddrTableFromStack( MIB_IPADDRTABLE **table, BOOL s
return err;
}
static int ipforward_row_cmp( const void *a, const void *b )
{
const MIB_IPFORWARDROW *rowA = a;
const MIB_IPFORWARDROW *rowB = b;
int ret;
if ((ret = rowA->dwForwardDest - rowB->dwForwardDest) != 0) return ret;
if ((ret = rowA->u2.dwForwardProto - rowB->u2.dwForwardProto) != 0) return ret;
if ((ret = rowA->dwForwardPolicy - rowB->dwForwardPolicy) != 0) return ret;
return rowA->dwForwardNextHop - rowB->dwForwardNextHop;
}
/******************************************************************
* GetIpForwardTable (IPHLPAPI.@)
*
* Get the route table.
*
* PARAMS
* pIpForwardTable [Out] buffer for route table
* pdwSize [In/Out] length of output buffer
* bOrder [In] whether to sort the table
* table [Out] buffer for route table
* size [In/Out] length of output buffer
* sort [In] whether to sort the table
*
* RETURNS
* Success: NO_ERROR
* Failure: error code from winerror.h
*
* NOTES
* If pdwSize is less than required, the function will return
* ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to the required byte
* size.
* If bOrder is true, the returned table will be sorted by the next hop and
* an assortment of arbitrary parameters.
*/
DWORD WINAPI GetIpForwardTable(PMIB_IPFORWARDTABLE pIpForwardTable, PULONG pdwSize, BOOL bOrder)
DWORD WINAPI GetIpForwardTable( MIB_IPFORWARDTABLE *table, ULONG *size, BOOL sort )
{
DWORD ret;
PMIB_IPFORWARDTABLE table;
DWORD err, count, uni_count, needed, i, addr;
struct nsi_ipv4_forward_key *keys;
struct nsi_ip_forward_rw *rw;
struct nsi_ipv4_forward_dynamic *dyn;
struct nsi_ip_forward_static *stat;
struct nsi_ipv4_unicast_key *uni_keys = NULL;
TRACE("pIpForwardTable %p, pdwSize %p, bOrder %d\n", pIpForwardTable, pdwSize, bOrder);
TRACE( "table %p, size %p, sort %d\n", table, size, sort );
if (!size) return ERROR_INVALID_PARAMETER;
if (!pdwSize) return ERROR_INVALID_PARAMETER;
err = NsiAllocateAndGetTable( 1, &NPI_MS_IPV4_MODULEID, NSI_IP_FORWARD_TABLE, (void **)&keys, sizeof(*keys),
(void **)&rw, sizeof(*rw), (void **)&dyn, sizeof(*dyn),
(void **)&stat, sizeof(*stat), &count, 0 );
if (err) return err;
ret = AllocateAndGetIpForwardTableFromStack(&table, bOrder, GetProcessHeap(), 0);
if (!ret) {
DWORD size = FIELD_OFFSET( MIB_IPFORWARDTABLE, table[table->dwNumEntries] );
if (!pIpForwardTable || *pdwSize < size) {
*pdwSize = size;
ret = ERROR_INSUFFICIENT_BUFFER;
needed = FIELD_OFFSET( MIB_IPFORWARDTABLE, table[count] );
if (!table || *size < needed)
{
*size = needed;
err = ERROR_INSUFFICIENT_BUFFER;
goto err;
}
else {
*pdwSize = size;
memcpy(pIpForwardTable, table, size);
err = NsiAllocateAndGetTable( 1, &NPI_MS_IPV4_MODULEID, NSI_IP_UNICAST_TABLE, (void **)&uni_keys, sizeof(*uni_keys),
NULL, 0, NULL, 0, NULL, 0, &uni_count, 0 );
if (err) goto err;
table->dwNumEntries = count;
for (i = 0; i < count; i++)
{
MIB_IPFORWARDROW *row = table->table + i;
row->dwForwardDest = keys[i].prefix.WS_s_addr;
ConvertLengthToIpv4Mask( keys[i].prefix_len, &row->dwForwardMask );
row->dwForwardPolicy = 0;
row->dwForwardNextHop = keys[i].next_hop.WS_s_addr;
row->u1.dwForwardType = row->dwForwardNextHop ? MIB_IPROUTE_TYPE_INDIRECT : MIB_IPROUTE_TYPE_DIRECT;
if (!row->dwForwardNextHop) /* find the interface's addr */
{
for (addr = 0; addr < uni_count; addr++)
{
if (uni_keys[addr].luid.Value == keys[i].luid.Value)
{
row->dwForwardNextHop = uni_keys[addr].addr.WS_s_addr;
break;
}
HeapFree(GetProcessHeap(), 0, table);
}
TRACE("returning %d\n", ret);
return ret;
}
row->dwForwardIfIndex = stat[i].if_index;
row->u2.dwForwardProto = rw[i].protocol;
row->dwForwardAge = dyn[i].age;
row->dwForwardNextHopAS = 0;
row->dwForwardMetric1 = rw[i].metric; /* FIXME: add interface metric */
row->dwForwardMetric2 = 0;
row->dwForwardMetric3 = 0;
row->dwForwardMetric4 = 0;
row->dwForwardMetric5 = 0;
}
if (sort) qsort( table->table, count, sizeof(MIB_IPFORWARDROW), ipforward_row_cmp );
err:
NsiFreeTable( uni_keys, NULL, NULL, NULL );
NsiFreeTable( keys, rw, dyn, stat );
return err;
}
static void forward_row2_fill( MIB_IPFORWARD_ROW2 *row, USHORT fam, void *key, struct nsi_ip_forward_rw *rw,

View File

@ -286,63 +286,83 @@ static void testGetIfTable(void)
static void testGetIpForwardTable(void)
{
DWORD apiReturn;
ULONG dwSize = 0;
DWORD err, i, j;
ULONG size = 0;
MIB_IPFORWARDTABLE *buf;
MIB_IPFORWARD_TABLE2 *table2;
MIB_UNICASTIPADDRESS_TABLE *unicast;
apiReturn = GetIpForwardTable(NULL, NULL, FALSE);
if (apiReturn == ERROR_NOT_SUPPORTED) {
skip("GetIpForwardTable is not supported\n");
return;
}
ok(apiReturn == ERROR_INVALID_PARAMETER,
"GetIpForwardTable(NULL, NULL, FALSE) returned %d, expected ERROR_INVALID_PARAMETER\n",
apiReturn);
apiReturn = GetIpForwardTable(NULL, &dwSize, FALSE);
ok(apiReturn == ERROR_INSUFFICIENT_BUFFER,
"GetIpForwardTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
apiReturn);
if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
PMIB_IPFORWARDTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
err = GetIpForwardTable( NULL, NULL, FALSE );
ok( err == ERROR_INVALID_PARAMETER, "got %d\n", err );
apiReturn = GetIpForwardTable(buf, &dwSize, FALSE);
ok(apiReturn == NO_ERROR,
"GetIpForwardTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n",
apiReturn);
err = GetIpForwardTable( NULL, &size, FALSE );
ok( err == ERROR_INSUFFICIENT_BUFFER, "got %d\n", err );
if (apiReturn == NO_ERROR)
{
DWORD i;
buf = malloc( size );
err = GetIpForwardTable( buf, &size, FALSE );
ok( !err, "got %d\n", err );
err = GetIpForwardTable2( AF_INET, &table2 );
ok( !err, "got %d\n", err );
ok( buf->dwNumEntries == table2->NumEntries, "got %d vs %d\n",
buf->dwNumEntries, table2->NumEntries );
err = GetUnicastIpAddressTable( AF_INET, &unicast );
ok( !err, "got %d\n", err );
trace( "IP forward table: %u entries\n", buf->dwNumEntries );
for (i = 0; i < buf->dwNumEntries; i++)
{
if (!U1(buf->table[i]).dwForwardDest) /* Default route */
{
todo_wine
ok (U1(buf->table[i]).dwForwardProto == MIB_IPPROTO_NETMGMT,
"Unexpected dwForwardProto %d\n", U1(buf->table[i]).dwForwardProto);
ok (U1(buf->table[i]).dwForwardType == MIB_IPROUTE_TYPE_INDIRECT,
"Unexpected dwForwardType %d\n", U1(buf->table[i]).dwForwardType);
}
else
{
/* In general we should get MIB_IPPROTO_LOCAL but does not work
* for Vista, 2008 and 7. */
ok (U1(buf->table[i]).dwForwardProto == MIB_IPPROTO_LOCAL ||
broken(U1(buf->table[i]).dwForwardProto == MIB_IPPROTO_NETMGMT),
"Unexpected dwForwardProto %d\n", U1(buf->table[i]).dwForwardProto);
/* The forward type varies depending on the address and gateway
* value so it is not worth testing in this case. */
}
MIB_IPFORWARDROW *row = buf->table + i;
MIB_IPFORWARD_ROW2 *row2 = table2->Table + i;
DWORD mask, next_hop;
trace( "%u: dest %s mask %s gw %s if %u type %u proto %u\n", i,
ntoa( buf->table[i].dwForwardDest ), ntoa( buf->table[i].dwForwardMask ),
ntoa( buf->table[i].dwForwardNextHop ), buf->table[i].dwForwardIfIndex,
U1(buf->table[i]).dwForwardType, U1(buf->table[i]).dwForwardProto );
winetest_push_context( "%d", i );
trace( "dest %s mask %s gw %s if %u type %u proto %u\n",
ntoa( row->dwForwardDest ), ntoa( row->dwForwardMask ),
ntoa( row->dwForwardNextHop ), row->dwForwardIfIndex,
row->dwForwardType, row->dwForwardProto );
ok( row->dwForwardDest == row2->DestinationPrefix.Prefix.Ipv4.sin_addr.s_addr,
"got %08x vs %08x\n", row->dwForwardDest, row2->DestinationPrefix.Prefix.Ipv4.sin_addr.s_addr );
ConvertLengthToIpv4Mask( row2->DestinationPrefix.PrefixLength, &mask );
ok( row->dwForwardMask == mask, "got %08x vs %08x\n", row->dwForwardMask, mask );
ok( row->dwForwardPolicy == 0, "got %d\n", row->dwForwardPolicy );
next_hop = row2->NextHop.Ipv4.sin_addr.s_addr;
if (!next_hop) /* for direct addresses, dwForwardNextHop is set to the address of the appropriate interface */
{
for (j = 0; j < unicast->NumEntries; j++)
{
if (unicast->Table[j].InterfaceLuid.Value == row2->InterfaceLuid.Value)
{
next_hop = unicast->Table[j].Address.Ipv4.sin_addr.s_addr;
break;
}
}
HeapFree(GetProcessHeap(), 0, buf);
}
ok( row->dwForwardNextHop == next_hop, "got %08x vs %08x\n", row->dwForwardNextHop, next_hop );
ok( row->dwForwardIfIndex == row2->InterfaceIndex, "got %d vs %d\n", row->dwForwardIfIndex, row2->InterfaceIndex );
if (!row2->NextHop.Ipv4.sin_addr.s_addr)
ok( buf->table[i].dwForwardType == MIB_IPROUTE_TYPE_DIRECT, "got %d\n", buf->table[i].dwForwardType );
else
ok( buf->table[i].dwForwardType == MIB_IPROUTE_TYPE_INDIRECT, "got %d\n", buf->table[i].dwForwardType );
ok( row->dwForwardProto == row2->Protocol, "got %d vs %d\n", row->dwForwardProto, row2->Protocol );
ok( row->dwForwardAge == row2->Age, "got %d vs %d\n", row->dwForwardAge, row2->Age );
ok( row->dwForwardNextHopAS == 0, "got %08x\n", row->dwForwardNextHopAS );
/* FIXME: need to add the interface's metric from GetIpInterfaceTable() */
ok( row->dwForwardMetric1 >= row2->Metric, "got %d vs %d\n", row->dwForwardMetric1, row2->Metric );
ok( row->dwForwardMetric2 == 0, "got %d\n", row->dwForwardMetric2 );
ok( row->dwForwardMetric3 == 0, "got %d\n", row->dwForwardMetric3 );
ok( row->dwForwardMetric4 == 0, "got %d\n", row->dwForwardMetric4 );
ok( row->dwForwardMetric5 == 0, "got %d\n", row->dwForwardMetric5 );
winetest_pop_context();
}
FreeMibTable( unicast );
FreeMibTable( table2 );
free( buf );
}
static void testGetIpNetTable(void)