You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4363 lines
163 KiB
4363 lines
163 KiB
/*
|
|
* File: print.c
|
|
* Description: This file contains the implementation of the print
|
|
* utilities for the NLB KD extensions.
|
|
* Author: Created by shouse, 1.4.01
|
|
*/
|
|
|
|
#include "nlbkd.h"
|
|
#include "utils.h"
|
|
#include "print.h"
|
|
#include "packet.h"
|
|
#include "load.h"
|
|
|
|
/*
|
|
* Function: PrintUsage
|
|
* Description: Prints usage information for the specified context.
|
|
* Author: Created by shouse, 1.5.01
|
|
*/
|
|
void PrintUsage (ULONG dwContext) {
|
|
|
|
/* Display the appropriate help. */
|
|
switch (dwContext) {
|
|
case USAGE_ADAPTERS:
|
|
dprintf("Usage: nlbadapters [verbosity]\n");
|
|
dprintf(" [verbosity]: 0 (LOW) Prints minimal detail for adapters in use (default)\n");
|
|
dprintf(" 1 (MEDIUM) Prints adapter state for adapters in use\n");
|
|
dprintf(" 2 (HIGH) Prints adapter state for ALL NLB adapter blocks\n");
|
|
break;
|
|
case USAGE_ADAPTER:
|
|
dprintf("Usage: nlbadapter <pointer to adapter block> [verbosity]\n");
|
|
dprintf(" [verbosity]: 0 (LOW) Prints minimal detail for the specified adapter\n");
|
|
dprintf(" 1 (MEDIUM) Prints adapter state for the specified adapter (default)\n");
|
|
dprintf(" 2 (HIGH) Recurses into NLB context with LOW verbosity\n");
|
|
break;
|
|
case USAGE_CONTEXT:
|
|
dprintf("Usage: nlbctxt <pointer to context block> [verbosity]\n");
|
|
dprintf(" [verbosity]: 0 (LOW) Prints fundamental NLB configuration and state (default)\n");
|
|
dprintf(" 1 (MEDIUM) Prints resource state and packet statistics\n");
|
|
dprintf(" 2 (HIGH) Recurses into parameters and load with LOW verbosity\n");
|
|
break;
|
|
case USAGE_LOAD:
|
|
dprintf("Usage: nlbload <pointer to load block> [verbosity]\n");
|
|
dprintf(" [verbosity]: 0 (LOW) Prints fundamental load state and configuration\n");
|
|
dprintf(" 1 (MEDIUM) Prints the state of all port rules and bins\n");
|
|
dprintf(" 2 (HIGH) Prints the NLB heartbeat information\n");
|
|
break;
|
|
case USAGE_PARAMS:
|
|
dprintf("Usage: nlbparams <pointer to params block> [verbosity]\n");
|
|
dprintf(" [verbosity]: 0 (LOW) Prints fundamental NLB configuration parameters (default)\n");
|
|
dprintf(" 1 (MEDIUM) Prints all configured port rules\n");
|
|
dprintf(" 2 (HIGH) Prints extra miscellaneous configuration\n");
|
|
break;
|
|
case USAGE_RESP:
|
|
dprintf("Usage: nlbresp <pointer to packet> [direction]\n");
|
|
dprintf(" [direction]: 0 (RECEIVE) Packet is on the receive path (default)\n");
|
|
dprintf(" 1 (SEND) Packet is on the send path\n");
|
|
break;
|
|
case USAGE_PKT:
|
|
dprintf("Usage: nlbpkt <Packet> [RC Port]\n");
|
|
dprintf(" [RC port]: Remote control port assuming the packet is a remote control packet\n");
|
|
break;
|
|
case USAGE_ETHER:
|
|
dprintf("Usage: nlbether <Ether Frame> [RC Port]\n");
|
|
dprintf(" [RC port]: Remote control port assuming the packet is a remote control packet\n");
|
|
break;
|
|
case USAGE_IP:
|
|
dprintf("Usage: nlbip <IP Packet> [RC Port]\n");
|
|
dprintf(" [RC port]: Remote control port assuming the packet is a remote control packet\n");
|
|
break;
|
|
case USAGE_TEAMS:
|
|
dprintf("Usage: nlbteams\n");
|
|
break;
|
|
case USAGE_HOOKS:
|
|
dprintf("Usage: nlbhooks\n");
|
|
break;
|
|
case USAGE_MAC:
|
|
dprintf("Usage: nlbmac <pointer to context block>\n");
|
|
break;
|
|
case USAGE_DSCR:
|
|
dprintf("Usage: nlbdscr <pointer to connection descriptor>\n");
|
|
break;
|
|
case USAGE_CONNQ:
|
|
dprintf("Usage: nlbconnq <pointer to queue>[index] [max entries]\n");
|
|
dprintf(" [max entries]: Maximum number of entries to print (default is 10)\n");
|
|
dprintf(" [index]: If queue pointer points to an array of queues, this is the index of the\n");
|
|
dprintf(" queue to be traversed, provided in [index], {index} or (index) form.\n");
|
|
break;
|
|
case USAGE_GLOBALQ:
|
|
dprintf("Usage: nlbglobalq <pointer to queue>[index] [max entries]\n");
|
|
dprintf(" [max entries]: Maximum number of entries to print (default is 10)\n");
|
|
dprintf(" [index]: If queue pointer points to an array of queues, this is the index of the\n");
|
|
dprintf(" queue to be traversed, provided in [index], {index} or (index) form.\n");
|
|
break;
|
|
case USAGE_FILTER:
|
|
dprintf("Usage: nlbfilter <pointer to context block> <protocol> <client IP>[:<client port>] <server IP>[:<server port>] [flags]\n");
|
|
dprintf(" <protocol>: TCP, PPTP, GRE, UDP, IPSec or ICMP\n");
|
|
dprintf(" [flags]: One of SYN, FIN or RST (default is DATA)\n");
|
|
dprintf("\n");
|
|
dprintf(" IP addresses can be in dotted notation or network byte order DWORDs.\n");
|
|
dprintf(" I.e., 169.128.0.101 = 0x650080a9 (in x86 memory = A9 80 00 65)\n");
|
|
break;
|
|
case USAGE_HASH:
|
|
dprintf("Usage: nlbhash <pointer to context block> <pointer to packet>\n");
|
|
break;
|
|
default:
|
|
dprintf("No usage information available.\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintAdapter
|
|
* Description: Prints the contents of the MAIN_ADAPTER structure at the specified verbosity.
|
|
* LOW (0) prints only the adapter address and device name.
|
|
* MEDIUM (1) additionally prints the status flags (init, bound, annouce, etc.).
|
|
* HIGH (2) recurses into the context structure and prints it at MEDIUM verbosity.
|
|
* Author: Created by shouse, 1.5.01
|
|
*/
|
|
void PrintAdapter (ULONG64 pAdapter, ULONG dwVerbosity) {
|
|
WCHAR szString[256];
|
|
ULONG dwValue;
|
|
UCHAR cValue;
|
|
ULONG64 pAddr = 0;
|
|
ULONG64 pContext = 0;
|
|
ULONG64 pOpen;
|
|
ULONG64 pMiniport;
|
|
ULONG64 pName;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pAdapter) {
|
|
dprintf("Error: NLB adapter block is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
dprintf("NLB Adapter Block 0x%p\n", pAdapter);
|
|
|
|
/* Get the MAIN_ADAPTER_CODE from the structure to make sure that this address
|
|
indeed points to a valid NLB adapter block. */
|
|
GetFieldValue(pAdapter, MAIN_ADAPTER, MAIN_ADAPTER_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != MAIN_ADAPTER_CODE) {
|
|
dprintf(" Error: Invalid NLB adapter block. Wrong code found (0x%08x).\n", dwValue);
|
|
return;
|
|
}
|
|
|
|
/* Retrieve the used/unused state of the adapter. */
|
|
GetFieldValue(pAdapter, MAIN_ADAPTER, MAIN_ADAPTER_FIELD_USED, cValue);
|
|
|
|
if (!cValue)
|
|
dprintf(" This adapter is unused.\n");
|
|
else {
|
|
/* Get the offset of the NLB context pointer. */
|
|
if (GetFieldOffset(MAIN_ADAPTER, MAIN_ADAPTER_FIELD_CONTEXT, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", MAIN_ADAPTER_FIELD_CONTEXT, MAIN_ADAPTER);
|
|
else {
|
|
pAddr = pAdapter + dwValue;
|
|
|
|
/* Retrieve the pointer. */
|
|
pContext = GetPointerFromAddress(pAddr);
|
|
|
|
/* Get the MAC handle from the context block; this is a NDIS_OPEN_BLOCK pointer. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_MAC_HANDLE, pOpen);
|
|
|
|
/* Get the miniport handle from the open block; this is a NDIS_MINIPORT_BLOCK pointer. */
|
|
GetFieldValue(pOpen, NDIS_OPEN_BLOCK, NDIS_OPEN_BLOCK_FIELD_MINIPORT_HANDLE, pMiniport);
|
|
|
|
/* Get a pointer to the adapter name from the miniport block. */
|
|
GetFieldValue(pMiniport, NDIS_MINIPORT_BLOCK, NDIS_MINIPORT_BLOCK_FIELD_ADAPTER_NAME, pName);
|
|
|
|
/* Get the length of the unicode string. */
|
|
GetFieldValue(pName, UNICODE_STRING, UNICODE_STRING_FIELD_LENGTH, dwValue);
|
|
|
|
/* Get the maximum length of the unicode string. */
|
|
GetFieldValue(pName, UNICODE_STRING, UNICODE_STRING_FIELD_BUFFER, pAddr);
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, dwValue);
|
|
|
|
dprintf(" Physical device name: %ls\n", szString);
|
|
}
|
|
|
|
/* Get the pointer to and length of the device to which NLB is bound. */
|
|
GetFieldValue(pAdapter, MAIN_ADAPTER, MAIN_ADAPTER_FIELD_NAME_LENGTH, dwValue);
|
|
GetFieldValue(pAdapter, MAIN_ADAPTER, MAIN_ADAPTER_FIELD_NAME, pAddr);
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, dwValue);
|
|
|
|
dprintf(" Physical device GUID: %ls\n", szString);
|
|
}
|
|
|
|
/* Get the IP interface index. */
|
|
GetFieldValue(pAdapter, MAIN_ADAPTER, MAIN_ADAPTER_FIELD_IF_INDEX, dwValue);
|
|
|
|
dprintf(" IP interface index: %u\n", dwValue);
|
|
|
|
/* If we're printing at low verbosity, bail out here. */
|
|
if (dwVerbosity == VERBOSITY_LOW) goto end;
|
|
|
|
/* Get the IP interface index operation. */
|
|
GetFieldValue(pAdapter, MAIN_ADAPTER, MAIN_ADAPTER_FIELD_IF_INDEX_OPERATION, dwValue);
|
|
|
|
dprintf(" IP interface operation in progress: ");
|
|
|
|
switch (dwValue) {
|
|
case IF_INDEX_OPERATION_UPDATE:
|
|
dprintf("Deleting\n");
|
|
break;
|
|
case IF_INDEX_OPERATION_NONE:
|
|
dprintf("None\n");
|
|
break;
|
|
default:
|
|
dprintf("Unkonwn\n");
|
|
break;
|
|
}
|
|
|
|
/* Determine whether or not the adapter has been initialized. */
|
|
GetFieldValue(pAdapter, MAIN_ADAPTER, MAIN_ADAPTER_FIELD_INITED, cValue);
|
|
|
|
dprintf(" Context state initialized: %s\n", (cValue) ? "Yes" : "No");
|
|
|
|
/* Determine whether or not NLB has been bound to the stack yet. */
|
|
GetFieldValue(pAdapter, MAIN_ADAPTER, MAIN_ADAPTER_FIELD_BOUND, cValue);
|
|
|
|
dprintf(" NLB bound to adapter: %s\n", (cValue) ? "Yes" : "No");
|
|
|
|
/* Determine whether or not TCP/IP has been bound to the NLB virtual adapter or not. */
|
|
GetFieldValue(pAdapter, MAIN_ADAPTER, MAIN_ADAPTER_FIELD_ANNOUNCED, cValue);
|
|
|
|
dprintf(" NLB miniport announced: %s\n", (cValue) ? "Yes" : "No");
|
|
|
|
end:
|
|
|
|
dprintf(" %sNLB context: 0x%p\n",
|
|
(pContext && (dwVerbosity == VERBOSITY_HIGH)) ? "-" : (pContext) ? "+" : " ", pContext);
|
|
|
|
/* If we're printing at medium verbosity, bail out here. */
|
|
if ((dwVerbosity == VERBOSITY_LOW) || (dwVerbosity == VERBOSITY_MEDIUM)) return;
|
|
|
|
/* Print the context information (always with LOW verbosity during recursion. */
|
|
if (pContext) {
|
|
dprintf("\n");
|
|
PrintContext(pContext, VERBOSITY_LOW);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintContext
|
|
* Description: Prints the contents of the MAIN_CTXT structure at the specified verbosity.
|
|
* LOW (0) prints fundamental NLB configuration and state.
|
|
* MEDIUM (1) additionally prints the resource state (pools, allocations, etc).
|
|
* HIGH (2) further prints other miscelaneous information.
|
|
* Author: Created by shouse, 1.5.01
|
|
*/
|
|
void PrintContext (ULONG64 pContext, ULONG dwVerbosity) {
|
|
WCHAR szNICName[CVY_MAX_VIRTUAL_NIC];
|
|
ULONGLONG dwwValue;
|
|
IN_ADDR dwIPAddr;
|
|
CHAR * szString;
|
|
UCHAR szMAC[6];
|
|
ULONG64 pAddr;
|
|
ULONG dwValue;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pContext) {
|
|
dprintf("Error: NLB context block is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
dprintf("NLB Context Block 0x%p\n", pContext);
|
|
|
|
/* Get the MAIN_CTXT_CODE from the structure to make sure that this address
|
|
indeed points to a valid NLB context block. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != MAIN_CTXT_CODE) {
|
|
dprintf(" Error: Invalid NLB context block. Wrong code found (0x%08x).\n", dwValue);
|
|
return;
|
|
}
|
|
|
|
/* Get the offset of the NLB virtual NIC name. */
|
|
if (GetFieldOffset(MAIN_CTXT, MAIN_CTXT_FIELD_VIRTUAL_NIC, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", MAIN_CTXT_FIELD_VIRTUAL_NIC, MAIN_CTXT);
|
|
else {
|
|
pAddr = pContext + dwValue;
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szNICName, CVY_MAX_VIRTUAL_NIC);
|
|
|
|
dprintf(" NLB virtual NIC name: %ls\n", szNICName);
|
|
}
|
|
|
|
/* Get the convoy enabled status. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_ENABLED, dwValue);
|
|
|
|
dprintf(" NLB enabled: %s ", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the draining status. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_DRAINING, dwValue);
|
|
|
|
if (dwValue) dprintf("(Draining) ");
|
|
|
|
/* Get the suspended status. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_SUSPENDED, dwValue);
|
|
|
|
if (dwValue) dprintf("(Suspended) ");
|
|
|
|
/* Get the stopping status. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_STOPPING, dwValue);
|
|
|
|
if (dwValue) dprintf("(Stopping) ");
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the adapter index. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_ADAPTER_ID, dwValue);
|
|
|
|
dprintf(" NLB adapter ID: %u\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the adapter medium. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_MEDIUM, dwValue);
|
|
|
|
dprintf(" Network medium: %s\n", (dwValue == NdisMedium802_3) ? "802.3" : "Invalid");
|
|
|
|
/* Get the media connect status. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_MEDIA_CONNECT, dwValue);
|
|
|
|
dprintf(" Network connect status: %s\n", (dwValue) ? "Connected" : "Disconnected");
|
|
|
|
/* Get the media connect status. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_FRAME_SIZE, dwValue);
|
|
|
|
dprintf(" Frame size (MTU): %u\n", dwValue);
|
|
|
|
/* Get the media connect status. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_MCAST_LIST_SIZE, dwValue);
|
|
|
|
dprintf(" Multicast MAC list size: %u\n", dwValue);
|
|
|
|
/* Determine dynamic MAC address support. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_MAC_OPTIONS, dwValue);
|
|
|
|
dprintf(" Dynamic MAC address support: %s\n",
|
|
(dwValue & NDIS_MAC_OPTION_SUPPORTS_MAC_ADDRESS_OVERWRITE) ? "Yes" : "No");
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" NDIS handles\n");
|
|
|
|
/* Get the NDIS bind handle. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_BIND_HANDLE, pAddr);
|
|
|
|
dprintf(" Bind handle: 0x%p\n", pAddr);
|
|
|
|
/* Get the NDIS unbind handle. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_UNBIND_HANDLE, pAddr);
|
|
|
|
dprintf(" Unbind handle: 0x%p\n", pAddr);
|
|
|
|
/* Get the NDIS MAC handle. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_MAC_HANDLE, pAddr);
|
|
|
|
dprintf(" MAC handle: 0x%p\n", pAddr);
|
|
|
|
/* Get the NDIS protocol handle. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_PROT_HANDLE, pAddr);
|
|
|
|
dprintf(" Protocol handle: 0x%p\n", pAddr);
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" Cluster IP settings\n");
|
|
|
|
/* Get the cluster IP address, which is a DWORD, and convert it to a string. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CL_IP_ADDR, dwValue);
|
|
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" IP address: %s\n", szString);
|
|
|
|
/* Get the cluster net mask, which is a DWORD, and convert it to a string. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CL_NET_MASK, dwValue);
|
|
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" Netmask: %s\n", szString);
|
|
|
|
/* Get the offset of the cluster MAC address and retrieve the MAC from that address. */
|
|
if (GetFieldOffset(MAIN_CTXT, MAIN_CTXT_FIELD_CL_MAC_ADDR, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", MAIN_CTXT_FIELD_CL_MAC_ADDR, MAIN_CTXT);
|
|
else {
|
|
pAddr = pContext + dwValue;
|
|
|
|
GetMAC(pAddr, szMAC, ETH_LENGTH_OF_ADDRESS);
|
|
|
|
dprintf(" MAC address: %02X-%02X-%02X-%02X-%02X-%02X\n",
|
|
((PUCHAR)(szMAC))[0], ((PUCHAR)(szMAC))[1], ((PUCHAR)(szMAC))[2],
|
|
((PUCHAR)(szMAC))[3], ((PUCHAR)(szMAC))[4], ((PUCHAR)(szMAC))[5]);
|
|
}
|
|
|
|
/* Get the cluster broadcast address, which is a DWORD, and convert it to a string. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CL_BROADCAST, dwValue);
|
|
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" Broadcast address: %s\n", szString);
|
|
|
|
/* Get the IGMP multicast IP address, which is a DWORD, and convert it to a string. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_IGMP_MCAST_IP, dwValue);
|
|
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" IGMP multicast IP address: %s\n", szString);
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" Dedicated IP settings\n");
|
|
|
|
/* Get the dedicated IP address, which is a DWORD, and convert it to a string. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_DED_IP_ADDR, dwValue);
|
|
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" IP address: %s\n", szString);
|
|
|
|
/* Get the dedicated net mask, which is a DWORD, and convert it to a string. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_DED_NET_MASK, dwValue);
|
|
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" Netmask: %s\n", szString);
|
|
|
|
/* Get the dedicated broadcast address, which is a DWORD, and convert it to a string. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_DED_BROADCAST, dwValue);
|
|
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" Broadcast address: %s\n", szString);
|
|
|
|
/* Get the offset of the dedicated MAC address and retrieve the MAC from that address. */
|
|
if (GetFieldOffset(MAIN_CTXT, MAIN_CTXT_FIELD_DED_MAC_ADDR, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", MAIN_CTXT_FIELD_DED_MAC_ADDR, MAIN_CTXT);
|
|
else {
|
|
pAddr = pContext + dwValue;
|
|
|
|
GetMAC(pAddr, szMAC, ETH_LENGTH_OF_ADDRESS);
|
|
|
|
dprintf(" MAC address: %02X-%02X-%02X-%02X-%02X-%02X\n",
|
|
((PUCHAR)(szMAC))[0], ((PUCHAR)(szMAC))[1], ((PUCHAR)(szMAC))[2],
|
|
((PUCHAR)(szMAC))[3], ((PUCHAR)(szMAC))[4], ((PUCHAR)(szMAC))[5]);
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the offset of the BDA teaming information for this context. */
|
|
if (GetFieldOffset(MAIN_CTXT, MAIN_CTXT_FIELD_BDA_TEAMING, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", MAIN_CTXT_FIELD_BDA_TEAMING, MAIN_CTXT);
|
|
else {
|
|
pAddr = pContext + dwValue;
|
|
|
|
/* Print the bi-directional affinity teaming state. */
|
|
PrintBDAMember(pAddr);
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" Cluster dedicated IP addresses\n");
|
|
|
|
/* Get the offset of the dedicated IP address list for this context. */
|
|
if (GetFieldOffset(MAIN_CTXT, MAIN_CTXT_FIELD_DIP_LIST, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", MAIN_CTXT_FIELD_DIP_LIST, MAIN_CTXT);
|
|
else {
|
|
pAddr = pContext + dwValue;
|
|
|
|
/* Print the known dedicated IP addresses of other cluster members. */
|
|
PrintDIPList(pAddr);
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the current heartbeat period. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_PING_TIMEOUT, dwValue);
|
|
|
|
dprintf(" Current heartbeat period: %u millisecond(s)\n", dwValue);
|
|
|
|
/* Get the current IGMP join counter. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_IGMP_TIMEOUT, dwValue);
|
|
|
|
dprintf(" Time since last IGMP join: %.1f second(s)\n", (float)(dwValue/1000.0));
|
|
|
|
/* Get the current descriptor purge counter. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_DSCR_PURGE_TIMEOUT, dwValue);
|
|
|
|
dprintf(" Time since last descriptor purge: %.1f second(s)\n", (float)(dwValue/1000.0));
|
|
|
|
/* Get the total number of connections purged. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_NUM_DSCRS_PURGED, dwValue);
|
|
|
|
dprintf(" Number of connections purged: %u\n", dwValue);
|
|
|
|
/* If we're printing at low verbosity, go to the end and print the load and params pointers. */
|
|
if (dwVerbosity == VERBOSITY_LOW) goto end;
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" Send packet pools\n");
|
|
|
|
/* Get the state of the send packet pool. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_EXHAUSTED, dwValue);
|
|
|
|
dprintf(" Pool exhausted: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the number of send packet pools allocated. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_SEND_POOLS_ALLOCATED, dwValue);
|
|
|
|
dprintf(" Pools allocated: %u\n", dwValue);
|
|
|
|
/* Get the number of send packets allocated. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_SEND_PACKETS_ALLOCATED, dwValue);
|
|
|
|
dprintf(" Packets allocated: %u\n", dwValue);
|
|
|
|
/* Get the current send packet pool. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_SEND_POOL_CURRENT, dwValue);
|
|
|
|
dprintf(" Current pool: %u\n", dwValue);
|
|
|
|
/* Get the number of pending send packets (outstanding). */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_SEND_OUTSTANDING, dwValue);
|
|
|
|
dprintf(" Packets outstanding: %u\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" Receive packet pools\n");
|
|
|
|
/* Get the receive "out of resoures" counter. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_RECV_NO_BUF, dwValue);
|
|
|
|
dprintf(" Allocation failures: %u\n", dwValue);
|
|
|
|
/* Get the number of receive packet pools allocated. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_RECV_POOLS_ALLOCATED, dwValue);
|
|
|
|
dprintf(" Pools allocated: %u\n", dwValue);
|
|
|
|
/* Get the number of receive packets allocated. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_RECV_PACKETS_ALLOCATED, dwValue);
|
|
|
|
dprintf(" Packets allocated: %u\n", dwValue);
|
|
|
|
/* Get the current receive packet pool. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_RECV_POOL_CURRENT, dwValue);
|
|
|
|
dprintf(" Current pool: %u\n", dwValue);
|
|
|
|
/* Get the number of pending receive packets (outstanding). */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_RECV_OUTSTANDING, dwValue);
|
|
|
|
dprintf(" Packets outstanding: %u\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" Ping/IGMP packet pool\n");
|
|
|
|
/* Get the receive "out of resoures" counter. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_PING_NO_BUF, dwValue);
|
|
|
|
dprintf(" Allocation failures: %u\n", dwValue);
|
|
|
|
/* Get the number of ping/igmp packets allocated. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_PING_PACKETS_ALLOCATED, dwValue);
|
|
|
|
dprintf(" Packets allocated: %u\n", dwValue);
|
|
|
|
/* Get the number of pending ping/igmp packets (outstanding). */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_PING_OUTSTANDING, dwValue);
|
|
|
|
dprintf(" Packets outstanding: %u\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" Receive buffer pools\n");
|
|
|
|
/* Get the number of receive buffer pools allocated. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_BUF_POOLS_ALLOCATED, dwValue);
|
|
|
|
dprintf(" Pools allocated: %u\n", dwValue);
|
|
|
|
/* Get the number of receive buffers allocated. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_BUFS_ALLOCATED, dwValue);
|
|
|
|
dprintf(" Buffers allocated: %u\n", dwValue);
|
|
|
|
/* Get the number of pending receive buffers (outstanding). */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_BUFS_OUTSTANDING, dwValue);
|
|
|
|
dprintf(" Buffers outstanding: %u\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" Sent Received\n");
|
|
dprintf(" Statistics ---------- ----------\n");
|
|
|
|
/* Get the number of successful sends. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_XMIT_OK, dwValue);
|
|
|
|
dprintf(" Successful: %10u", dwValue);
|
|
|
|
/* Get the number of successful receives. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_RECV_OK, dwValue);
|
|
|
|
dprintf(" %10u\n", dwValue);
|
|
|
|
/* Get the number of unsuccessful sends. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_XMIT_ERROR, dwValue);
|
|
|
|
dprintf(" Unsuccessful: %10u", dwValue);
|
|
|
|
/* Get the number of unsuccessful receives. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_RECV_ERROR, dwValue);
|
|
|
|
dprintf(" %10u\n", dwValue);
|
|
|
|
/* Get the number of directed frames transmitted. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_XMIT_FRAMES_DIR, dwValue);
|
|
|
|
dprintf(" Directed packets: %10u", dwValue);
|
|
/* Get the number of directed frames received. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_RECV_FRAMES_DIR, dwValue);
|
|
|
|
dprintf(" %10u\n", dwValue);
|
|
|
|
/* Get the number of directed bytes transmitted. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_XMIT_BYTES_DIR, dwwValue);
|
|
|
|
dprintf(" Directed bytes: %10u", dwwValue);
|
|
|
|
/* Get the number of directed bytes received. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_RECV_BYTES_DIR, dwwValue);
|
|
|
|
dprintf(" %10u\n", dwwValue);
|
|
|
|
/* Get the number of multicast frames transmitted. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_XMIT_FRAMES_MCAST, dwValue);
|
|
|
|
dprintf(" Multicast packets: %10u", dwValue);
|
|
|
|
/* Get the number of multicast frames received. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_RECV_FRAMES_MCAST, dwValue);
|
|
|
|
dprintf(" %10u\n", dwValue);
|
|
|
|
/* Get the number of multicast bytes transmitted. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_XMIT_BYTES_MCAST, dwwValue);
|
|
|
|
dprintf(" Multicast bytes: %10u", dwwValue);
|
|
|
|
/* Get the number of multicast bytes received. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_RECV_BYTES_MCAST, dwwValue);
|
|
|
|
dprintf(" %10u\n", dwwValue);
|
|
|
|
/* Get the number of broadcast frames transmitted. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_XMIT_FRAMES_BCAST, dwValue);
|
|
|
|
dprintf(" Broadcast packets: %10u", dwValue);
|
|
|
|
/* Get the number of broadcast frames received. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_RECV_FRAMES_BCAST, dwValue);
|
|
|
|
dprintf(" %10u\n", dwValue);
|
|
|
|
/* Get the number of broadcast bytes transmitted. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_XMIT_BYTES_BCAST, dwwValue);
|
|
|
|
dprintf(" Broadcast bytes: %10u", dwwValue);
|
|
|
|
/* Get the number of broadcast bytes received. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_RECV_BYTES_BCAST, dwwValue);
|
|
|
|
dprintf(" %10u\n", dwwValue);
|
|
|
|
/* Get the number of TCP resets transmitted. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_XMIT_TCP_RESETS, dwValue);
|
|
|
|
dprintf(" TCP resets: %10u", dwValue);
|
|
|
|
/* Get the number of TCP resets received. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CNTR_RECV_TCP_RESETS, dwValue);
|
|
|
|
dprintf(" %10u\n", dwValue);
|
|
|
|
end:
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the pointer to the NLB load. */
|
|
GetFieldOffset(MAIN_CTXT, MAIN_CTXT_FIELD_LOAD, &dwValue);
|
|
|
|
pAddr = pContext + dwValue;
|
|
|
|
dprintf(" %sNLB load: 0x%p\n",
|
|
(pAddr && (dwVerbosity == VERBOSITY_HIGH)) ? "-" : (pAddr) ? "+" : " ", pAddr);
|
|
|
|
/* Print the load information if verbosity is high. */
|
|
if (pAddr && (dwVerbosity == VERBOSITY_HIGH)) {
|
|
dprintf("\n");
|
|
PrintLoad(pAddr, VERBOSITY_LOW);
|
|
dprintf("\n");
|
|
}
|
|
|
|
/* Get the pointer to the NLB parameters. */
|
|
GetFieldOffset(MAIN_CTXT, MAIN_CTXT_FIELD_PARAMS, &dwValue);
|
|
|
|
pAddr = pContext + dwValue;
|
|
|
|
dprintf(" %sNLB parameters: 0x%p ",
|
|
(pAddr && (dwVerbosity == VERBOSITY_HIGH)) ? "-" : (pAddr) ? "+" : " ", pAddr);
|
|
|
|
/* Get the validity of the NLB parameter block. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_PARAMS_VALID, dwValue);
|
|
|
|
dprintf("(%s)\n", (dwValue) ? "Valid" : "Invalid");
|
|
|
|
/* Print the parameter information if verbosity is high. */
|
|
if (pAddr && (dwVerbosity == VERBOSITY_HIGH)) {
|
|
dprintf("\n");
|
|
PrintParams(pAddr, VERBOSITY_LOW);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintParams
|
|
* Description: Prints the contents of the CVY_PARAMS structure at the specified verbosity.
|
|
* LOW (0) prints fundamental configuration parameters.
|
|
* MEDIUM (1) prints all configured port rules.
|
|
* HIGH (2) prints other miscellaneous configuration.
|
|
* Author: Created by shouse, 1.21.01
|
|
*/
|
|
void PrintParams (ULONG64 pParams, ULONG dwVerbosity) {
|
|
WCHAR szString[256];
|
|
ULONG64 pAddr;
|
|
ULONG dwValue;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pParams) {
|
|
dprintf("Error: NLB parameter block is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
/* Get the parameter version number. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_VERSION, dwValue);
|
|
|
|
dprintf("NLB Parameters Block 0x%p (Version %d)\n", pParams, dwValue);
|
|
|
|
/* Get the offset of the hostname and retrieve the string from that address. */
|
|
if (GetFieldOffset(CVY_PARAMS, CVY_PARAMS_FIELD_HOSTNAME, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", CVY_PARAMS_FIELD_HOSTNAME, CVY_PARAMS);
|
|
else {
|
|
pAddr = pParams + dwValue;
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, CVY_MAX_HOST_NAME + 1);
|
|
|
|
dprintf(" Hostname: %ls\n", szString);
|
|
}
|
|
|
|
/* Get the host priority. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_HOST_PRIORITY, dwValue);
|
|
|
|
dprintf(" Host priority: %u\n", dwValue);
|
|
|
|
/* Get the initial cluster state flag. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_INITIAL_STATE, dwValue);
|
|
|
|
dprintf(" Preferred initial host state: %s\n",
|
|
(dwValue == CVY_HOST_STATE_STARTED) ? "Started" :
|
|
(dwValue == CVY_HOST_STATE_STOPPED) ? "Stopped" :
|
|
(dwValue == CVY_HOST_STATE_SUSPENDED) ? "Suspended" : "Unknown");
|
|
|
|
/* Get the current host state. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_HOST_STATE, dwValue);
|
|
|
|
dprintf(" Current host state: %s\n",
|
|
(dwValue == CVY_HOST_STATE_STARTED) ? "Started" :
|
|
(dwValue == CVY_HOST_STATE_STOPPED) ? "Stopped" :
|
|
(dwValue == CVY_HOST_STATE_SUSPENDED) ? "Suspended" : "Unknown");
|
|
|
|
/* Get the persisted states flags. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_PERSISTED_STATES, dwValue);
|
|
|
|
dprintf(" Persisted host states: ");
|
|
|
|
if (!dwValue)
|
|
dprintf("None");
|
|
|
|
if (dwValue & CVY_PERSIST_STATE_STARTED) {
|
|
dprintf("Started");
|
|
|
|
if ((dwValue &= ~CVY_PERSIST_STATE_STARTED))
|
|
dprintf(", ");
|
|
}
|
|
|
|
if (dwValue & CVY_PERSIST_STATE_STOPPED) {
|
|
dprintf("Stopped");
|
|
|
|
if ((dwValue &= ~CVY_PERSIST_STATE_STOPPED))
|
|
dprintf(", ");
|
|
}
|
|
|
|
if (dwValue & CVY_PERSIST_STATE_SUSPENDED) {
|
|
dprintf("Suspended");
|
|
|
|
if ((dwValue &= ~CVY_PERSIST_STATE_SUSPENDED))
|
|
dprintf(", ");
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the multicast support flag. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_MULTICAST_SUPPORT, dwValue);
|
|
|
|
dprintf(" Multicast support enabled: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the IGMP support flag. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_IGMP_SUPPORT, dwValue);
|
|
|
|
dprintf(" IGMP multicast support enabled: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the ICMP filter flag. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_FILTER_ICMP, dwValue);
|
|
|
|
dprintf(" ICMP receive filtering enabled: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" Remote control settings\n");
|
|
|
|
/* Get the remote control support flag. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_REMOTE_CONTROL_ENABLED, dwValue);
|
|
|
|
dprintf(" Enabled: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the remote control port. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_REMOTE_CONTROL_PORT, dwValue);
|
|
|
|
dprintf(" Port number: %u\n", dwValue);
|
|
|
|
/* Get the host priority. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_REMOTE_CONTROL_PASSWD, dwValue);
|
|
|
|
dprintf(" Password: 0x%08x\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" Cluster IP settings\n");
|
|
|
|
/* Get the offset of the cluster IP address and retrieve the string from that address. */
|
|
if (GetFieldOffset(CVY_PARAMS, CVY_PARAMS_FIELD_CL_IP_ADDR, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", CVY_PARAMS_FIELD_CL_IP_ADDR, CVY_PARAMS);
|
|
else {
|
|
pAddr = pParams + dwValue;
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, CVY_MAX_CL_IP_ADDR + 1);
|
|
|
|
dprintf(" IP address: %ls\n", szString);
|
|
}
|
|
|
|
/* Get the offset of the cluster netmask and retrieve the string from that address. */
|
|
if (GetFieldOffset(CVY_PARAMS, CVY_PARAMS_FIELD_CL_NET_MASK, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", CVY_PARAMS_FIELD_CL_NET_MASK, CVY_PARAMS);
|
|
else {
|
|
pAddr = pParams + dwValue;
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, CVY_MAX_CL_NET_MASK + 1);
|
|
|
|
dprintf(" Netmask: %ls\n", szString);
|
|
}
|
|
|
|
/* Get the offset of the cluster MAC address and retrieve the MAC from that address. */
|
|
if (GetFieldOffset(CVY_PARAMS, CVY_PARAMS_FIELD_CL_MAC_ADDR, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", CVY_PARAMS_FIELD_CL_MAC_ADDR, CVY_PARAMS);
|
|
else {
|
|
pAddr = pParams + dwValue;
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, CVY_MAX_NETWORK_ADDR + 1);
|
|
|
|
dprintf(" MAC address: %ls\n", szString);
|
|
}
|
|
|
|
/* Get the offset of the cluster IGMP multicast address and retrieve the string from that address. */
|
|
if (GetFieldOffset(CVY_PARAMS, CVY_PARAMS_FIELD_CL_IGMP_ADDR, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", CVY_PARAMS_FIELD_CL_IGMP_ADDR, CVY_PARAMS);
|
|
else {
|
|
pAddr = pParams + dwValue;
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, CVY_MAX_CL_IGMP_ADDR + 1);
|
|
|
|
dprintf(" IGMP multicast IP address: %ls\n", szString);
|
|
}
|
|
|
|
/* Get the offset of the cluster name and retrieve the string from that address. */
|
|
if (GetFieldOffset(CVY_PARAMS, CVY_PARAMS_FIELD_CL_NAME, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", CVY_PARAMS_FIELD_CL_NAME, CVY_PARAMS);
|
|
else {
|
|
pAddr = pParams + dwValue;
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, CVY_MAX_DOMAIN_NAME + 1);
|
|
|
|
dprintf(" Domain name: %ls\n", szString);
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" Dedicated IP settings\n");
|
|
|
|
/* Get the offset of the dedicated IP address and retrieve the string from that address. */
|
|
if (GetFieldOffset(CVY_PARAMS, CVY_PARAMS_FIELD_DED_IP_ADDR, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", CVY_PARAMS_FIELD_DED_IP_ADDR, CVY_PARAMS);
|
|
else {
|
|
pAddr = pParams + dwValue;
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, CVY_MAX_DED_IP_ADDR + 1);
|
|
|
|
dprintf(" IP address: %ls\n", szString);
|
|
}
|
|
|
|
/* Get the offset of the dedicated netmask and retrieve the string from that address. */
|
|
if (GetFieldOffset(CVY_PARAMS, CVY_PARAMS_FIELD_DED_NET_MASK, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", CVY_PARAMS_FIELD_DED_NET_MASK, CVY_PARAMS);
|
|
else {
|
|
pAddr = pParams + dwValue;
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, CVY_MAX_DED_NET_MASK + 1);
|
|
|
|
dprintf(" Netmask: %ls\n", szString);
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the offset of the BDA teaming parameters structure. */
|
|
if (GetFieldOffset(CVY_PARAMS, CVY_PARAMS_FIELD_BDA_TEAMING, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", CVY_PARAMS_FIELD_BDA_TEAMING, CVY_PARAMS);
|
|
else {
|
|
ULONG64 pBDA = pParams + dwValue;
|
|
|
|
/* Find out whether or not teaming is active on this adapter. */
|
|
GetFieldValue(pBDA, CVY_BDA, CVY_BDA_FIELD_ACTIVE, dwValue);
|
|
|
|
dprintf(" Bi-directional affinity teaming: %s\n", (dwValue) ? "Active" : "Inactive");
|
|
|
|
/* Get the offset of the team ID and retrieve the string from that address. */
|
|
if (GetFieldOffset(CVY_BDA, CVY_BDA_FIELD_TEAM_ID, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", CVY_BDA_FIELD_TEAM_ID, CVY_BDA);
|
|
else {
|
|
pAddr = pBDA + dwValue;
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, CVY_MAX_BDA_TEAM_ID + 1);
|
|
|
|
dprintf(" Team ID: %ls\n", szString);
|
|
}
|
|
|
|
/* Get the master flag. */
|
|
GetFieldValue(pBDA, CVY_BDA, CVY_BDA_FIELD_MASTER, dwValue);
|
|
|
|
dprintf(" Master: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the reverse hashing flag. */
|
|
GetFieldValue(pBDA, CVY_BDA, CVY_BDA_FIELD_REVERSE_HASH, dwValue);
|
|
|
|
dprintf(" Reverse hashing: %s\n", (dwValue) ? "Yes" : "No");
|
|
}
|
|
|
|
/* If we're printing at low verbosity, bail out here. */
|
|
if (dwVerbosity == VERBOSITY_LOW) return;
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the offset of the port rules and pass it to PrintPortRules. */
|
|
if (GetFieldOffset(CVY_PARAMS, CVY_PARAMS_FIELD_PORT_RULES, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", CVY_PARAMS_FIELD_PORT_RULES, CVY_PARAMS);
|
|
else {
|
|
pAddr = pParams + dwValue;
|
|
|
|
/* Get the number of port rules. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_NUM_RULES, dwValue);
|
|
|
|
PrintPortRules(dwValue, pAddr);
|
|
}
|
|
|
|
/* If we're printing at medium verbosity, bail out here. */
|
|
if (dwVerbosity == VERBOSITY_MEDIUM) return;
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the heartbeat period. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_ALIVE_PERIOD, dwValue);
|
|
|
|
dprintf(" Heartbeat period: %u millisecond(s)\n", dwValue);
|
|
|
|
/* Get the heartbeat loss tolerance. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_ALIVE_TOLERANCE, dwValue);
|
|
|
|
dprintf(" Heartbeat loss tolerance: %u\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the number of remote control actions to allocate. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_NUM_ACTIONS, dwValue);
|
|
|
|
dprintf(" Number of actions to allocate: %u\n", dwValue);
|
|
|
|
/* Get the number of packets to allocate. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_NUM_PACKETS, dwValue);
|
|
|
|
dprintf(" Number of packets to allocate: %u\n", dwValue);
|
|
|
|
/* Get the number of heartbeats to allocate. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_NUM_PINGS, dwValue);
|
|
|
|
dprintf(" Number of heartbeats to allocate: %u\n", dwValue);
|
|
|
|
/* Get the number of descriptors per allocation. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_NUM_DESCR, dwValue);
|
|
|
|
dprintf(" Descriptors per allocation: %u\n", dwValue);
|
|
|
|
/* Get the maximum number of descriptor allocations. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_MAX_DESCR, dwValue);
|
|
|
|
dprintf(" Maximum Descriptors allocations: %u\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the TCP connection descriptor timeout. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_TCP_TIMEOUT, dwValue);
|
|
|
|
dprintf(" TCP descriptor timeout: %u second(s)\n", dwValue);
|
|
|
|
/* Get the IPSec connection descriptor timeout. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_IPSEC_TIMEOUT, dwValue);
|
|
|
|
dprintf(" IPSec descriptor timeout: %u second(s)\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the NetBT support flag. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_NBT_SUPPORT, dwValue);
|
|
|
|
dprintf(" NetBT support enabled: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the multicast spoof flag. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_MCAST_SPOOF, dwValue);
|
|
|
|
dprintf(" Multicast spoofing enabled: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the netmon passthru flag. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_NETMON_PING, dwValue);
|
|
|
|
dprintf(" Netmon heartbeat passthru enabled: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the mask source MAC flag. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_MASK_SRC_MAC, dwValue);
|
|
|
|
dprintf(" Mask source MAC enabled: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the convert MAC flag. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_CONVERT_MAC, dwValue);
|
|
|
|
dprintf(" IP to MAC conversion enabled: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the IP change delay value. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_IP_CHANGE_DELAY, dwValue);
|
|
|
|
dprintf(" IP change delay: %u millisecond(s)\n", dwValue);
|
|
|
|
/* Get the dirty descriptor cleanup delay value. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_CLEANUP_DELAY, dwValue);
|
|
|
|
dprintf(" Dirty connection cleanup delay: %u millisecond(s)\n", dwValue);
|
|
}
|
|
|
|
/*
|
|
* Function: PrintPortRules
|
|
* Description: Prints the NLB port rules.
|
|
* Author: Created by shouse, 1.21.01
|
|
*/
|
|
void PrintPortRules (ULONG dwNumRules, ULONG64 pRules) {
|
|
ULONG dwRuleSize;
|
|
ULONG dwIndex;
|
|
ULONG64 pAddr;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pRules) {
|
|
dprintf("Error: NLB port rule block is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
dprintf(" Configured port rules (%u)\n", dwNumRules);
|
|
|
|
/* If no port rules are present, print a notification. */
|
|
if (!dwNumRules) {
|
|
dprintf(" There are no port rules configured on this cluster.\n");
|
|
return;
|
|
}
|
|
|
|
/* Print the column headers. */
|
|
dprintf(" Virtual IP Start End Protocol Mode Priority Load Weight Affinity\n");
|
|
dprintf(" --------------- ----- ----- -------- -------- -------- ----------- --------\n");
|
|
|
|
/* Find out the size of a CVY_RULE structure. */
|
|
dwRuleSize = GetTypeSize(CVY_RULE);
|
|
|
|
/* Loop through all port rules and print the configuration. Note: The print statements
|
|
are full of seemingly non-sensicle format strings, but trust me, they're right. */
|
|
for (dwIndex = 0; dwIndex < dwNumRules; dwIndex++) {
|
|
IN_ADDR dwIPAddr;
|
|
CHAR * szString;
|
|
ULONG dwValue;
|
|
USHORT wValue;
|
|
|
|
/* Get the VIP. Convert from a DWORD to a string. */
|
|
GetFieldValue(pRules, CVY_RULE, CVY_RULE_FIELD_VIP, dwValue);
|
|
|
|
if (dwValue != CVY_ALL_VIP) {
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" %-15s", szString);
|
|
} else
|
|
dprintf(" %-15s", "ALL VIPs");
|
|
|
|
/* Get the start port. */
|
|
GetFieldValue(pRules, CVY_RULE, CVY_RULE_FIELD_START_PORT, dwValue);
|
|
|
|
dprintf(" %5u", dwValue);
|
|
|
|
/* Get the end port. */
|
|
GetFieldValue(pRules, CVY_RULE, CVY_RULE_FIELD_END_PORT, dwValue);
|
|
|
|
dprintf(" %5u", dwValue);
|
|
|
|
/* Figure out the protocol. */
|
|
GetFieldValue(pRules, CVY_RULE, CVY_RULE_FIELD_PROTOCOL, dwValue);
|
|
|
|
switch (dwValue) {
|
|
case CVY_TCP:
|
|
dprintf(" %s ", "TCP");
|
|
break;
|
|
case CVY_UDP:
|
|
dprintf(" %s ", "UDP");
|
|
break;
|
|
case CVY_TCP_UDP:
|
|
dprintf(" %s ", "Both");
|
|
break;
|
|
default:
|
|
dprintf(" %s", "Unknown");
|
|
break;
|
|
}
|
|
|
|
/* Find the rule mode. */
|
|
GetFieldValue(pRules, CVY_RULE, CVY_RULE_FIELD_MODE, dwValue);
|
|
|
|
switch (dwValue) {
|
|
case CVY_SINGLE:
|
|
/* Print mode and priority. */
|
|
dprintf(" %s ", "Single");
|
|
|
|
/* Get the handling priority. */
|
|
GetFieldValue(pRules, CVY_RULE, CVY_RULE_FIELD_PRIORITY, dwValue);
|
|
|
|
dprintf(" %2u ", dwValue);
|
|
break;
|
|
case CVY_MULTI:
|
|
/* Print mode, weight and affinity. */
|
|
dprintf(" %s", "Multiple");
|
|
|
|
dprintf(" %8s", "");
|
|
|
|
/* Get the equal load flag. */
|
|
GetFieldValue(pRules, CVY_RULE, CVY_RULE_FIELD_EQUAL_LOAD, wValue);
|
|
|
|
if (wValue) {
|
|
dprintf(" %5s ", "Equal");
|
|
} else {
|
|
/* If distribution is unequal, get the load weight. */
|
|
GetFieldValue(pRules, CVY_RULE, CVY_RULE_FIELD_LOAD_WEIGHT, dwValue);
|
|
|
|
dprintf(" %3u ", dwValue);
|
|
}
|
|
|
|
/* Get the affinity for this rule. */
|
|
GetFieldValue(pRules, CVY_RULE, CVY_RULE_FIELD_AFFINITY, wValue);
|
|
|
|
switch (wValue) {
|
|
case CVY_AFFINITY_NONE:
|
|
dprintf(" %s", "None");
|
|
break;
|
|
case CVY_AFFINITY_SINGLE:
|
|
dprintf(" %s", "Single");
|
|
break;
|
|
case CVY_AFFINITY_CLASSC:
|
|
dprintf(" %s", "Class C");
|
|
break;
|
|
default:
|
|
dprintf(" %s", "Unknown");
|
|
break;
|
|
}
|
|
|
|
break;
|
|
case CVY_NEVER:
|
|
/* Print the mode. */
|
|
dprintf(" %s", "Disabled");
|
|
break;
|
|
default:
|
|
|
|
break;
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Advance the pointer to the next index in the array of structures. */
|
|
pRules += dwRuleSize;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintLoad
|
|
* Description: Prints the contents of the CVY_LOAD structure at the specified verbosity.
|
|
* LOW (0)
|
|
* MEDIUM (1)
|
|
* HIGH (2)
|
|
* Author: Created by shouse, 1.21.01
|
|
*/
|
|
void PrintLoad (ULONG64 pLoad, ULONG dwVerbosity) {
|
|
WCHAR szString[256];
|
|
ULONG dwMissedPings[CVY_MAX_HOSTS];
|
|
ULONG dwDirtyBins[CVY_MAX_BINS];
|
|
ULONG64 pQueue;
|
|
ULONG64 pAddr;
|
|
ULONG dwValue;
|
|
ULONG dwHostID;
|
|
BOOL bActive;
|
|
BOOL bValue;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pLoad) {
|
|
dprintf("Error: NLB load block is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
dprintf("NLB Load Block 0x%p\n", pLoad);
|
|
|
|
/* Get the LOAD_CTXT_CODE from the structure to make sure that this address
|
|
indeed points to a valid NLB load block. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != LOAD_CTXT_CODE) {
|
|
dprintf(" Error: Invalid NLB load block. Wrong code found (0x%08x).\n", dwValue);
|
|
return;
|
|
}
|
|
|
|
/* Get my host ID. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_HOST_ID, dwHostID);
|
|
|
|
/* Determine whether or not the load context has been initialized. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_REF_COUNT, dwValue);
|
|
|
|
dprintf(" Reference count: %u\n", dwValue);
|
|
|
|
/* Determine whether or not the load context has been initialized. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_INIT, bValue);
|
|
|
|
dprintf(" Load initialized: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Determine whether or not the load context is active. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_ACTIVE, bActive);
|
|
|
|
dprintf(" Load active: %s\n", (bActive) ? "Yes" : "No");
|
|
|
|
/* Get the number of total packets handled since last convergence. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_PACKET_COUNT, dwValue);
|
|
|
|
dprintf(" Packets handled since convergence: %u\n", dwValue);
|
|
|
|
/* Get the number of currently active connections. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_CONNECTIONS, dwValue);
|
|
|
|
dprintf(" Current active connections: %u\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
/* Find out the level of consistency from incoming heartbeats. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_CONSISTENT, bValue);
|
|
|
|
dprintf(" Consistent heartbeats detected: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Have we seen duplicate host IDs? */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_DUP_HOST_ID, bValue);
|
|
|
|
dprintf(" Duplicate host IDs: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Have we seen duplicate handling priorities? */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_DUP_PRIORITY, bValue);
|
|
|
|
dprintf(" Duplicate handling priorities: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Have we seen inconsistent BDA teaming configuration? */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_BAD_TEAM_CONFIG, bValue);
|
|
|
|
dprintf(" Inconsistent BDA teaming: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Have we seen inconsistent BDA teaming configuration? */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_LEGACY_HOSTS, dwValue);
|
|
|
|
dprintf(" Mixed cluster detected: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Have we seen a different number of port rules? */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_BAD_NUM_RULES, bValue);
|
|
|
|
dprintf(" Different number of port rules: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Is the new host map bad? */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_BAD_NEW_MAP, bValue);
|
|
|
|
dprintf(" Invalid new host map: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Do the maps overlap? */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_OVERLAPPING_MAP, bValue);
|
|
|
|
dprintf(" Overlapping maps: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Was there an error in updating bins? */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_RECEIVING_BINS, bValue);
|
|
|
|
dprintf(" Received bins already owned: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Were there orphaned bins after an update? */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_ORPHANED_BINS, bValue);
|
|
|
|
dprintf(" Orphaned bins: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the current host map. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_HOST_MAP, dwValue);
|
|
|
|
dprintf(" Current host map: 0x%08x ", dwValue);
|
|
|
|
/* If there are hosts in the map, print them. */
|
|
if (dwValue) {
|
|
dprintf("(");
|
|
PrintHostList(dwValue);
|
|
dprintf(")");
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the current map of pinged hosts. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_PING_MAP, dwValue);
|
|
|
|
dprintf(" Ping'd host map: 0x%08x ", dwValue);
|
|
|
|
/* If there are hosts in the map, print them. */
|
|
if (dwValue) {
|
|
dprintf("(");
|
|
PrintHostList(dwValue);
|
|
dprintf(")");
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the map from the last convergence. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_LAST_MAP, dwValue);
|
|
|
|
dprintf(" Host map after last convergence: 0x%08x ", dwValue);
|
|
|
|
/* If there are hosts in the map, print them. */
|
|
if (dwValue) {
|
|
dprintf("(");
|
|
PrintHostList(dwValue);
|
|
dprintf(")");
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the stable host map. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_STABLE_MAP, dwValue);
|
|
|
|
dprintf(" Stable host map: 0x%08x ", dwValue);
|
|
|
|
/* If there are hosts in the map, print them. */
|
|
if (dwValue) {
|
|
dprintf("(");
|
|
PrintHostList(dwValue);
|
|
dprintf(")");
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the minimum number of timeouts with stable condition. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_MIN_STABLE, dwValue);
|
|
|
|
dprintf(" Stable timeouts necessary: %u\n", dwValue);
|
|
|
|
/* Get the number of local stable timeouts. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_LOCAL_STABLE, dwValue);
|
|
|
|
dprintf(" Local stable timeouts: %u\n", dwValue);
|
|
|
|
/* Get the number of global stable timeouts. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_ALL_STABLE, dwValue);
|
|
|
|
dprintf(" Global stable timeouts: %u\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the default timeout period. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_DEFAULT_TIMEOUT, dwValue);
|
|
|
|
dprintf(" Default timeout interval: %u millisecond(s)\n", dwValue);
|
|
|
|
/* Get the current timeout period. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_CURRENT_TIMEOUT, dwValue);
|
|
|
|
dprintf(" Current timeout interval: %u millisecond(s)\n", dwValue);
|
|
|
|
/* Get the ping miss tolerance. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_PING_TOLERANCE, dwValue);
|
|
|
|
dprintf(" Missed ping tolerance: %u\n", dwValue);
|
|
|
|
/* Get the missed ping array. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_PING_MISSED, dwMissedPings);
|
|
|
|
dprintf(" Missed pings: ");
|
|
|
|
PrintMissedPings(dwMissedPings);
|
|
|
|
dprintf("\n");
|
|
|
|
/* Are we waiting for a cleanup? */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_CLEANUP_WAITING, bValue);
|
|
|
|
dprintf(" Cleanup waiting: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Get the cleanup timeout. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_CLEANUP_TIMEOUT, dwValue);
|
|
|
|
dprintf(" Cleanup timeout: %.1f second(s)\n", (float)(dwValue/1000.0));
|
|
|
|
/* Get the current cleanup wait time. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_CLEANUP_CURRENT, dwValue);
|
|
|
|
dprintf(" Current cleanup wait time: %.1f second(s)\n", (float)(dwValue/1000.0));
|
|
|
|
/* Get the number of dirty connection descriptors. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_NUM_DIRTY, dwValue);
|
|
|
|
dprintf(" Number of dirty connections: %u\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the TCP connection descriptor timeout. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_CLOCK_SECONDS, dwValue);
|
|
|
|
dprintf(" Internal clock time: %u.", dwValue);
|
|
|
|
/* Get the TCP connection descriptor timeout. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_CLOCK_MILISECONDS, dwValue);
|
|
|
|
dprintf("%03u second(s)\n", dwValue);
|
|
|
|
/* Get the number of convergences since we joined the cluster. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_NUM_CONVERGENCES, dwValue);
|
|
|
|
dprintf(" Total number of convergences: %u\n", dwValue);
|
|
|
|
/* Get the time since the last convergence completed. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_LAST_CONVERGENCE, dwValue);
|
|
|
|
dprintf(" Time of last completed convergence: %u.0 second(s)\n", dwValue);
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the maximum number of allocations allowed. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_MAX_DSCR_OUT, dwValue);
|
|
|
|
dprintf(" Maximum descriptor allocations: %u\n", dwValue);
|
|
|
|
/* Get the number of allocations thusfar. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_NUM_DSCR_OUT, dwValue);
|
|
|
|
dprintf(" Number of descriptor allocations: %u\n", dwValue);
|
|
|
|
/* Get the inhibited allocations flag. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_INHIBITED_ALLOC, bValue);
|
|
|
|
dprintf(" Allocations inhibited: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Get the failed allocations flag. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_FAILED_ALLOC, bValue);
|
|
|
|
dprintf(" Allocations failed: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* If wer're printing at low verbosity, bail out here. */
|
|
if (dwVerbosity == VERBOSITY_LOW) return;
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the address of the global established connection queue. */
|
|
pAddr = GetExpression(CONN_ESTABQ);
|
|
|
|
if (!pAddr)
|
|
/* If this global variable is NULL, check the symbols. */
|
|
ErrorCheckSymbols(CONN_ESTABQ);
|
|
else
|
|
dprintf(" Global established connections[0]: 0x%p\n", pAddr);
|
|
|
|
/* Get the address of the global established connection queue. */
|
|
pAddr = GetExpression(CONN_PENDINGQ);
|
|
|
|
if (!pAddr)
|
|
/* If this global variable is NULL, check the symbols. */
|
|
ErrorCheckSymbols(CONN_PENDINGQ);
|
|
else
|
|
dprintf(" Global pending connections[0]: 0x%p\n", pAddr);
|
|
|
|
/* Get the address of the global established connection queue. */
|
|
pAddr = GetExpression(PENDING_CONN_POOL);
|
|
|
|
if (!pAddr)
|
|
/* If this global variable is NULL, check the symbols. */
|
|
ErrorCheckSymbols(PENDING_CONN_POOL);
|
|
else {
|
|
/* Get the address of the global pending connection state pool. */
|
|
pAddr = GetPointerFromAddress(pAddr);
|
|
|
|
dprintf(" Global pending connection pool: 0x%p\n", pAddr);
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the number of allocations thusfar. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_FREE_POOL, pAddr);
|
|
|
|
dprintf(" Free descriptor pool: 0x%p\n", pAddr);
|
|
|
|
/* Get the offset of the connection descriptor queue hash array. */
|
|
if (GetFieldOffset(LOAD_CTXT, LOAD_CTXT_FIELD_CONN_QUEUE, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", LOAD_CTXT_FIELD_CONN_QUEUE, LOAD_CTXT);
|
|
else {
|
|
pAddr = pLoad + dwValue;
|
|
|
|
dprintf(" Connection descriptor queue[0]: 0x%p\n", pAddr);
|
|
}
|
|
|
|
/* Get the offset of the dirty descriptor queue. */
|
|
if (GetFieldOffset(LOAD_CTXT, LOAD_CTXT_FIELD_DIRTY_QUEUE, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", LOAD_CTXT_FIELD_DIRTY_QUEUE, LOAD_CTXT);
|
|
else {
|
|
pAddr = pLoad + dwValue;
|
|
|
|
dprintf(" Dirty descriptor queue: 0x%p\n", pAddr);
|
|
}
|
|
|
|
/* Get the offset of the recovery queue. */
|
|
if (GetFieldOffset(LOAD_CTXT, LOAD_CTXT_FIELD_RECOVERY_QUEUE, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", LOAD_CTXT_FIELD_RECOVERY_QUEUE, LOAD_CTXT);
|
|
else {
|
|
pAddr = pLoad + dwValue;
|
|
|
|
dprintf(" Recovery descriptor queue: 0x%p\n", pAddr);
|
|
}
|
|
|
|
/* Get the offset of the TCP descriptor timeout queue. */
|
|
if (GetFieldOffset(LOAD_CTXT, LOAD_CTXT_FIELD_TCP_TIMEOUT_QUEUE, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", LOAD_CTXT_FIELD_TCP_TIMEOUT_QUEUE, LOAD_CTXT);
|
|
else {
|
|
pAddr = pLoad + dwValue;
|
|
|
|
dprintf(" TCP descriptor timeout queue: 0x%p\n", pAddr);
|
|
}
|
|
|
|
/* Get the offset of the IPSec descriptor timeout queue. */
|
|
if (GetFieldOffset(LOAD_CTXT, LOAD_CTXT_FIELD_IPSEC_TIMEOUT_QUEUE, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", LOAD_CTXT_FIELD_IPSEC_TIMEOUT_QUEUE, LOAD_CTXT);
|
|
else {
|
|
pAddr = pLoad + dwValue;
|
|
|
|
dprintf(" IPSec descriptor timeout queue: 0x%p\n", pAddr);
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the dirty bin array. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_DIRTY_BINS, dwDirtyBins);
|
|
|
|
dprintf(" Dirty bins: ");
|
|
|
|
/* Print the bins which have dirty connections. */
|
|
PrintDirtyBins(dwDirtyBins);
|
|
|
|
dprintf("\n");
|
|
|
|
/* Print load module state for all of the configured NLB port rules. */
|
|
{
|
|
ULONG dwPortRuleStateSize;
|
|
ULONG dwNumRules = 0;
|
|
ULONG dwIndex;
|
|
ULONG dwTemp;
|
|
|
|
/* Get the offset of the port rule state structures and use PrintPortRuleState to print them. */
|
|
if (GetFieldOffset(LOAD_CTXT, LOAD_CTXT_FIELD_PORT_RULE_STATE, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", LOAD_CTXT_FIELD_PORT_RULE_STATE, LOAD_CTXT);
|
|
else {
|
|
/* If the load module is not currently active, then we really can't trust the
|
|
number of rules listed in the CVY_PARAMS structure, as a reload may have
|
|
occurred while the load module was stopped. Get the number of rules from
|
|
the heartbeat instead, which should be consistent with the state of the
|
|
load module the last time it was active. */
|
|
if (!bActive) {
|
|
USHORT wValue;
|
|
|
|
dprintf("Warning: The load module is inactive and therefore the information in the NLB parameters\n");
|
|
dprintf(" structure is potentially out-of-sync with the current state of the load module.\n");
|
|
dprintf(" The number of port rules will be extracted from the heartbeat message rather than\n");
|
|
dprintf(" from the NLB parameters structure; the number of port rules indicated in the heart-\n");
|
|
dprintf(" beat is consistent with the number of port rules configured in the load module at\n");
|
|
dprintf(" the last instant the load module was active.\n");
|
|
dprintf("\n");
|
|
|
|
/* Get the offset of the heartbeat structure and use PrintHeartbeat to print it. */
|
|
if (GetFieldOffset(LOAD_CTXT, LOAD_CTXT_FIELD_PING, &dwTemp))
|
|
dprintf("Can't get offset of %s in %s\n", LOAD_CTXT_FIELD_PING, LOAD_CTXT);
|
|
else {
|
|
pAddr = pLoad + dwTemp;
|
|
|
|
/* Get the number of port rules. */
|
|
GetFieldValue(pAddr, PING_MSG, PING_MSG_FIELD_NUM_RULES, wValue);
|
|
|
|
/* Cast the USHORT to a ULONG. Subtract one for the DEFAULT port rule,
|
|
which is accounted for in the structure of the loop below - do NOT
|
|
count it here. */
|
|
dwNumRules = (ULONG)(wValue - 1);
|
|
}
|
|
} else {
|
|
/* Get the offset of the params pointer. */
|
|
if (GetFieldOffset(LOAD_CTXT, LOAD_CTXT_FIELD_PARAMS, &dwTemp))
|
|
dprintf("Can't get offset of %s in %s\n", LOAD_CTXT_FIELD_PARAMS, LOAD_CTXT);
|
|
else {
|
|
pAddr = pLoad + dwTemp;
|
|
|
|
/* Retrieve the pointer. */
|
|
pAddr = GetPointerFromAddress(pAddr);
|
|
|
|
/* Get the number of port rules from the params block. */
|
|
GetFieldValue(pAddr, CVY_PARAMS, CVY_PARAMS_FIELD_NUM_RULES, dwNumRules);
|
|
}
|
|
}
|
|
|
|
/* Set the address of the port rule state array. */
|
|
pAddr = pLoad + dwValue;
|
|
}
|
|
|
|
/* Find out the size of a BIN_STATE structure. */
|
|
dwPortRuleStateSize = GetTypeSize(BIN_STATE);
|
|
|
|
/* NOTE: its "less than or equal" as opposed to "less than" because we need to include
|
|
the DEFAULT port rule, which is always at index "num rules" (i.e. the last rule). */
|
|
for (dwIndex = 0; dwIndex <= dwNumRules; dwIndex++) {
|
|
/* Print the state information for the port rule. */
|
|
PrintPortRuleState(pAddr, dwHostID, (dwIndex == dwNumRules) ? TRUE : FALSE);
|
|
|
|
if (dwIndex < dwNumRules) dprintf("\n");
|
|
|
|
/* Advance the pointer to the next port rule. */
|
|
pAddr += dwPortRuleStateSize;
|
|
}
|
|
}
|
|
|
|
/* If wer're printing at medium verbosity, bail out here. */
|
|
if (dwVerbosity == VERBOSITY_MEDIUM) return;
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" Heartbeat message\n");
|
|
|
|
/* Get the offset of the heartbeat structure and use PrintHeartbeat to print it. */
|
|
if (GetFieldOffset(LOAD_CTXT, LOAD_CTXT_FIELD_PING, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", LOAD_CTXT_FIELD_PING, LOAD_CTXT);
|
|
else {
|
|
pAddr = pLoad + dwValue;
|
|
|
|
/* Print the NLB heartbeat contents. */
|
|
PrintHeartbeat(pAddr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintResp
|
|
* Description: Prints the NLB private data associated with the given packet.
|
|
* Author: Created by shouse, 1.31.01
|
|
*/
|
|
void PrintResp (ULONG64 pPacket, ULONG dwDirection) {
|
|
ULONG64 pPacketStack;
|
|
ULONG bStackLeft;
|
|
ULONG64 pProtReserved = 0;
|
|
ULONG64 pIMReserved = 0;
|
|
ULONG64 pMPReserved = 0;
|
|
ULONG64 pResp;
|
|
ULONG64 pAddr;
|
|
ULONG dwValue;
|
|
USHORT wValue;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pPacket) {
|
|
dprintf("Error: Packet is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
/* Print a warning concerning the importance of knowing whether its a send or receive. */
|
|
dprintf("Assuming packet 0x%p is on the %s packet path. If this is\n", pPacket,
|
|
(dwDirection == DIRECTION_RECEIVE) ? "RECEIVE" : "SEND");
|
|
dprintf(" incorrect, the information displayed below MAY be incorrect.\n");
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the current NDIS packet stack. */
|
|
pPacketStack = PrintCurrentPacketStack(pPacket, &bStackLeft);
|
|
|
|
dprintf("\n");
|
|
|
|
if (pPacketStack) {
|
|
/* Get the offset of the IMReserved field in the packet stack. */
|
|
if (GetFieldOffset(NDIS_PACKET_STACK, NDIS_PACKET_STACK_FIELD_IMRESERVED, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", NDIS_PACKET_STACK_FIELD_IMRESERVED, NDIS_PACKET_STACK);
|
|
else {
|
|
pAddr = pPacketStack + dwValue;
|
|
|
|
/* Get the resp pointer from the IMReserved field. */
|
|
pIMReserved = GetPointerFromAddress(pAddr);
|
|
}
|
|
}
|
|
|
|
/* Get the offset of the MiniportReserved field in the packet. */
|
|
if (GetFieldOffset(NDIS_PACKET, NDIS_PACKET_FIELD_MPRESERVED, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", NDIS_PACKET_FIELD_MPRESERVED, NDIS_PACKET);
|
|
else {
|
|
pAddr = pPacket + dwValue;
|
|
|
|
/* Get the resp pointer from the MPReserved field. */
|
|
pMPReserved = GetPointerFromAddress(pAddr);
|
|
}
|
|
|
|
/* Get the offset of the ProtocolReserved field in the packet. */
|
|
if (GetFieldOffset(NDIS_PACKET, NDIS_PACKET_FIELD_PROTRESERVED, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", NDIS_PACKET_FIELD_PROTRESERVED, NDIS_PACKET);
|
|
else {
|
|
pProtReserved = pPacket + dwValue;
|
|
}
|
|
|
|
/* Mimic #define MAIN_RESP_FIELD(pkt, left, ps, rsp, send) (from wlbs\driver\main.h). */
|
|
if (pPacketStack) {
|
|
if (pIMReserved)
|
|
pResp = pIMReserved;
|
|
else if (dwDirection == DIRECTION_SEND)
|
|
pResp = pProtReserved;
|
|
else if (pMPReserved)
|
|
pResp = pMPReserved;
|
|
else
|
|
pResp = pProtReserved;
|
|
} else {
|
|
if (dwDirection == DIRECTION_SEND)
|
|
pResp = pProtReserved;
|
|
else if (pMPReserved)
|
|
pResp = pMPReserved;
|
|
else
|
|
pResp = pProtReserved;
|
|
}
|
|
|
|
dprintf("NLB Main Protocol Reserved Block 0x%p\n");
|
|
|
|
/* Get the offset of the miscellaneous pointer. */
|
|
if (GetFieldOffset(MAIN_PROTOCOL_RESERVED, MAIN_PROTOCOL_RESERVED_FIELD_MISCP, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", MAIN_PROTOCOL_RESERVED_FIELD_MISCP, MAIN_PROTOCOL_RESERVED);
|
|
else {
|
|
pAddr = pResp + dwValue;
|
|
|
|
/* Retrieve the pointer. */
|
|
pAddr = GetPointerFromAddress(pAddr);
|
|
|
|
dprintf(" Miscellaneous pointer: 0x%p\n", pAddr);
|
|
}
|
|
|
|
/* Retrieve the packet type from the NLB private data. */
|
|
GetFieldValue(pResp, MAIN_PROTOCOL_RESERVED, MAIN_PROTOCOL_RESERVED_FIELD_TYPE, wValue);
|
|
|
|
switch (wValue) {
|
|
case MAIN_PACKET_TYPE_NONE:
|
|
dprintf(" Packet type: %u (None)\n", wValue);
|
|
break;
|
|
case MAIN_PACKET_TYPE_PING:
|
|
dprintf(" Packet type: %u (Heartbeat)\n", wValue);
|
|
break;
|
|
case MAIN_PACKET_TYPE_INDICATE:
|
|
dprintf(" Packet type: %u (Indicate)\n", wValue);
|
|
break;
|
|
case MAIN_PACKET_TYPE_PASS:
|
|
dprintf(" Packet type: %u (Passthrough)\n", wValue);
|
|
break;
|
|
case MAIN_PACKET_TYPE_CTRL:
|
|
dprintf(" Packet type: %u (Remote Control)\n", wValue);
|
|
break;
|
|
case MAIN_PACKET_TYPE_TRANSFER:
|
|
dprintf(" Packet type: %u (Transfer)\n", wValue);
|
|
break;
|
|
case MAIN_PACKET_TYPE_IGMP:
|
|
dprintf(" Packet type: %u (IGMP)\n", wValue);
|
|
break;
|
|
default:
|
|
dprintf(" Packet type: %u (Invalid)\n", wValue);
|
|
break;
|
|
}
|
|
|
|
/* Retrieve the group from the NLB private data. */
|
|
GetFieldValue(pResp, MAIN_PROTOCOL_RESERVED, MAIN_PROTOCOL_RESERVED_FIELD_GROUP, wValue);
|
|
|
|
switch (wValue) {
|
|
case MAIN_FRAME_UNKNOWN:
|
|
dprintf(" Packet type: %u (Unknown)\n", wValue);
|
|
break;
|
|
case MAIN_FRAME_DIRECTED:
|
|
dprintf(" Packet type: %u (Directed)\n", wValue);
|
|
break;
|
|
case MAIN_FRAME_MULTICAST:
|
|
dprintf(" Packet type: %u (Multicast)\n", wValue);
|
|
break;
|
|
case MAIN_FRAME_BROADCAST:
|
|
dprintf(" Packet type: %u (Broadcast)\n", wValue);
|
|
break;
|
|
default:
|
|
dprintf(" Packet type: %u (Invalid)\n", wValue);
|
|
break;
|
|
}
|
|
|
|
/* Retrieve the data field from the NLB private data. */
|
|
GetFieldValue(pResp, MAIN_PROTOCOL_RESERVED, MAIN_PROTOCOL_RESERVED_FIELD_DATA, dwValue);
|
|
|
|
dprintf(" Data: %u\n", dwValue);
|
|
|
|
/* Retrieve the length field from the NLB private data. */
|
|
GetFieldValue(pResp, MAIN_PROTOCOL_RESERVED, MAIN_PROTOCOL_RESERVED_FIELD_LENGTH, dwValue);
|
|
|
|
dprintf(" Length: %u\n", dwValue);
|
|
}
|
|
|
|
/*
|
|
* Function: PrintCurrentPacketStack
|
|
* Description: Retrieves the current packet stack for the specified packet. Note: this
|
|
* is heavily dependent on the current NDIS packet stacking mechanics - any
|
|
* changes to NDIS packet stacking could easily (will) break this. This
|
|
* entire function mimics NdisIMGetCurrentPacketStack().
|
|
* Author: Created by shouse, 1.31.01
|
|
*/
|
|
ULONG64 PrintCurrentPacketStack (ULONG64 pPacket, ULONG * bStackLeft) {
|
|
ULONG64 pNumPacketStacks;
|
|
ULONG64 pPacketWrapper;
|
|
ULONG64 pPacketStack;
|
|
ULONG dwNumPacketStacks;
|
|
ULONG dwStackIndexSize;
|
|
ULONG dwPacketStackSize;
|
|
ULONG dwCurrentIndex;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pPacket) {
|
|
dprintf("Error: Packet is NULL.\n");
|
|
*bStackLeft = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* Get the address of the global variable containing the number of packet stacks. */
|
|
pNumPacketStacks = GetExpression(NDIS_PACKET_STACK_SIZE);
|
|
|
|
if (!pNumPacketStacks) {
|
|
ErrorCheckSymbols(NDIS_PACKET_STACK_SIZE);
|
|
*bStackLeft = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* Get the number of packet stacks from the address. */
|
|
dwNumPacketStacks = GetUlongFromAddress(pNumPacketStacks);
|
|
|
|
/* Find out the size of a STACK_INDEX structure. */
|
|
dwStackIndexSize = GetTypeSize(STACK_INDEX);
|
|
|
|
/* Find out the size of a NDIS_PACKET_STACK structure. */
|
|
dwPacketStackSize = GetTypeSize(NDIS_PACKET_STACK);
|
|
|
|
/* This is the calculation we're doing (from ndis\sys\wrapper.h):
|
|
#define SIZE_PACKET_STACKS (sizeof(STACK_INDEX) + (sizeof(NDIS_PACKET_STACK) * ndisPacketStackSize)) */
|
|
pPacketStack = pPacket - (dwStackIndexSize + (dwPacketStackSize * dwNumPacketStacks));
|
|
|
|
/* The wrapper is the packet address minus the size of the stack index.
|
|
See ndis\sys\wrapper.h. We need this to get the current stack index. */
|
|
pPacketWrapper = pPacket - dwStackIndexSize;
|
|
|
|
dprintf("NDIS Packet Stack: 0x%p\n", pPacketStack);
|
|
|
|
/* Retrieve the current stack index. */
|
|
GetFieldValue(pPacketWrapper, NDIS_PACKET_WRAPPER, NDIS_PACKET_WRAPPER_FIELD_STACK_INDEX, dwCurrentIndex);
|
|
|
|
dprintf(" Current stack index: %d\n", dwCurrentIndex);
|
|
|
|
if (dwCurrentIndex < dwNumPacketStacks) {
|
|
/* If the current index is less than the number of stacks, then point the stack to
|
|
the right address and determine whether or not there is stack room left. */
|
|
pPacketStack += dwCurrentIndex * dwPacketStackSize;
|
|
*bStackLeft = (dwNumPacketStacks - dwCurrentIndex - 1) > 0;
|
|
} else {
|
|
/* If not, then we're out of stack space. */
|
|
pPacketStack = 0;
|
|
*bStackLeft = 0;
|
|
}
|
|
|
|
dprintf(" Current packet stack: 0x%p\n", pPacketStack);
|
|
dprintf(" Stack remaining: %s\n", (*bStackLeft) ? "Yes" : "No");
|
|
|
|
return pPacketStack;
|
|
}
|
|
|
|
/*
|
|
* Function: PrintHostList
|
|
* Description: Prints a list of hosts in a host map.
|
|
* Author: Created by shouse, 2.1.01
|
|
*/
|
|
void PrintHostList (ULONG dwHostMap) {
|
|
BOOL bFirst = TRUE;
|
|
ULONG dwHostNum = 1;
|
|
|
|
/* As long as there are hosts still in the map, print them. */
|
|
while (dwHostMap) {
|
|
/* If the least significant bit is set, print the host number. */
|
|
if (dwHostMap & 0x00000001) {
|
|
/* If this is the first host printed, just print the number. */
|
|
if (bFirst) {
|
|
dprintf("%u", dwHostNum);
|
|
bFirst = FALSE;
|
|
} else
|
|
/* Otherwise, we need to print a comma first. */
|
|
dprintf(", %u", dwHostNum);
|
|
}
|
|
|
|
/* Increment the host number and shift the map to the right one bit. */
|
|
dwHostNum++;
|
|
dwHostMap >>= 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintMissedPings
|
|
* Description: Prints a list hosts from which we are missing pings.
|
|
* Author: Created by shouse, 2.1.01
|
|
*/
|
|
void PrintMissedPings (ULONG dwMissedPings[]) {
|
|
BOOL bMissing = FALSE;
|
|
ULONG dwIndex;
|
|
|
|
/* Loop through the entire array of missed pings. */
|
|
for (dwIndex = 0; dwIndex < CVY_MAX_HOSTS; dwIndex++) {
|
|
/* If we're missing pings from this host, print the number missed and
|
|
the host priority, which is the index (host ID) plus one. */
|
|
if (dwMissedPings[dwIndex]) {
|
|
dprintf("\n Missing %u ping(s) from Host %u", dwMissedPings[dwIndex], dwIndex + 1);
|
|
|
|
/* Not the fact that we found at least one host with missing pings. */
|
|
bMissing = TRUE;
|
|
}
|
|
}
|
|
|
|
/* If we're missing no pings, print "None". */
|
|
if (!bMissing) dprintf("None");
|
|
|
|
dprintf("\n");
|
|
}
|
|
|
|
/*
|
|
* Function: PrintDirtyBins
|
|
* Description: Prints a list of bins with dirty connections.
|
|
* Author: Created by shouse, 2.1.01
|
|
*/
|
|
void PrintDirtyBins (ULONG dwDirtyBins[]) {
|
|
BOOL bFirst = TRUE;
|
|
ULONG dwIndex;
|
|
|
|
/* Loop through the entire array of dirty bins. */
|
|
for (dwIndex = 0; dwIndex < CVY_MAX_BINS; dwIndex++) {
|
|
if (dwDirtyBins[dwIndex]) {
|
|
/* If this is the first bin printed, just print the number. */
|
|
if (bFirst) {
|
|
dprintf("%u", dwIndex);
|
|
bFirst = FALSE;
|
|
} else
|
|
/* Otherwise, we need to print a comma first. */
|
|
dprintf(", %u", dwIndex);
|
|
}
|
|
}
|
|
|
|
/* If there are no dirty bins, print "None". */
|
|
if (bFirst) dprintf("None");
|
|
|
|
dprintf("\n");
|
|
}
|
|
|
|
/*
|
|
* Function: PrintHeartbeat
|
|
* Description: Prints the contents of the NLB heartbeat structure.
|
|
* Author: Created by shouse, 2.1.01
|
|
*/
|
|
void PrintHeartbeat (ULONG64 pHeartbeat) {
|
|
ULONG dwValue;
|
|
USHORT wValue;
|
|
ULONG dwIndex;
|
|
ULONG dwRuleCode[CVY_MAX_RULES];
|
|
ULONGLONG ddwCurrentMap[CVY_MAX_RULES];
|
|
ULONGLONG ddwNewMap[CVY_MAX_RULES];
|
|
ULONGLONG ddwIdleMap[CVY_MAX_RULES];
|
|
ULONGLONG ddwReadyBins[CVY_MAX_RULES];
|
|
ULONG dwLoadAmount[CVY_MAX_RULES];
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pHeartbeat) {
|
|
dprintf("Error: Heartbeat is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
/* Get the default host ID. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_DEFAULT_HOST_ID, wValue);
|
|
|
|
dprintf(" DEFAULT host ID: %u (%u)\n", wValue, wValue + 1);
|
|
|
|
/* Get my host ID. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_HOST_ID, wValue);
|
|
|
|
dprintf(" My host ID: %u (%u)\n", wValue, wValue + 1);
|
|
|
|
/* Get my host code. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_HOST_CODE, dwValue);
|
|
|
|
dprintf(" Unique host code: 0x%08x\n", dwValue);
|
|
|
|
/* Get the host state. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_STATE, wValue);
|
|
|
|
dprintf(" Host state: ");
|
|
|
|
switch (wValue) {
|
|
case HST_CVG:
|
|
dprintf("Converging\n");
|
|
break;
|
|
case HST_STABLE:
|
|
dprintf("Stable\n");
|
|
break;
|
|
case HST_NORMAL:
|
|
dprintf("Normal\n");
|
|
break;
|
|
default:
|
|
dprintf("Unknown\n");
|
|
break;
|
|
}
|
|
|
|
/* Get the teaming configuration code. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_TEAMING_CODE, dwValue);
|
|
|
|
dprintf(" BDA teaming configuration: 0x%08x\n", dwValue);
|
|
|
|
/* Get the packet count. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_PACKET_COUNT, dwValue);
|
|
|
|
dprintf(" Packets handled: %u\n", dwValue);
|
|
|
|
/* Get the number of port rules. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_NUM_RULES, wValue);
|
|
|
|
dprintf(" Number of port rules: %u\n", wValue);
|
|
|
|
/* Get the rule codes. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_RULE_CODE, dwRuleCode);
|
|
|
|
/* Get the current bin map. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_CURRENT_MAP, ddwCurrentMap);
|
|
|
|
/* Get the new bin map. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_NEW_MAP, ddwNewMap);
|
|
|
|
/* Get the idle bin map. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_IDLE_MAP, ddwIdleMap);
|
|
|
|
/* Get the ready bins map. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_READY_BINS, ddwReadyBins);
|
|
|
|
/* Get the load amount for each rule. */
|
|
GetFieldValue(pHeartbeat, PING_MSG, PING_MSG_FIELD_LOAD_AMOUNT, dwLoadAmount);
|
|
|
|
/* Loop through all port rules and spit out some information. */
|
|
for (dwIndex = 0; dwIndex < wValue; dwIndex++) {
|
|
/* Decode the rule. See CVY_RULE_CODE_SET() in net\inc\wlbsparams.h. */
|
|
ULONG dwStartPort = dwRuleCode[dwIndex] & 0x00000fff;
|
|
ULONG dwEndPort = (dwRuleCode[dwIndex] & 0x00fff000) >> 12;
|
|
ULONG dwProtocol = (dwRuleCode[dwIndex] & 0x0f000000) >> 24;
|
|
ULONG dwMode = (dwRuleCode[dwIndex] & 0x30000000) >> 28;
|
|
ULONG dwAffinity = (dwRuleCode[dwIndex] & 0xc0000000) >> 30;
|
|
|
|
dprintf(" Port rule %u\n", dwIndex + 1);
|
|
|
|
/* Print out the bin maps and load weight. */
|
|
dprintf(" Rule code: 0x%08x ", dwRuleCode[dwIndex]);
|
|
|
|
/* If this is the last port rule, then its the default port rule. */
|
|
if (dwIndex == (wValue - 1))
|
|
dprintf("(DEFAULT port rule)\n");
|
|
else {
|
|
#if 0 /* Because rule codes are overlapped logical ORs, we can't necessarily get back the
|
|
information that was put in, so we won't spit it out until we can guarantee that. */
|
|
|
|
/* Print out the port range - keep in mind that 16 bit port ranges are
|
|
encoded in 12 bit numbers, so this may not be 100% accurate. */
|
|
dprintf("(%u - %u, ", dwStartPort, dwEndPort);
|
|
|
|
/* Print the protocol. */
|
|
switch (dwProtocol) {
|
|
case CVY_TCP:
|
|
dprintf("TCP, ");
|
|
break;
|
|
case CVY_UDP:
|
|
dprintf("UDP, ");
|
|
break;
|
|
case CVY_TCP_UDP:
|
|
dprintf("TCP/UDP, ");
|
|
break;
|
|
default:
|
|
dprintf("Unknown protocol, ");
|
|
break;
|
|
}
|
|
|
|
/* Print the filtering mode. */
|
|
switch (dwMode) {
|
|
case CVY_SINGLE:
|
|
dprintf("Single host)\n");
|
|
break;
|
|
case CVY_MULTI:
|
|
dprintf("Multiple host, ");
|
|
|
|
/* If this rule uses multiple host, then we also print the affinity. */
|
|
switch (dwAffinity) {
|
|
case CVY_AFFINITY_NONE:
|
|
dprintf("No affinity)\n");
|
|
break;
|
|
case CVY_AFFINITY_SINGLE:
|
|
dprintf("Single affinity)\n");
|
|
break;
|
|
case CVY_AFFINITY_CLASSC:
|
|
dprintf("Class C affinity)\n");
|
|
break;
|
|
default:
|
|
dprintf("Unknown affinity)\n");
|
|
break;
|
|
}
|
|
|
|
break;
|
|
case CVY_NEVER:
|
|
dprintf("Disabled)\n");
|
|
break;
|
|
default:
|
|
dprintf("Unknown filtering mode)\n");
|
|
break;
|
|
}
|
|
#else
|
|
dprintf("\n");
|
|
#endif
|
|
/* Print the load weight. */
|
|
dprintf(" Load weight: %u\n", dwLoadAmount[dwIndex]);
|
|
}
|
|
|
|
/* Print the bin maps for all rules, default or not. */
|
|
dprintf(" Current map: 0x%015I64x\n", ddwCurrentMap[dwIndex]);
|
|
dprintf(" New map: 0x%015I64x\n", ddwNewMap[dwIndex]);
|
|
dprintf(" Idle map: 0x%015I64x\n", ddwIdleMap[dwIndex]);
|
|
dprintf(" Ready bins: 0x%015I64x\n", ddwReadyBins[dwIndex]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintPortRuleState
|
|
* Description: Prints the state information for the port rule.
|
|
* Author: Created by shouse, 2.5.01
|
|
*/
|
|
void PrintPortRuleState (ULONG64 pPortRule, ULONG dwHostID, BOOL bDefault) {
|
|
ULONG dwValue;
|
|
ULONG dwMode;
|
|
USHORT wValue;
|
|
BOOL bValue;
|
|
ULONG64 pAddr;
|
|
ULONGLONG ddwValue;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pPortRule) {
|
|
dprintf("Error: Port rule is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
/* Get the BIN_STATE_CODE from the structure to make sure that this address
|
|
indeed points to a valid NLB port rule state block. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != BIN_STATE_CODE) {
|
|
dprintf(" Error: Invalid NLB port rule state block. Wrong code found (0x%08x).\n", dwValue);
|
|
return;
|
|
}
|
|
|
|
/* Get the index of the rule - the "rule number". */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_INDEX, dwValue);
|
|
|
|
dprintf(" Port rule %u\n", dwValue + 1);
|
|
|
|
/* Is the port rule state initialized? */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_INITIALIZED, bValue);
|
|
|
|
dprintf(" State initialized: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Are the codes compatible? */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_COMPATIBLE, bValue);
|
|
|
|
dprintf(" Compatibility detected: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Is the port rule state initialized? */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_EQUAL, bValue);
|
|
|
|
dprintf(" Equal load balancing: %s\n", (bValue) ? "Yes" : "No");
|
|
|
|
/* Get the filtering mode for this port rule. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_MODE, dwMode);
|
|
|
|
dprintf(" Filtering mode: ");
|
|
|
|
/* If this is the DEFAULT port rule, then jump to the bottom. */
|
|
if (bDefault) {
|
|
dprintf("DEFAULT\n");
|
|
goto end;
|
|
}
|
|
|
|
switch (dwMode) {
|
|
case CVY_SINGLE:
|
|
dprintf("Single host\n");
|
|
break;
|
|
case CVY_MULTI:
|
|
dprintf("Multiple host\n");
|
|
break;
|
|
case CVY_NEVER:
|
|
dprintf("Disabled\n");
|
|
break;
|
|
default:
|
|
dprintf("Unknown\n");
|
|
break;
|
|
}
|
|
|
|
if (dwMode == CVY_MULTI) {
|
|
/* Get the affinity for this port rule. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_AFFINITY, wValue);
|
|
|
|
dprintf(" Affinity: ");
|
|
|
|
switch (wValue) {
|
|
case CVY_AFFINITY_NONE:
|
|
dprintf("None\n");
|
|
break;
|
|
case CVY_AFFINITY_SINGLE:
|
|
dprintf("Single\n");
|
|
break;
|
|
case CVY_AFFINITY_CLASSC:
|
|
dprintf("Class C\n");
|
|
break;
|
|
default:
|
|
dprintf("Unknown\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Get the protocol(s) for this port rule. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_PROTOCOL, dwValue);
|
|
|
|
dprintf(" Protocol(s): ");
|
|
|
|
/* Print the protocol. */
|
|
switch (dwValue) {
|
|
case CVY_TCP:
|
|
dprintf("TCP\n");
|
|
break;
|
|
case CVY_UDP:
|
|
dprintf("UDP\n");
|
|
break;
|
|
case CVY_TCP_UDP:
|
|
dprintf("TCP/UDP\n");
|
|
break;
|
|
default:
|
|
dprintf("Unknown\n");
|
|
break;
|
|
}
|
|
|
|
/* In multiple host filtering, print the load information. For single host
|
|
filtering, print the host priority information. */
|
|
if (dwMode == CVY_MULTI) {
|
|
ULONG dwCurrentLoad[CVY_MAX_HOSTS];
|
|
|
|
/* Get the original load for this rule on this host. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_ORIGINAL_LOAD, dwValue);
|
|
|
|
dprintf(" Configured load weight: %u\n", dwValue);
|
|
|
|
/* Get the original load for this rule on this host. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_CURRENT_LOAD, dwCurrentLoad);
|
|
|
|
dprintf(" Current load weight: %u/", dwCurrentLoad[dwHostID]);
|
|
|
|
/* Get the total load for this rule on all hosts. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_TOTAL_LOAD, dwValue);
|
|
|
|
dprintf("%u\n", dwValue);
|
|
} else if (dwMode == CVY_SINGLE) {
|
|
/* Get the host priority. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_ORIGINAL_LOAD, dwValue);
|
|
|
|
dprintf(" Host priority: %u\n", dwValue);
|
|
}
|
|
|
|
end:
|
|
|
|
/* Get the total number of active connections. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_TOTAL_CONNECTIONS, dwValue);
|
|
|
|
dprintf(" Total active connections: %u\n", dwValue);
|
|
|
|
/* Get the number of packets accepted. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_PACKETS_ACCEPTED, ddwValue);
|
|
|
|
dprintf(" Packets accepted: %u\n", ddwValue);
|
|
|
|
/* Get the number of packets dropped. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_PACKETS_DROPPED, ddwValue);
|
|
|
|
dprintf(" Packets dropped: %u\n", ddwValue);
|
|
|
|
/* Get the current map. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_CURRENT_MAP, ddwValue);
|
|
|
|
dprintf(" Current map: 0x%015I64x\n", ddwValue);
|
|
|
|
/* Get the all idle map. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_ALL_IDLE_MAP, ddwValue);
|
|
|
|
dprintf(" All idle map: 0x%015I64x\n", ddwValue);
|
|
|
|
/* Get the idle bins map. */
|
|
GetFieldValue(pPortRule, BIN_STATE, BIN_STATE_FIELD_IDLE_BINS, ddwValue);
|
|
|
|
dprintf(" My idle map: 0x%015I64x\n", ddwValue);
|
|
|
|
/* Get the offset of the IPSec descriptor timeout queue. */
|
|
if (GetFieldOffset(BIN_STATE, BIN_STATE_FIELD_CONN_QUEUE, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", BIN_STATE_FIELD_CONN_QUEUE, BIN_STATE);
|
|
else {
|
|
pAddr = pPortRule + dwValue;
|
|
|
|
dprintf(" Connection descriptor queue: 0x%p\n", pAddr);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Function: PrintBDAMember
|
|
* Description: Prints the BDA teaming configuration and state of a member.
|
|
* Author: Created by shouse, 4.8.01
|
|
*/
|
|
void PrintBDAMember (ULONG64 pMember) {
|
|
ULONG64 pAddr;
|
|
ULONG dwValue;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pMember) {
|
|
dprintf("Error: Member is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
/* Find out whether or not teaming is active on this adapter. */
|
|
GetFieldValue(pMember, BDA_MEMBER, BDA_MEMBER_FIELD_ACTIVE, dwValue);
|
|
|
|
dprintf(" Bi-directional affinity teaming: %s\n", (dwValue) ? "Active" : "Inactive");
|
|
|
|
/* Get the current BDA operation in progress. */
|
|
GetFieldValue(pMember, BDA_MEMBER, BDA_MEMBER_FIELD_OPERATION, dwValue);
|
|
|
|
dprintf(" Operation in progress: ");
|
|
|
|
switch (dwValue) {
|
|
case BDA_TEAMING_OPERATION_CREATING:
|
|
dprintf("Creating\n");
|
|
break;
|
|
case BDA_TEAMING_OPERATION_DELETING:
|
|
dprintf("Deleting\n");
|
|
break;
|
|
case BDA_TEAMING_OPERATION_NONE:
|
|
dprintf("None\n");
|
|
break;
|
|
default:
|
|
dprintf("Unkonwn\n");
|
|
break;
|
|
}
|
|
|
|
/* Get the team-assigned member ID. */
|
|
GetFieldValue(pMember, BDA_MEMBER, BDA_MEMBER_FIELD_MEMBER_ID, dwValue);
|
|
|
|
if (dwValue == CVY_BDA_INVALID_MEMBER_ID)
|
|
dprintf(" Member ID: %s\n", "Invalid");
|
|
else
|
|
dprintf(" Member ID: %u\n", dwValue);
|
|
|
|
/* Get the master status flag. */
|
|
GetFieldValue(pMember, BDA_MEMBER, BDA_MEMBER_FIELD_MASTER, dwValue);
|
|
|
|
dprintf(" Master: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the reverse hashing flag. */
|
|
GetFieldValue(pMember, BDA_MEMBER, BDA_MEMBER_FIELD_REVERSE_HASH, dwValue);
|
|
|
|
dprintf(" Reverse hashing: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the pointer to the BDA team. */
|
|
GetFieldValue(pMember, BDA_MEMBER, BDA_MEMBER_FIELD_TEAM, pAddr);
|
|
|
|
dprintf(" %sBDA team: 0x%p\n", (pAddr) ? "-" : "+", pAddr);
|
|
|
|
/* If this adapter is part of a team, print out the team configuration and state. */
|
|
if (pAddr) {
|
|
dprintf("\n");
|
|
PrintBDATeam(pAddr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintBDAMember
|
|
* Description: Prints the BDA teaming configuration and state of a member.
|
|
* Author: Created by shouse, 4.8.01
|
|
*/
|
|
void PrintBDATeam (ULONG64 pTeam) {
|
|
WCHAR szString[256];
|
|
ULONG64 pAddr;
|
|
ULONG dwValue;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pTeam) {
|
|
dprintf("Error: Team is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
dprintf(" BDA Team 0x%p\n", pTeam);
|
|
|
|
/* Find out whether or not the team is active. */
|
|
GetFieldValue(pTeam, BDA_TEAM, BDA_TEAM_FIELD_ACTIVE, dwValue);
|
|
|
|
dprintf(" Active: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the offset of the team ID and retrieve the string from that address. */
|
|
if (GetFieldOffset(BDA_TEAM, BDA_TEAM_FIELD_TEAM_ID, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", BDA_TEAM_FIELD_TEAM_ID, BDA_TEAM);
|
|
else {
|
|
pAddr = pTeam + dwValue;
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, CVY_MAX_BDA_TEAM_ID + 1);
|
|
|
|
dprintf(" Team ID: %ls\n", szString);
|
|
}
|
|
|
|
/* Get the current membership count. */
|
|
GetFieldValue(pTeam, BDA_TEAM, BDA_TEAM_FIELD_MEMBERSHIP_COUNT, dwValue);
|
|
|
|
dprintf(" Number of members: %u\n", dwValue);
|
|
|
|
/* Get the current membership list. */
|
|
GetFieldValue(pTeam, BDA_TEAM, BDA_TEAM_FIELD_MEMBERSHIP_FINGERPRINT, dwValue);
|
|
|
|
dprintf(" Membership fingerprint: 0x%08x\n", dwValue);
|
|
|
|
/* Get the current membership map. */
|
|
GetFieldValue(pTeam, BDA_TEAM, BDA_TEAM_FIELD_MEMBERSHIP_MAP, dwValue);
|
|
|
|
dprintf(" Members: 0x%08x ", dwValue);
|
|
|
|
/* If there are members in the map, print them. */
|
|
if (dwValue) {
|
|
dprintf("(");
|
|
PrintBDAMemberList(dwValue);
|
|
dprintf(")");
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the current consistency map. */
|
|
GetFieldValue(pTeam, BDA_TEAM, BDA_TEAM_FIELD_CONSISTENCY_MAP, dwValue);
|
|
|
|
dprintf(" Consistent members: 0x%08x ", dwValue);
|
|
|
|
/* If there are members in the map, print them. */
|
|
if (dwValue) {
|
|
dprintf("(");
|
|
PrintBDAMemberList(dwValue);
|
|
dprintf(")");
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the offset of the load module pointer. */
|
|
if (GetFieldOffset(BDA_TEAM, BDA_TEAM_FIELD_LOAD, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", BDA_TEAM_FIELD_LOAD, BDA_TEAM);
|
|
else {
|
|
pAddr = pTeam + dwValue;
|
|
|
|
/* Retrieve the pointer. */
|
|
pAddr = GetPointerFromAddress(pAddr);
|
|
|
|
dprintf(" Load: 0x%p\n", pAddr);
|
|
}
|
|
|
|
/* Get the offset of the load lock pointer. */
|
|
if (GetFieldOffset(BDA_TEAM, BDA_TEAM_FIELD_LOAD_LOCK, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", BDA_TEAM_FIELD_LOAD_LOCK, BDA_TEAM);
|
|
else {
|
|
pAddr = pTeam + dwValue;
|
|
|
|
/* Retrieve the pointer. */
|
|
pAddr = GetPointerFromAddress(pAddr);
|
|
|
|
dprintf(" Load lock: 0x%p\n", pAddr);
|
|
}
|
|
|
|
/* Get the offset of the previous pointer. */
|
|
if (GetFieldOffset(BDA_TEAM, BDA_TEAM_FIELD_PREV, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", BDA_TEAM_FIELD_PREV, BDA_TEAM);
|
|
else {
|
|
pAddr = pTeam + dwValue;
|
|
|
|
/* Retrieve the pointer. */
|
|
pAddr = GetPointerFromAddress(pAddr);
|
|
|
|
dprintf(" Previous BDA Team: 0x%p\n", pAddr);
|
|
}
|
|
|
|
/* Get the offset of the next pointer. */
|
|
if (GetFieldOffset(BDA_TEAM, BDA_TEAM_FIELD_NEXT, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", BDA_TEAM_FIELD_NEXT, BDA_TEAM);
|
|
else {
|
|
pAddr = pTeam + dwValue;
|
|
|
|
/* Retrieve the pointer. */
|
|
pAddr = GetPointerFromAddress(pAddr);
|
|
|
|
dprintf(" Next BDA Team: 0x%p\n", pAddr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintBDAMemberList
|
|
* Description: Prints a list of members in a BDA membership or consistency map.
|
|
* Author: Created by shouse, 4.8.01
|
|
*/
|
|
void PrintBDAMemberList (ULONG dwMemberMap) {
|
|
BOOL bFirst = TRUE;
|
|
ULONG dwMemberNum = 0;
|
|
|
|
/* As long as there are hosts still in the map, print them. */
|
|
while (dwMemberMap) {
|
|
/* If the least significant bit is set, print the host number. */
|
|
if (dwMemberMap & 0x00000001) {
|
|
/* If this is the first host printed, just print the number. */
|
|
if (bFirst) {
|
|
dprintf("%u", dwMemberNum);
|
|
bFirst = FALSE;
|
|
} else
|
|
/* Otherwise, we need to print a comma first. */
|
|
dprintf(", %u", dwMemberNum);
|
|
}
|
|
|
|
/* Increment the host number and shift the map to the right one bit. */
|
|
dwMemberNum++;
|
|
dwMemberMap >>= 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintConnectionDescriptor
|
|
* Description: Prints a connection descriptor (CONN_ENTRY).
|
|
* Author: Created by shouse, 1.9.02
|
|
*/
|
|
void PrintConnectionDescriptor (ULONG64 pDescriptor) {
|
|
IN_ADDR dwIPAddr;
|
|
CHAR * szString;
|
|
ULONG64 pAddr;
|
|
USHORT wValue;
|
|
ULONG dwValue;
|
|
BOOL bValue;
|
|
UCHAR cValue;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pDescriptor) {
|
|
dprintf("Error: Connection descriptor is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
dprintf(" Connection descriptor 0x%p\n", pDescriptor);
|
|
|
|
/* Check the connection entry code. */
|
|
GetFieldValue(pDescriptor, CONN_ENTRY, CONN_ENTRY_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != CVY_ENTRCODE) {
|
|
dprintf("Invalid NLB connection descriptor pointer.\n");
|
|
return;
|
|
}
|
|
|
|
/* Get load module pointer. */
|
|
GetFieldValue(pDescriptor, CONN_ENTRY, CONN_ENTRY_FIELD_LOAD, pAddr);
|
|
|
|
dprintf(" Load pointer: 0x%p\n", pAddr);
|
|
|
|
/* Get the flags register. */
|
|
GetFieldValue(pDescriptor, CONN_ENTRY, CONN_ENTRY_FIELD_FLAGS, wValue);
|
|
|
|
dprintf(" Used: %s\n", (wValue & NLB_CONN_ENTRY_FLAGS_USED) ? "Yes" : "No");
|
|
|
|
dprintf(" Dirty: %s\n", (wValue & NLB_CONN_ENTRY_FLAGS_DIRTY) ? "Yes" : "No");
|
|
|
|
dprintf(" Allocated: %s\n", (wValue & NLB_CONN_ENTRY_FLAGS_ALLOCATED) ? "Yes" : "No");
|
|
|
|
dprintf(" Virtual: %s\n", (wValue & NLB_CONN_ENTRY_FLAGS_VIRTUAL) ? "Yes" : "No");
|
|
|
|
/* Get the connection queue index. */
|
|
GetFieldValue(pDescriptor, CONN_ENTRY, CONN_ENTRY_FIELD_INDEX, wValue);
|
|
|
|
dprintf(" Index: %u\n", wValue);
|
|
|
|
/* Get the bin number. */
|
|
GetFieldValue(pDescriptor, CONN_ENTRY, CONN_ENTRY_FIELD_BIN, cValue);
|
|
|
|
dprintf(" Bin: %u\n", cValue);
|
|
|
|
/* Get the reference count. */
|
|
GetFieldValue(pDescriptor, CONN_ENTRY, CONN_ENTRY_FIELD_REF_COUNT, wValue);
|
|
|
|
dprintf(" Reference count: %u\n", wValue);
|
|
|
|
/* Get the descriptor timeout value. */
|
|
GetFieldValue(pDescriptor, CONN_ENTRY, CONN_ENTRY_FIELD_TIMEOUT, dwValue);
|
|
|
|
dprintf(" Timeout (clock time): %u.000\n", dwValue);
|
|
|
|
/* Get the client IP address, which is a DWORD, and convert it to a string. */
|
|
GetFieldValue(pDescriptor, CONN_ENTRY, CONN_ENTRY_FIELD_CLIENT_IP_ADDRESS, dwValue);
|
|
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" Client IP address: %s\n", szString);
|
|
|
|
/* Get the client port. */
|
|
GetFieldValue(pDescriptor, CONN_ENTRY, CONN_ENTRY_FIELD_CLIENT_PORT, wValue);
|
|
|
|
dprintf(" Client port: %u\n", wValue);
|
|
|
|
/* Get the server IP address, which is a DWORD, and convert it to a string. */
|
|
GetFieldValue(pDescriptor, CONN_ENTRY, CONN_ENTRY_FIELD_SERVER_IP_ADDRESS, dwValue);
|
|
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" Server IP address: %s\n", szString);
|
|
|
|
/* Get the client port. */
|
|
GetFieldValue(pDescriptor, CONN_ENTRY, CONN_ENTRY_FIELD_SERVER_PORT, wValue);
|
|
|
|
dprintf(" Server port: %u\n", wValue);
|
|
|
|
/* Get the connection protocol. */
|
|
GetFieldValue(pDescriptor, CONN_ENTRY, CONN_ENTRY_FIELD_PROTOCOL, cValue);
|
|
|
|
switch(cValue)
|
|
{
|
|
case TCPIP_PROTOCOL_TCP:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", cValue, "TCP");
|
|
break;
|
|
case TCPIP_PROTOCOL_PPTP:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", cValue, "PPTP");
|
|
break;
|
|
case TCPIP_PROTOCOL_UDP:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", cValue, "UDP");
|
|
break;
|
|
case TCPIP_PROTOCOL_IPSEC_UDP:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", wValue, "IPSec/UDP Fragment");
|
|
break;
|
|
case TCPIP_PROTOCOL_GRE:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", cValue, "GRE");
|
|
break;
|
|
case TCPIP_PROTOCOL_IPSEC1:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", cValue, "IPSec");
|
|
break;
|
|
default:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", cValue, "Unknown");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintPendingConnection
|
|
* Description: Prints a pending connection entry (PENDING_ENTRY).
|
|
* Author: Created by shouse, 4.15.02
|
|
*/
|
|
void PrintPendingConnection (ULONG64 pPending) {
|
|
IN_ADDR dwIPAddr;
|
|
CHAR * szString;
|
|
ULONG64 pAddr;
|
|
USHORT wValue;
|
|
ULONG dwValue;
|
|
UCHAR cValue;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pPending) {
|
|
dprintf("Error: Pending connection is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
dprintf(" Pending connection 0x%p\n", pPending);
|
|
|
|
/* Check the connection entry code. */
|
|
GetFieldValue(pPending, PENDING_ENTRY, PENDING_ENTRY_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != CVY_PENDINGCODE) {
|
|
dprintf("Invalid NLB pending connection pointer.\n");
|
|
return;
|
|
}
|
|
|
|
/* Get the client IP address, which is a DWORD, and convert it to a string. */
|
|
GetFieldValue(pPending, PENDING_ENTRY, PENDING_ENTRY_FIELD_CLIENT_IP_ADDRESS, dwValue);
|
|
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" Client IP address: %s\n", szString);
|
|
|
|
/* Get the client port. */
|
|
GetFieldValue(pPending, PENDING_ENTRY, PENDING_ENTRY_FIELD_CLIENT_PORT, wValue);
|
|
|
|
dprintf(" Client port: %u\n", wValue);
|
|
|
|
/* Get the server IP address, which is a DWORD, and convert it to a string. */
|
|
GetFieldValue(pPending, PENDING_ENTRY, PENDING_ENTRY_FIELD_SERVER_IP_ADDRESS, dwValue);
|
|
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" Server IP address: %s\n", szString);
|
|
|
|
/* Get the client port. */
|
|
GetFieldValue(pPending, PENDING_ENTRY, PENDING_ENTRY_FIELD_SERVER_PORT, wValue);
|
|
|
|
dprintf(" Server port: %u\n", wValue);
|
|
|
|
/* Get the connection protocol. */
|
|
GetFieldValue(pPending, PENDING_ENTRY, PENDING_ENTRY_FIELD_PROTOCOL, cValue);
|
|
|
|
switch(wValue)
|
|
{
|
|
case TCPIP_PROTOCOL_TCP:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", wValue, "TCP");
|
|
break;
|
|
case TCPIP_PROTOCOL_PPTP:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", wValue, "PPTP");
|
|
break;
|
|
case TCPIP_PROTOCOL_UDP:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", wValue, "UDP");
|
|
break;
|
|
case TCPIP_PROTOCOL_IPSEC_UDP:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", wValue, "IPSec/UDP Fragment");
|
|
break;
|
|
case TCPIP_PROTOCOL_GRE:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", wValue, "GRE");
|
|
break;
|
|
case TCPIP_PROTOCOL_IPSEC1:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", wValue, "IPSec");
|
|
break;
|
|
default:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", wValue, "Unknown");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintQueue
|
|
* Description: Prints MaxEntries entries in a connection descriptor queue.
|
|
* Author: Created by shouse, 4.15.01
|
|
*/
|
|
void PrintQueue (ULONG64 pQueue, ULONG dwIndex, ULONG dwMaxEntries) {
|
|
ULONG64 pAddr;
|
|
ULONG64 pNext;
|
|
ULONG dwEntryOffset;
|
|
ULONG dwLinkSize;
|
|
ULONG dwValue;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pQueue) {
|
|
dprintf("Error: Queue is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
/* Get the size of a queue link. */
|
|
dwLinkSize = GetTypeSize(LIST_ENTRY);
|
|
|
|
/* Use the index and the size of a LIST_ENTRY to move to the
|
|
indicated index in an array of queues. If no index was
|
|
provided, dwIndex is zero so the queue pointer is unchanged. */
|
|
pQueue += (dwLinkSize * dwIndex);
|
|
|
|
/* Get the Next pointer from the list entry. */
|
|
GetFieldValue(pQueue, LIST_ENTRY, LIST_ENTRY_FIELD_NEXT, pNext);
|
|
|
|
if (pNext != pQueue) {
|
|
|
|
/* Assume this is a DESCR (not an ENTRY) and look for the code. */
|
|
GetFieldValue(pNext, CONN_DESCR, CONN_DESCR_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != CVY_DESCCODE) {
|
|
|
|
/* Assume this points to an ENTRY and look for the code. */
|
|
GetFieldValue(pNext, CONN_ENTRY, CONN_ENTRY_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != CVY_ENTRCODE) {
|
|
|
|
/* Adjust for the size of a LIST_ENTRY and see if we get an ENTRY. */
|
|
pAddr = pNext - dwLinkSize;
|
|
|
|
/* Assume this points to an entry and look for the code. */
|
|
GetFieldValue(pAddr, CONN_ENTRY, CONN_ENTRY_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != CVY_ENTRCODE) {
|
|
|
|
dprintf("Invalid NLB connection queue pointer.\n");
|
|
|
|
} else {
|
|
|
|
dprintf("Traversing a connection entry queue (Recovery/Timeout).\n");
|
|
|
|
while ((pNext != pQueue) && dwMaxEntries && !CheckControlC()) {
|
|
|
|
dprintf("\nQueue entry 0x%p\n", pNext);
|
|
|
|
/* Print the connection descriptor. */
|
|
PrintConnectionDescriptor(pAddr);
|
|
|
|
/* Get the Next pointer from the list entry. */
|
|
GetFieldValue(pNext, LIST_ENTRY, LIST_ENTRY_FIELD_NEXT, pAddr);
|
|
|
|
/* Save the next pointer for "end of list" comparison. */
|
|
pNext = pAddr;
|
|
|
|
/* Adjust for the size of a LIST_ENTRY to get a pointer to the ENTRY. */
|
|
pAddr = pNext - dwLinkSize;
|
|
|
|
/* Decrement the number of entries we're still permitted to print. */
|
|
dwMaxEntries--;
|
|
}
|
|
|
|
if (pNext == pQueue)
|
|
dprintf("\nNote: End of queue.\n");
|
|
else
|
|
dprintf("\nNote: Entries remaining.\n");
|
|
}
|
|
|
|
} else {
|
|
|
|
dprintf("Traversing a connection entry queue (Bin/Dirty).\n");
|
|
|
|
/* The first descriptor to print is the one the next pointer points to. */
|
|
pAddr = pNext;
|
|
|
|
while ((pNext != pQueue) && dwMaxEntries && !CheckControlC()) {
|
|
|
|
dprintf("\nQueue entry 0x%p\n", pAddr);
|
|
|
|
/* Print the connection descriptor. */
|
|
PrintConnectionDescriptor(pAddr);
|
|
|
|
/* Get the Next pointer from the list entry. */
|
|
GetFieldValue(pNext, LIST_ENTRY, LIST_ENTRY_FIELD_NEXT, pAddr);
|
|
|
|
/* Save the next pointer for "end of list" comparison. */
|
|
pNext = pAddr;
|
|
|
|
/* Decrement the number of entries we're still permitted to print. */
|
|
dwMaxEntries--;
|
|
}
|
|
|
|
if (pNext == pQueue)
|
|
dprintf("\nNote: End of queue.\n");
|
|
else
|
|
dprintf("\nNote: Entries remaining.\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dprintf("Traversing a connection descriptor queue (Free/Conn).\n");
|
|
|
|
/* Get the field offset of the ENTRY, which is a member of the DESCR. */
|
|
if (GetFieldOffset(CONN_DESCR, CONN_DESCR_FIELD_ENTRY, &dwEntryOffset))
|
|
dprintf("Can't get offset of %s in %s\n", CONN_DESCR_FIELD_ENTRY, CONN_DESCR);
|
|
else {
|
|
|
|
/* The first descriptor to print is the ENTRY member of the DESCR. */
|
|
pAddr = pNext + dwEntryOffset;
|
|
|
|
while ((pNext != pQueue) && dwMaxEntries && !CheckControlC()) {
|
|
|
|
dprintf("\nQueue entry 0x%p\n", pNext);
|
|
|
|
/* Print the connection descriptor. */
|
|
PrintConnectionDescriptor(pAddr);
|
|
|
|
/* Get the Next pointer from the list entry. */
|
|
GetFieldValue(pNext, LIST_ENTRY, LIST_ENTRY_FIELD_NEXT, pAddr);
|
|
|
|
/* Save the next pointer for "end of list" comparison. */
|
|
pNext = pAddr;
|
|
|
|
/* Find the next descriptor pointer. */
|
|
pAddr = pNext + dwEntryOffset;
|
|
|
|
/* Decrement the number of entries we're still permitted to print. */
|
|
dwMaxEntries--;
|
|
}
|
|
|
|
if (pNext == pQueue)
|
|
dprintf("\nNote: End of queue.\n");
|
|
else
|
|
dprintf("\nNote: Entries remaining.\n");
|
|
|
|
}
|
|
}
|
|
} else {
|
|
|
|
dprintf("Queue is empty.\n");
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintGlobalQueue
|
|
* Description: Prints MaxEntries entries in a global connection descriptor queue.
|
|
* Author: Created by shouse, 4.15.02
|
|
*/
|
|
void PrintGlobalQueue (ULONG64 pQueue, ULONG dwIndex, ULONG dwMaxEntries) {
|
|
ULONG64 pAddr;
|
|
ULONG64 pNext;
|
|
ULONG dwLinkSize;
|
|
ULONG dwQueueSize;
|
|
ULONG dwValue;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pQueue) {
|
|
dprintf("Error: Queue is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
/* Get the size of a queue link. */
|
|
dwLinkSize = GetTypeSize(LIST_ENTRY);
|
|
|
|
/* Get the size of a global connection queue. */
|
|
dwQueueSize = GetTypeSize(GLOBAL_CONN_QUEUE);
|
|
|
|
/* Use the index and the size of a GLOBAL_CONN_QUEUE to move to the
|
|
indicated index in an array of queues. If no index was provided,
|
|
dwIndex is zero so the queue pointer is unchanged. */
|
|
pQueue += (dwQueueSize * dwIndex);
|
|
|
|
/* Get the Next pointer from the list entry. */
|
|
GetFieldValue(pQueue, GLOBAL_CONN_QUEUE, GLOBAL_CONN_QUEUE_FIELD_LENGTH, dwValue);
|
|
|
|
dprintf("Queue has %u entry(ies).\n", dwValue);
|
|
|
|
/* Get the field offset of the QUEUE and add it to the queue pointer. */
|
|
if (GetFieldOffset(GLOBAL_CONN_QUEUE, GLOBAL_CONN_QUEUE_FIELD_QUEUE, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", GLOBAL_CONN_QUEUE_FIELD_QUEUE, GLOBAL_CONN_QUEUE);
|
|
else
|
|
pQueue += dwValue;
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the Next pointer from the list entry. */
|
|
GetFieldValue(pQueue, LIST_ENTRY, LIST_ENTRY_FIELD_NEXT, pNext);
|
|
|
|
if (pNext != pQueue) {
|
|
|
|
/* Assume this is a PENDING_ENTRY (not a CONN_ENTRY) and look for the code. */
|
|
GetFieldValue(pNext, PENDING_ENTRY, PENDING_ENTRY_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != CVY_PENDINGCODE) {
|
|
|
|
/* Adjust for the size of two LIST_ENTRYs and see if we get an CONN_ENTRY. */
|
|
pAddr = pNext - (2 * dwLinkSize);
|
|
|
|
/* Assume this points to a CONN_ENTRY and look for the code. */
|
|
GetFieldValue(pAddr, CONN_ENTRY, CONN_ENTRY_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != CVY_ENTRCODE) {
|
|
|
|
dprintf("Invalid NLB connection queue pointer.\n");
|
|
|
|
} else {
|
|
|
|
dprintf("Traversing an established connection entry queue.\n");
|
|
|
|
while ((pNext != pQueue) && dwMaxEntries && !CheckControlC()) {
|
|
|
|
dprintf("\nQueue entry 0x%p\n", pNext);
|
|
|
|
/* Print the connection descriptor. */
|
|
PrintConnectionDescriptor(pAddr);
|
|
|
|
/* Get the Next pointer from the list entry. */
|
|
GetFieldValue(pNext, LIST_ENTRY, LIST_ENTRY_FIELD_NEXT, pAddr);
|
|
|
|
/* Save the next pointer for "end of list" comparison. */
|
|
pNext = pAddr;
|
|
|
|
/* Adjust for the size of a LIST_ENTRY to get a pointer to the ENTRY. */
|
|
pAddr = pNext - (2 * dwLinkSize);
|
|
|
|
/* Decrement the number of entries we're still permitted to print. */
|
|
dwMaxEntries--;
|
|
}
|
|
|
|
if (pNext == pQueue)
|
|
dprintf("\nNote: End of queue.\n");
|
|
else
|
|
dprintf("\nNote: Entries remaining.\n");
|
|
}
|
|
|
|
} else {
|
|
|
|
dprintf("Traversing a pending connection entry queue.\n");
|
|
|
|
while ((pNext != pQueue) && dwMaxEntries && !CheckControlC()) {
|
|
|
|
dprintf("\nQueue entry 0x%p\n", pNext);
|
|
|
|
/* Print the pending connection descriptor. */
|
|
PrintPendingConnection(pNext);
|
|
|
|
/* Get the Next pointer from the list entry. */
|
|
GetFieldValue(pNext, LIST_ENTRY, LIST_ENTRY_FIELD_NEXT, pAddr);
|
|
|
|
/* Decrement the number of entries we're still permitted to print. */
|
|
dwMaxEntries--;
|
|
}
|
|
|
|
if (pNext == pQueue)
|
|
dprintf("\nNote: End of queue.\n");
|
|
else
|
|
dprintf("\nNote: Entries remaining.\n");
|
|
}
|
|
|
|
} else {
|
|
|
|
dprintf("Queue is empty.\n");
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintHash
|
|
* Description: Extracts the network data previously parsed from an NDIS_PACKET and calls PrintFilter
|
|
* to determine whether NLB will accept this packet.
|
|
* Author: Created by shouse, 4.15.01
|
|
*/
|
|
void PrintHash (ULONG64 pContext, PNETWORK_DATA pnd) {
|
|
ULONG dwValue;
|
|
|
|
/* Make sure the load address is non-NULL. */
|
|
if (!pContext) {
|
|
dprintf("Error: NLB context block is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
/* Get the MAIN_CTXT_CODE from the structure to make sure that this address
|
|
indeed points to a valid NLB context block. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != MAIN_CTXT_CODE) {
|
|
dprintf(" Error: Invalid NLB context block. Wrong code found (0x%08x).\n", dwValue);
|
|
return;
|
|
}
|
|
|
|
/* If the packet is marked invalid by the parsing implementation, then we don't want
|
|
to even bother to try and filter it, as the parsed information may not be correct. */
|
|
if (!pnd->bValid) {
|
|
dprintf("This packet was marked INVALID during parsing and therefore cannot be reliably filtered.\n");
|
|
return;
|
|
}
|
|
|
|
switch(pnd->EtherFrameType) {
|
|
case TCPIP_IP_SIG:
|
|
{
|
|
/* Grab the client and server IP addresses from the network data. */
|
|
ULONG dwClientIPAddress = pnd->SourceIPAddr;
|
|
ULONG dwServerIPAddress = pnd->DestIPAddr;
|
|
|
|
switch((int)pnd->Protocol) {
|
|
case TCPIP_PROTOCOL_ICMP:
|
|
/* ICMP may be filtered if the registry key for ICMP filtering is set. */
|
|
PrintFilter(pContext, dwClientIPAddress, 0, dwServerIPAddress, 0, TCPIP_PROTOCOL_ICMP, NLB_FILTER_FLAGS_CONN_DATA);
|
|
break;
|
|
case TCPIP_PROTOCOL_IGMP:
|
|
/* IGMP packets are never filtered by NLB. */
|
|
dprintf("Accept: IGMP traffic is not filtered by NLB.\n");
|
|
break;
|
|
case TCPIP_PROTOCOL_TCP:
|
|
{
|
|
/* Extract the client and server ports. */
|
|
ULONG dwClientPort = pnd->SourcePort;
|
|
ULONG dwServerPort = pnd->DestPort;
|
|
|
|
/* By default, assume this is a data packet. */
|
|
UCHAR cFlags = NLB_FILTER_FLAGS_CONN_DATA;
|
|
|
|
/* Convert the actual TCP flags to values the load module understands
|
|
(generic - not specific to TCP necessarily). */
|
|
if (pnd->TCPFlags & TCP_FLAG_SYN)
|
|
cFlags |= NLB_FILTER_FLAGS_CONN_UP;
|
|
else if (pnd->TCPFlags & TCP_FLAG_FIN)
|
|
cFlags |= NLB_FILTER_FLAGS_CONN_DOWN;
|
|
else if (pnd->TCPFlags & TCP_FLAG_RST)
|
|
cFlags |= NLB_FILTER_FLAGS_CONN_RESET;
|
|
|
|
/* Translate TCP 1723 to PPTP. */
|
|
if (dwServerPort == PPTP_CTRL_PORT)
|
|
/* Call the filter function with the collected parameters. */
|
|
PrintFilter(pContext, dwClientIPAddress, dwClientPort, dwServerIPAddress, dwServerPort, TCPIP_PROTOCOL_PPTP, cFlags);
|
|
else
|
|
/* Call the filter function with the collected parameters. */
|
|
PrintFilter(pContext, dwClientIPAddress, dwClientPort, dwServerIPAddress, dwServerPort, TCPIP_PROTOCOL_TCP, cFlags);
|
|
|
|
break;
|
|
}
|
|
case TCPIP_PROTOCOL_UDP:
|
|
{
|
|
/* Extract the client and server ports. */
|
|
ULONG dwClientPort = pnd->SourcePort;
|
|
ULONG dwServerPort = pnd->DestPort;
|
|
|
|
/* By default, assume this is a data packet. */
|
|
UCHAR cFlags = NLB_FILTER_FLAGS_CONN_DATA;
|
|
|
|
/* If this is an IKE initial contact packet, set the CONN_UP flag. The parsing functions
|
|
should special case UDP 500 as IPSec control traffic and set the initial contact flag
|
|
appropriately. */
|
|
if ((dwServerPort == IPSEC_CTRL_PORT) && (pnd->IPSecInitialContact))
|
|
cFlags |= NLB_FILTER_FLAGS_CONN_UP;
|
|
|
|
|
|
|
|
// Re-do IPSec IC MMSA parsing
|
|
|
|
|
|
|
|
/* Translate UDP 500/4500 to IPSec. */
|
|
if ((dwServerPort == IPSEC_CTRL_PORT) || (dwServerPort == IPSEC_NAT_PORT))
|
|
/* Call the filter function with the collected parameters. */
|
|
PrintFilter(pContext, dwClientIPAddress, dwClientPort, dwServerIPAddress, dwServerPort, TCPIP_PROTOCOL_IPSEC1, cFlags);
|
|
else
|
|
/* Call the filter function with the collected parameters. */
|
|
PrintFilter(pContext, dwClientIPAddress, dwClientPort, dwServerIPAddress, dwServerPort, TCPIP_PROTOCOL_UDP, cFlags);
|
|
|
|
break;
|
|
}
|
|
case TCPIP_PROTOCOL_GRE:
|
|
/* GRE packets do not have ports and instead arbitrarily use 0 and 0 as the client and server ports,
|
|
respectively. Further, GRE packets are always data, as they are always part of a previously
|
|
established PPTP tunnel, so the flags are always DATA. */
|
|
PrintFilter(pContext, dwClientIPAddress, PPTP_CTRL_PORT, dwServerIPAddress, PPTP_CTRL_PORT, TCPIP_PROTOCOL_GRE, NLB_FILTER_FLAGS_CONN_DATA);
|
|
break;
|
|
case TCPIP_PROTOCOL_IPSEC1:
|
|
case TCPIP_PROTOCOL_IPSEC2:
|
|
/* IPSec (AH/ESP) packets are always treated as data - establishment of IPSec connections is done
|
|
via UDP packet, so any traffic actually utilizing the IPSec protocol(s) are DATA packets.
|
|
Further, both ports are hard-coded to 500 because the only data traffic that will traverse this
|
|
protocol is traffic for clients NOT behind a NAT, in which case the source port is also always
|
|
500. For clients behind a NAT, the data traffic is encapsulated in UDP with an arbitrary source
|
|
port and a destination port of 500. So, here we can always assume server and client ports of 500. */
|
|
PrintFilter(pContext, dwClientIPAddress, IPSEC_CTRL_PORT, dwServerIPAddress, IPSEC_CTRL_PORT, TCPIP_PROTOCOL_IPSEC1, NLB_FILTER_FLAGS_CONN_DATA);
|
|
break;
|
|
default:
|
|
/* NLB is essentially a TCP/IP filter, so unknown protocols are NOT filtered. */
|
|
dprintf("Accept: Unknown protocol.\n");
|
|
}
|
|
|
|
return;
|
|
}
|
|
case TCPIP_ARP_SIG:
|
|
dprintf("Accept: Received ARPs are never filtered by NLB.\n");
|
|
return;
|
|
case MAIN_FRAME_SIG:
|
|
case MAIN_FRAME_SIG_OLD:
|
|
{
|
|
/* Check to see whether this host is started or not. */
|
|
{
|
|
ULONG dwEnabled;
|
|
|
|
/* Get the convoy enabled status. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_ENABLED, dwEnabled);
|
|
|
|
/* If the cluster is not operational, which can happen, for example as a result of a wlbs.exe
|
|
command such as "wlbs stop", or as a result of bad parameter settings, then drop all traffic
|
|
that does not meet the above conditions. */
|
|
if (!dwEnabled) {
|
|
dprintf("Reject: This host is currently stopped.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Check to see whether this heartbeat is intended for this cluster. */
|
|
{
|
|
ULONG dwClusterIP;
|
|
|
|
/* Get the cluster IP address. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CL_IP_ADDR, dwClusterIP);
|
|
|
|
/* If the cluster IP address in the heartbeat is zero, or if it is for a cluster other than
|
|
this one, then reject it. */
|
|
if (!pnd->HBCluster || (pnd->HBCluster != dwClusterIP)) {
|
|
dprintf("Reject: This is not a heartbeat for this NLB cluster.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* If the recipient host ID is invalid, then reject the heartbeat. */
|
|
if (!pnd->HBHost || (pnd->HBHost > CVY_MAX_HOSTS)) {
|
|
dprintf("Reject: The host ID specified in the heartbeat is invalid.\n");
|
|
return;
|
|
}
|
|
|
|
dprintf("Accept: Heartbeat directed to this NLB cluster.\n");
|
|
return;
|
|
}
|
|
default:
|
|
dprintf("Accept: Unknown frame type.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintFilter
|
|
* Description: Searches the given load module to determine whether NLB will accept this packet.
|
|
* If state for this packet already exists, it is printed.
|
|
* Author: Created by shouse, 4.15.01
|
|
*/
|
|
void PrintFilter (ULONG64 pContext, ULONG dwClientIPAddress, ULONG dwClientPort, ULONG dwServerIPAddress, ULONG dwServerPort, USHORT wProtocol, UCHAR cFlags) {
|
|
ULONG64 pLoad;
|
|
ULONG64 pParams;
|
|
ULONG64 pHooks;
|
|
ULONG64 pFilter;
|
|
ULONG64 pAddr;
|
|
ULONG dwValue;
|
|
ULONG dwReverse = FALSE;
|
|
BOOL bRefused = FALSE;
|
|
BOOL bTeaming = FALSE;
|
|
|
|
/* Make sure the load address is non-NULL. */
|
|
if (!pContext) {
|
|
dprintf("Error: NLB context block is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
dprintf("Note: All filtering conclusions derived herein assume RECEIVE packet semantics.\n");
|
|
dprintf("\n");
|
|
dprintf("Hashing connection tuple (0x%08x, %u, 0x%08x, %u, ", dwClientIPAddress, dwClientPort, dwServerIPAddress, dwServerPort);
|
|
|
|
switch (wProtocol) {
|
|
case TCPIP_PROTOCOL_TCP:
|
|
dprintf("%s, %s)\n\n", "TCP", ConnectionFlagsToString(cFlags));
|
|
break;
|
|
case TCPIP_PROTOCOL_UDP:
|
|
dprintf("%s)\n\n", "UDP");
|
|
cFlags = NLB_FILTER_FLAGS_CONN_DATA;
|
|
break;
|
|
case TCPIP_PROTOCOL_IPSEC1:
|
|
dprintf("%s, %s)\n\n", "IPSec", ConnectionFlagsToString(cFlags));
|
|
break;
|
|
case TCPIP_PROTOCOL_GRE:
|
|
dprintf("%s)\n\n", "GRE");
|
|
cFlags = NLB_FILTER_FLAGS_CONN_DATA;
|
|
break;
|
|
case TCPIP_PROTOCOL_PPTP:
|
|
dprintf("%s, %s)\n\n", "PPTP", ConnectionFlagsToString(cFlags));
|
|
break;
|
|
case TCPIP_PROTOCOL_ICMP:
|
|
dprintf("%s)\n\n", "ICMP");
|
|
break;
|
|
default:
|
|
dprintf("%s)\n\n", "Unknown");
|
|
break;
|
|
}
|
|
|
|
/* Get the MAIN_CTXT_CODE from the structure to make sure that this address
|
|
indeed points to a valid NLB context block. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != MAIN_CTXT_CODE) {
|
|
dprintf(" Error: Invalid NLB context block. Wrong code found (0x%08x).\n", dwValue);
|
|
return;
|
|
}
|
|
|
|
/* Get the address of the global variable containing hook table. */
|
|
pHooks = GetExpression(UNIV_HOOKS);
|
|
|
|
if (!pHooks) {
|
|
ErrorCheckSymbols(UNIV_HOOKS);
|
|
return;
|
|
}
|
|
|
|
/* Get the offset of the filter hook sub-structure. */
|
|
if (GetFieldOffset(HOOK_TABLE, HOOK_TABLE_FIELD_FILTER_HOOK, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", HOOK_TABLE_FIELD_FILTER_HOOK, HOOK_TABLE);
|
|
else {
|
|
pFilter = pHooks + dwValue;
|
|
|
|
/* Find out whether or not there is an operation in progress. */
|
|
GetFieldValue(pFilter, FILTER_HOOK_TABLE, FILTER_HOOK_TABLE_FIELD_OPERATION, dwValue);
|
|
|
|
switch (dwValue) {
|
|
case HOOK_OPERATION_REGISTERING:
|
|
dprintf("Note: A register operation is currently underway for the NLB filter hook interface.\n");
|
|
break;
|
|
case HOOK_OPERATION_DEREGISTERING:
|
|
dprintf("Note: A de-register operation is currently underway for the NLB filter hook interface.\n");
|
|
break;
|
|
}
|
|
|
|
/* Get the offset of the receive filter hook. */
|
|
if (GetFieldOffset(FILTER_HOOK_TABLE, FILTER_HOOK_TABLE_FIELD_RECEIVE_HOOK, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", FILTER_HOOK_TABLE_FIELD_RECEIVE_HOOK, FILTER_HOOK_TABLE);
|
|
else {
|
|
pAddr = pFilter + dwValue;
|
|
|
|
/* Find out whether or not this hook is registered. */
|
|
GetFieldValue(pAddr, HOOK, HOOK_FIELD_REGISTERED, dwValue);
|
|
|
|
/* If a receive hook is registered, print a warning that our results here
|
|
_might_ not be accurate, depending on the result of invoking the hook. */
|
|
if (dwValue) {
|
|
dprintf("Note: A receive filter hook is currently registered. The filtering conclusions derived herein may or may not\n");
|
|
dprintf(" be accurate, as the filtering directive returned by the registered hook function cannot be anticipated.\n");
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Get the pointer to the NLB load. */
|
|
if (GetFieldOffset(MAIN_CTXT, MAIN_CTXT_FIELD_LOAD, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", MAIN_CTXT_FIELD_LOAD, MAIN_CTXT);
|
|
else {
|
|
pLoad = pContext + dwValue;
|
|
|
|
/* Get the LOAD_CTXT_CODE from the structure to make sure that this address
|
|
indeed points to a valid NLB load block. */
|
|
GetFieldValue(pLoad, LOAD_CTXT, LOAD_CTXT_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != LOAD_CTXT_CODE) {
|
|
dprintf(" Error: Invalid NLB load block. Wrong code found (0x%08x).\n", dwValue);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Get the pointer to the NLB parameters. */
|
|
if (GetFieldOffset(MAIN_CTXT, MAIN_CTXT_FIELD_PARAMS, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", MAIN_CTXT_FIELD_PARAMS, MAIN_CTXT);
|
|
else {
|
|
pParams = pContext + dwValue;
|
|
|
|
/* Get the validity of the NLB parameter block. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_PARAMS_VALID, dwValue);
|
|
|
|
if (!dwValue)
|
|
dprintf("Warning: Parameters block is marked invalid. Results may be skewed.\n");
|
|
}
|
|
|
|
/* Check for remote control packets. */
|
|
{
|
|
ULONG dwRemoteControlEnabled;
|
|
ULONG dwRemoteControlPort;
|
|
ULONG dwClusterIPAddress;
|
|
|
|
/* Get the remote control enabled flag. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_REMOTE_CONTROL_ENABLED, dwRemoteControlEnabled);
|
|
|
|
/* Get the remote control port. */
|
|
GetFieldValue(pParams, CVY_PARAMS, CVY_PARAMS_FIELD_REMOTE_CONTROL_PORT, dwRemoteControlPort);
|
|
|
|
/* Get the cluster IP address. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CL_IP_ADDR, dwClusterIPAddress);
|
|
|
|
/* First check for remote control packets, which are always UDP and are always allowed to pass.
|
|
However, if we are supposed to ignore remote control traffic and load balance instead, then
|
|
we let the load module tell us whether or not to take the packet. */
|
|
if (wProtocol == TCPIP_PROTOCOL_UDP) {
|
|
/* If the client UDP port is the remote control port, then this is a remote control
|
|
response from another NLB cluster host. These are always allowed to pass. */
|
|
if (dwClientPort == dwRemoteControlPort || dwClientPort == CVY_DEF_RCT_PORT_OLD) {
|
|
dprintf("Accept: This packet is an NLB remote control response.\n");
|
|
return;
|
|
/* Otherwise, if the server UDP port is the remote control port, then this is an incoming
|
|
remote control request from another NLB cluster host. These are always allowed to pass. */
|
|
} else if (dwRemoteControlEnabled &&
|
|
(dwServerPort == dwRemoteControlPort || dwServerPort == CVY_DEF_RCT_PORT_OLD) &&
|
|
(dwServerIPAddress == dwClusterIPAddress || dwServerIPAddress == TCPIP_BCAST_ADDR)) {
|
|
dprintf("Accept: This packet is an NLB remote control request.\n");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check for specialized IP address conditions. */
|
|
{
|
|
ULONG dwClusterIP;
|
|
ULONG dwClusterBcastIP;
|
|
ULONG dwDedicatedIP;
|
|
ULONG dwDedicatedBcastIP;
|
|
|
|
/* Get the cluster IP address. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CL_IP_ADDR, dwClusterIP);
|
|
|
|
/* Get the dedicated IP address. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_DED_IP_ADDR, dwDedicatedIP);
|
|
|
|
/* Get the cluster broadcast IP address. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CL_BROADCAST, dwClusterBcastIP);
|
|
|
|
/* Get the dedicated broadcast IP address. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_DED_BROADCAST, dwDedicatedBcastIP);
|
|
|
|
/* Check for traffic destined for the dedicated IP address of this host.
|
|
These packets are always allowed to pass. */
|
|
if (dwServerIPAddress == dwDedicatedIP) {
|
|
dprintf("Accept: This packet is directed to this host's dedicated IP address.\n");
|
|
return;
|
|
}
|
|
|
|
/* Check for traffic destined for the cluster or dedicated broadcast IP addresses.
|
|
These packets are always allowed to pass. */
|
|
if (dwServerIPAddress == dwDedicatedBcastIP || dwServerIPAddress == dwClusterBcastIP) {
|
|
dprintf("Accept: This packet is directed to the cluster or dedicated broadcast IP address.\n");
|
|
return;
|
|
}
|
|
|
|
/* Check for passthru packets. When the cluster IP address has not been specified, the
|
|
cluster moves into passthru mode, in which it passes up ALL packets received. */
|
|
if (dwClusterIP == 0) {
|
|
dprintf("Accept: This host is misconfigured and therefore operating in pass-through mode.\n");
|
|
return;
|
|
}
|
|
|
|
/* Get the pointer to the DIP list. */
|
|
if (GetFieldOffset(MAIN_CTXT, MAIN_CTXT_FIELD_DIP_LIST, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", MAIN_CTXT_FIELD_DIP_LIST, MAIN_CTXT);
|
|
else {
|
|
pAddr = pContext + dwValue;
|
|
|
|
/* Before we load-balance this packet, check to see whether or not its destined for
|
|
the dedicated IP address of another NLB host in our cluster. If it is, drop it. */
|
|
if (DipListCheckItem(pAddr, dwServerIPAddress)) {
|
|
dprintf("Drop: This packet is directed to the dedicated IP address of another NLB host.\n");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check to see whether this host is started or not. */
|
|
{
|
|
ULONG dwEnabled;
|
|
|
|
/* Get the convoy enabled status. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_ENABLED, dwEnabled);
|
|
|
|
/* If the cluster is not operational, which can happen, for example as a result of a wlbs.exe
|
|
command such as "wlbs stop", or as a result of bad parameter settings, then drop all traffic
|
|
that does not meet the above conditions. */
|
|
if (!dwEnabled) {
|
|
dprintf("Reject: This host is currently stopped.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* If this is an ICMP filter request, whether or not its filtered at all depends on the FilterICMP
|
|
registry setting. If we're not filtering ICMP, return ACCEPT now; otherwise, ICMP is filtered
|
|
like UDP with no port information - fall through and consult the load module. */
|
|
if (wProtocol == TCPIP_PROTOCOL_ICMP)
|
|
{
|
|
ULONG dwFilterICMP;
|
|
|
|
/* Get the convoy enabled status. */
|
|
GetFieldValue(pParams, MAIN_CTXT, CVY_PARAMS_FIELD_FILTER_ICMP, dwFilterICMP);
|
|
|
|
/* If we are filtering ICMP, change the protocol to UDP and the ports to 0, 0 before continuing. */
|
|
if (dwFilterICMP) {
|
|
wProtocol = TCPIP_PROTOCOL_UDP;
|
|
dwClientPort = 0;
|
|
dwServerPort = 0;
|
|
/* Otherwise, return ACCEPT now and bail out. */
|
|
} else {
|
|
dprintf("Accept: ICMP traffic is not being filtered by NLB.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Get the reverse hashing flag. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_REVERSE_HASH, dwReverse);
|
|
|
|
/* Get the appropriate load module by checking for BDA teaming. */
|
|
bTeaming = AcquireLoad(pContext, &pLoad, &bRefused);
|
|
|
|
/* If BDA teaming has refused the packet, drop it. */
|
|
if (bRefused) {
|
|
dprintf("Reject: BDA teaming has refused acceptance of this packet.\n");
|
|
return;
|
|
}
|
|
|
|
/* If teaming is configured, let the user know what's going on. */
|
|
if (bTeaming) {
|
|
dprintf("Note: BDA teaming is configured on this instance of NLB. The filtering conclusions derived herein will utilize the\n");
|
|
dprintf(" load module state of the BDA team master and may not be accurate if a BDA teaming operation(s) are in progress.\n");
|
|
dprintf("\n");
|
|
}
|
|
|
|
/* Consult the load module. */
|
|
LoadFilter(pLoad, dwServerIPAddress, dwServerPort, dwClientIPAddress, dwClientPort, wProtocol, cFlags, bTeaming, (BOOL)dwReverse);
|
|
}
|
|
|
|
/*
|
|
* Function: PrintRemoteControl
|
|
* Description: Print the properties associated with a remote control packet.
|
|
* Args: PNETWORK_DATA pnd - data structure where the extracted properties have been stored,
|
|
* populated by the function PopulateRemoteControl.
|
|
* Author: Created by chrisdar 2001.11.02
|
|
*/
|
|
void PrintRemoteControl (PNETWORK_DATA pnd)
|
|
{
|
|
char* pszIOCTL = NULL;
|
|
struct in_addr in;
|
|
char *pszIPAddr = NULL;
|
|
char *pszRCDirection = NULL;
|
|
|
|
switch(pnd->RCIoctrl)
|
|
{
|
|
case IOCTL_CVY_CLUSTER_ON:
|
|
pszIOCTL = STR_IOCTL_CVY_CLUSTER_ON;
|
|
break;
|
|
case IOCTL_CVY_CLUSTER_OFF:
|
|
pszIOCTL = STR_IOCTL_CVY_CLUSTER_OFF;
|
|
break;
|
|
case IOCTL_CVY_PORT_ON:
|
|
pszIOCTL = STR_IOCTL_CVY_PORT_ON;
|
|
break;
|
|
case IOCTL_CVY_PORT_OFF:
|
|
pszIOCTL = STR_IOCTL_CVY_PORT_OFF;
|
|
break;
|
|
case IOCTL_CVY_QUERY:
|
|
pszIOCTL = STR_IOCTL_CVY_QUERY;
|
|
break;
|
|
case IOCTL_CVY_RELOAD:
|
|
pszIOCTL = STR_IOCTL_CVY_RELOAD;
|
|
break;
|
|
case IOCTL_CVY_PORT_SET:
|
|
pszIOCTL = STR_IOCTL_CVY_PORT_SET;
|
|
break;
|
|
case IOCTL_CVY_PORT_DRAIN:
|
|
pszIOCTL = STR_IOCTL_CVY_PORT_DRAIN;
|
|
break;
|
|
case IOCTL_CVY_CLUSTER_DRAIN:
|
|
pszIOCTL = STR_IOCTL_CVY_CLUSTER_DRAIN;
|
|
break;
|
|
case IOCTL_CVY_CLUSTER_PLUG:
|
|
pszIOCTL = STR_IOCTL_CVY_CLUSTER_PLUG;
|
|
break;
|
|
case IOCTL_CVY_CLUSTER_SUSPEND:
|
|
pszIOCTL = STR_IOCTL_CVY_CLUSTER_SUSPEND;
|
|
break;
|
|
case IOCTL_CVY_CLUSTER_RESUME:
|
|
pszIOCTL = STR_IOCTL_CVY_CLUSTER_RESUME;
|
|
break;
|
|
case IOCTL_CVY_QUERY_FILTER:
|
|
pszIOCTL = STR_IOCTL_CVY_QUERY_FILTER;
|
|
break;
|
|
case IOCTL_CVY_QUERY_PORT_STATE:
|
|
pszIOCTL = STR_IOCTL_CVY_QUERY_PORT_STATE;
|
|
break;
|
|
case IOCTL_CVY_QUERY_PARAMS:
|
|
pszIOCTL = STR_IOCTL_CVY_QUERY_PARAMS;
|
|
break;
|
|
case IOCTL_CVY_QUERY_BDA_TEAMING:
|
|
pszIOCTL = STR_IOCTL_CVY_QUERY_BDA_TEAMING;
|
|
break;
|
|
default:
|
|
pszIOCTL = "Unknown";
|
|
}
|
|
|
|
switch(pnd->RemoteControl)
|
|
{
|
|
case NLB_RC_PACKET_NO:
|
|
pszRCDirection = STR_NLB_RC_PACKET_NO;
|
|
break;
|
|
case NLB_RC_PACKET_AMBIGUOUS:
|
|
pszRCDirection = STR_NLB_RC_PACKET_AMBIGUOUS;
|
|
break;
|
|
case NLB_RC_PACKET_REQUEST:
|
|
pszRCDirection = STR_NLB_RC_PACKET_REQUEST;
|
|
break;
|
|
case NLB_RC_PACKET_REPLY:
|
|
pszRCDirection = STR_NLB_RC_PACKET_REPLY;
|
|
}
|
|
|
|
|
|
dprintf(" Remote Control information\n");
|
|
dprintf(" Direction: %s\n" , pszRCDirection ? pszRCDirection : "");
|
|
dprintf(" Code: 0x%08x\n" , pnd->RCCode);
|
|
dprintf(" Version: 0x%08x\n" , pnd->RCVersion);
|
|
|
|
//
|
|
// Treat pnd->RCHost as an IP (even though it is optionally a host ID too)
|
|
// How could we distinguish one from the other without knowing the DIPs?
|
|
//
|
|
in.S_un.S_addr = pnd->RCHost;
|
|
pszIPAddr = inet_ntoa(in);
|
|
dprintf(" Host: 0x%08x (%s)\n", pnd->RCHost , pszIPAddr ? pszIPAddr : "");
|
|
|
|
in.S_un.S_addr = pnd->RCCluster;
|
|
pszIPAddr = inet_ntoa(in);
|
|
dprintf(" Cluster: 0x%08x (%s)\n", pnd->RCCluster, pszIPAddr ? pszIPAddr : "");
|
|
|
|
in.S_un.S_addr = pnd->RCAddr;
|
|
pszIPAddr = inet_ntoa(in);
|
|
dprintf(" Address: 0x%08x (%s)\n", pnd->RCAddr , pszIPAddr ? pszIPAddr : "");
|
|
|
|
dprintf(" ID: 0x%08x\n" , pnd->RCId);
|
|
dprintf(" IOCTL: 0x%08x (%s)\n", pnd->RCIoctrl , pszIOCTL);
|
|
}
|
|
|
|
/*
|
|
* Function: PrintICMP
|
|
* Description: Print the properties associated with an ICMP packet.
|
|
* Args: PNETWORK_DATA pnd - data structure where the extracted properties have been stored,
|
|
* populated by the function PopulateICMP.
|
|
* Author: Created by chrisdar 2001.11.02
|
|
*/
|
|
void PrintICMP (PNETWORK_DATA pnd)
|
|
{
|
|
//
|
|
// Just a stub for now. There is nothing in the payload that we are currently interested in.
|
|
//
|
|
}
|
|
|
|
/*
|
|
* Function: PrintIGMP
|
|
* Description: Print the properties associated with an IGMP packet.
|
|
* Args: PNETWORK_DATA pnd - data structure where the extracted properties have been stored,
|
|
* populated by the function PopulateIGMP.
|
|
* Author: Created by chrisdar 2001.11.02
|
|
*/
|
|
void PrintIGMP (PNETWORK_DATA pnd)
|
|
{
|
|
struct in_addr in;
|
|
char *pszIPaddress = NULL;
|
|
|
|
in.S_un.S_addr = pnd->IGMPGroupIPAddr;
|
|
pszIPaddress = inet_ntoa(in);
|
|
|
|
dprintf(" IGMP information\n");
|
|
dprintf(" Version: 0x%01x\n" , pnd->IGMPVersion);
|
|
dprintf(" Type: 0x%01x (%s)\n", pnd->IGMPType , (1 == pnd->IGMPType) ? "query" : "report");
|
|
dprintf(" Group IP address: 0x%08x (%s)\n" , pnd->IGMPGroupIPAddr, pszIPaddress ? pszIPaddress : "");
|
|
}
|
|
|
|
/*
|
|
* Function: PrintTCP
|
|
* Description: Print the properties associated with an TCP packet.
|
|
* Args: PNETWORK_DATA pnd - data structure where the extracted properties have been stored,
|
|
* populated by the function PopulateTCP.
|
|
* Author: Created by chrisdar 2001.11.02
|
|
*/
|
|
void PrintTCP (PNETWORK_DATA pnd)
|
|
{
|
|
UCHAR ucFlags[7];
|
|
PUCHAR pucFlags = ucFlags;
|
|
int iIdx, iMask;
|
|
|
|
//
|
|
// To print out the flags, set up a string with every possible flag "on".
|
|
// Then "turn off" the flag if in the output string if the flag isn't on.
|
|
// Do this by setting its char to '.'
|
|
//
|
|
strcpy(ucFlags, "UAPRSF");
|
|
iMask = 0x20;
|
|
|
|
// Bound the number of iterations by the mask value as a sanity check that we don't try to divide by 0
|
|
while (*pucFlags && iMask >= 1)
|
|
{
|
|
if (!(pnd->TCPFlags & iMask))
|
|
{
|
|
*pucFlags = '.';
|
|
}
|
|
iMask /= 2;
|
|
pucFlags++;
|
|
}
|
|
|
|
dprintf(" TCP information\n");
|
|
dprintf(" Source port: 0x%04x (%u)\n", pnd->SourcePort, pnd->SourcePort);
|
|
dprintf(" Destination port: 0x%04x (%u)\n", pnd->DestPort , pnd->DestPort);
|
|
dprintf(" Sequence number: 0x%08x (%u)\n", pnd->TCPSeqNum , pnd->TCPSeqNum);
|
|
dprintf(" Ack number: 0x%08x (%u)\n", pnd->TCPAckNum , pnd->TCPAckNum);
|
|
dprintf(" Flags: %s\n" , ucFlags);
|
|
}
|
|
|
|
/*
|
|
* Function: PrintUDP
|
|
* Description: Print the properties associated with an UDP packet.
|
|
* Args: PNETWORK_DATA pnd - data structure where the extracted properties have been stored,
|
|
* populated by the function PopulateUDP.
|
|
* Author: Created by chrisdar 2001.11.02
|
|
*/
|
|
void PrintUDP (PNETWORK_DATA pnd)
|
|
{
|
|
dprintf(" UDP information\n");
|
|
dprintf(" Source port: 0x%04x (%u)\n", pnd->SourcePort, pnd->SourcePort);
|
|
dprintf(" Destination port: 0x%04x (%u)\n", pnd->DestPort , pnd->DestPort);
|
|
|
|
//
|
|
// Is this a remote control packet?
|
|
//
|
|
if (NLB_RC_PACKET_NO != pnd->RemoteControl)
|
|
{
|
|
dprintf("\n");
|
|
PrintRemoteControl(pnd);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintGRE
|
|
* Description: Print the properties associated with an GRE packet.
|
|
* Args: PNETWORK_DATA pnd - data structure where the extracted properties have been stored,
|
|
* populated by the function PopulateGRE.
|
|
* Author: Created by chrisdar 2001.11.02
|
|
*/
|
|
void PrintGRE (PNETWORK_DATA pnd)
|
|
{
|
|
//
|
|
// Just a stub for now. There is nothing in the payload that we are currently interested in.
|
|
//
|
|
}
|
|
|
|
/*
|
|
* Function: PrintIPSec
|
|
* Description: Print the properties associated with an IPSec packet.
|
|
* Args: PNETWORK_DATA pnd - data structure where the extracted properties have been stored,
|
|
* populated by the function PopulateIPSec.
|
|
* Author: Created by chrisdar 2001.11.02
|
|
*/
|
|
void PrintIPSec (PNETWORK_DATA pnd)
|
|
{
|
|
//
|
|
// Just a stub for now. There is nothing in the payload that we are currently interested in.
|
|
//
|
|
}
|
|
|
|
/*
|
|
* Function: PrintIP
|
|
* Description: Print the properties associated with an IP packet.
|
|
* Args: PNETWORK_DATA pnd - data structure where the extracted properties have been stored,
|
|
* populated by the function PopulateIP.
|
|
* Author: Created by chrisdar 2001.11.02
|
|
*/
|
|
void PrintIP (PNETWORK_DATA pnd)
|
|
{
|
|
struct in_addr in;
|
|
char *pszIPaddress = NULL;
|
|
|
|
dprintf(" IP information\n");
|
|
dprintf(" Header length (bytes): 0x%02x (%u)\n", pnd->HeadLen, pnd->HeadLen);
|
|
dprintf(" Total length (bytes): 0x%04x (%u)\n", pnd->TotLen, pnd->TotLen);
|
|
|
|
in.S_un.S_addr = pnd->SourceIPAddr;
|
|
pszIPaddress = inet_ntoa(in);
|
|
|
|
dprintf(" Source IP address: 0x%08x (%s)\n", pnd->SourceIPAddr, pszIPaddress ? pszIPaddress : "");
|
|
|
|
in.S_un.S_addr = pnd->DestIPAddr;
|
|
pszIPaddress = inet_ntoa(in);
|
|
|
|
dprintf(" Destination IP address: 0x%08x (%s)\n", pnd->DestIPAddr, pszIPaddress ? pszIPaddress : "");
|
|
|
|
switch((int) pnd->Protocol)
|
|
{
|
|
case TCPIP_PROTOCOL_ICMP:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", pnd->Protocol, "ICMP");
|
|
PrintICMP(pnd);
|
|
break;
|
|
case TCPIP_PROTOCOL_IGMP:
|
|
dprintf(" Protocol: 0x%02x (%s)\n\n", pnd->Protocol, "IGMP");
|
|
PrintIGMP(pnd);
|
|
break;
|
|
case TCPIP_PROTOCOL_TCP:
|
|
dprintf(" Protocol: 0x%02x (%s)\n\n", pnd->Protocol, "TCP");
|
|
PrintTCP(pnd);
|
|
break;
|
|
case TCPIP_PROTOCOL_UDP:
|
|
dprintf(" Protocol: 0x%02x (%s)\n\n", pnd->Protocol, "UDP");
|
|
PrintUDP(pnd);
|
|
break;
|
|
case TCPIP_PROTOCOL_GRE:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", pnd->Protocol, "GRE");
|
|
PrintGRE(pnd);
|
|
break;
|
|
case TCPIP_PROTOCOL_IPSEC1:
|
|
case TCPIP_PROTOCOL_IPSEC2:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", pnd->Protocol, "IPSec");
|
|
PrintIPSec(pnd);
|
|
break;
|
|
default:
|
|
dprintf(" Protocol: 0x%02x (%s)\n", pnd->Protocol, "Unknown");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintARP
|
|
* Description: Print the properties associated with an ARP packet.
|
|
* Args: PNETWORK_DATA pnd - data structure where the extracted properties have been stored,
|
|
* populated by the function PopulateARP.
|
|
* Author: Created by chrisdar 2001.11.02
|
|
*/
|
|
void PrintARP (PNETWORK_DATA pnd)
|
|
{
|
|
struct in_addr in;
|
|
char *pszIPAddr = NULL;
|
|
|
|
dprintf(" ARP information\n");
|
|
dprintf(" Sender MAC address: %02x-%02x-%02x-%02x-%02x-%02x\n",
|
|
pnd->ARPSenderMAC[0],
|
|
pnd->ARPSenderMAC[1],
|
|
pnd->ARPSenderMAC[2],
|
|
pnd->ARPSenderMAC[3],
|
|
pnd->ARPSenderMAC[4],
|
|
pnd->ARPSenderMAC[5]
|
|
);
|
|
|
|
in.S_un.S_addr = pnd->ARPSenderIP;
|
|
pszIPAddr = inet_ntoa(in);
|
|
dprintf(" Sender IP address: 0x%08x (%s)\n", pnd->ARPSenderIP, pszIPAddr ? pszIPAddr : "");
|
|
|
|
dprintf(" Target MAC address: %02x-%02x-%02x-%02x-%02x-%02x\n",
|
|
pnd->ARPTargetMAC[0],
|
|
pnd->ARPTargetMAC[1],
|
|
pnd->ARPTargetMAC[2],
|
|
pnd->ARPTargetMAC[3],
|
|
pnd->ARPTargetMAC[4],
|
|
pnd->ARPTargetMAC[5]
|
|
);
|
|
|
|
in.S_un.S_addr = pnd->ARPTargetIP;
|
|
pszIPAddr = inet_ntoa(in);
|
|
dprintf(" Target IP address: 0x%08x (%s)\n", pnd->ARPTargetIP, pszIPAddr ? pszIPAddr : "");
|
|
}
|
|
|
|
/*
|
|
* Function: PrintNLBHeartbeat
|
|
* Description: Print the properties associated with an PrintNLBHeartbeat.
|
|
* Args: PNETWORK_DATA pnd - data structure where the extracted properties have been stored,
|
|
* populated by the function PopulateNLBHeartbeat.
|
|
* Author: Created by chrisdar 2001.11.02
|
|
*/
|
|
void PrintNLBHeartbeat(PNETWORK_DATA pnd)
|
|
{
|
|
struct in_addr in;
|
|
char *pszIPAddr = NULL;
|
|
|
|
dprintf(" NLB heartbeat information\n");
|
|
dprintf(" Code: 0x%08x\n" , pnd->HBCode);
|
|
dprintf(" Version: 0x%08x\n" , pnd->HBVersion);
|
|
dprintf(" Host: 0x%08x\n" , pnd->HBHost);
|
|
|
|
in.S_un.S_addr = pnd->HBCluster;
|
|
pszIPAddr = inet_ntoa(in);
|
|
dprintf(" Cluster IP address: 0x%08x (%s)\n" , pnd->HBCluster, pszIPAddr ? pszIPAddr : "");
|
|
|
|
in.S_un.S_addr = pnd->HBDip;
|
|
pszIPAddr = inet_ntoa(in);
|
|
dprintf(" Dedicated IP address: 0x%08x (%s)\n\n", pnd->HBDip , pszIPAddr ? pszIPAddr : "");
|
|
|
|
PrintHeartbeat(pnd->HBPtr);
|
|
}
|
|
|
|
/*
|
|
* Function: PrintConvoyHeartbeat
|
|
* Description: Print the properties associated with an PrintConvoyHeartbeat.
|
|
* Args: PNETWORK_DATA pnd - data structure where the extracted properties have been stored,
|
|
* populated by the function PopulateConvoyHeartbeat.
|
|
* Author: Created by chrisdar 2001.11.02
|
|
*/
|
|
void PrintConvoyHeartbeat(PNETWORK_DATA pnd)
|
|
{
|
|
//
|
|
// Just a stub for now. We won't deal with Convoy hosts.
|
|
//
|
|
}
|
|
|
|
/*
|
|
* Function: PrintPacket
|
|
* Description: Print the properties associated with an ethernet packet.
|
|
* Args: PNETWORK_DATA pnd - data structure where the extracted properties have been stored,
|
|
* populated by the function PopulatePacket.
|
|
* Author: Created by chrisdar 2001.11.02
|
|
*/
|
|
void PrintPacket (PNETWORK_DATA pnd)
|
|
{
|
|
dprintf(" Ethernet information\n");
|
|
dprintf(" Destination address: %02x-%02x-%02x-%02x-%02x-%02x\n",
|
|
pnd->DestMACAddr[0],
|
|
pnd->DestMACAddr[1],
|
|
pnd->DestMACAddr[2],
|
|
pnd->DestMACAddr[3],
|
|
pnd->DestMACAddr[4],
|
|
pnd->DestMACAddr[5]
|
|
);
|
|
dprintf(" Source address: %02x-%02x-%02x-%02x-%02x-%02x\n",
|
|
pnd->SourceMACAddr[0],
|
|
pnd->SourceMACAddr[1],
|
|
pnd->SourceMACAddr[2],
|
|
pnd->SourceMACAddr[3],
|
|
pnd->SourceMACAddr[4],
|
|
pnd->SourceMACAddr[5]
|
|
);
|
|
|
|
//
|
|
// Determine payload type and print accordingly
|
|
//
|
|
switch(pnd->EtherFrameType)
|
|
{
|
|
case TCPIP_IP_SIG:
|
|
dprintf(" Frame type: 0x%04x (%s)\n\n", pnd->EtherFrameType, "IP");
|
|
PrintIP(pnd);
|
|
break;
|
|
case TCPIP_ARP_SIG:
|
|
dprintf(" Frame type: 0x%04x (%s)\n\n", pnd->EtherFrameType, "ARP");
|
|
PrintARP(pnd);
|
|
break;
|
|
case MAIN_FRAME_SIG:
|
|
dprintf(" Frame type: 0x%04x (%s)\n\n", pnd->EtherFrameType, "NLB Heartbeat");
|
|
PrintNLBHeartbeat(pnd);
|
|
break;
|
|
case MAIN_FRAME_SIG_OLD:
|
|
dprintf(" Frame type: 0x%04x (%s)\n\n", pnd->EtherFrameType, "Convoy Heartbeat");
|
|
PrintConvoyHeartbeat(pnd);
|
|
break;
|
|
default:
|
|
dprintf(" Frame type: 0x%04x (%s)\n", pnd->EtherFrameType, "Unknown");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintSymbol
|
|
* Description: Print the symbol value and name for a given symbol.
|
|
* Author: Created by shouse, 12.20.01
|
|
*/
|
|
VOID PrintSymbol (ULONG64 Pointer, PCHAR EndOfLine) {
|
|
UCHAR SymbolName[128];
|
|
ULONG64 Displacement;
|
|
|
|
if (Pointer) {
|
|
/* Print the symbol value first. */
|
|
dprintf("%p ", Pointer);
|
|
|
|
/* Query the debugger for the symbol name and offset. */
|
|
GetSymbol(Pointer, SymbolName, &Displacement);
|
|
|
|
if (Displacement == 0)
|
|
/* If the displacement is zero, print just the symbol name. */
|
|
dprintf("(%s)%s", SymbolName, EndOfLine);
|
|
else
|
|
/* Otherwise, also print the offset from that symbol. */
|
|
dprintf("(%s + 0x%X)%s", SymbolName, Displacement, EndOfLine);
|
|
} else {
|
|
dprintf("None%s", EndOfLine);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintHookInterface
|
|
* Description: Print the configuration and state of a hook interface.
|
|
* Author: Created by shouse, 12.20.01
|
|
*/
|
|
void PrintHookInterface (ULONG64 pInterface) {
|
|
ULONG dwValue;
|
|
ULONG64 pAddr;
|
|
ULONG64 pTemp;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pInterface) {
|
|
dprintf("Error: Interface is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
/* Find out whether or not this interface is registered. */
|
|
GetFieldValue(pInterface, HOOK_INTERFACE, HOOK_INTERFACE_FIELD_REGISTERED, dwValue);
|
|
|
|
dprintf(" Registered: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Get the offset of the registering entity (owner). */
|
|
if (GetFieldOffset(HOOK_INTERFACE, HOOK_INTERFACE_FIELD_OWNER, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", HOOK_INTERFACE_FIELD_OWNER, HOOK_INTERFACE);
|
|
else {
|
|
pAddr = pInterface + dwValue;
|
|
|
|
/* Get the pointer to the first team. */
|
|
pTemp = GetPointerFromAddress(pAddr);
|
|
|
|
dprintf(" Owner: 0x%p\n", pTemp);
|
|
}
|
|
|
|
/* Get the offset of the deregister callback function. */
|
|
if (GetFieldOffset(HOOK_INTERFACE, HOOK_INTERFACE_FIELD_DEREGISTER, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", HOOK_INTERFACE_FIELD_DEREGISTER, HOOK_INTERFACE);
|
|
else {
|
|
pAddr = pInterface + dwValue;
|
|
|
|
/* Get the pointer to the first team. */
|
|
pTemp = GetPointerFromAddress(pAddr);
|
|
|
|
dprintf(" De-register callback: ");
|
|
|
|
PrintSymbol(pTemp, "\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintHook
|
|
* Description: Print the configuration and state of a single hook.
|
|
* Author: Created by shouse, 12.20.01
|
|
*/
|
|
void PrintHook (ULONG64 pHook) {
|
|
ULONG dwValue;
|
|
ULONG64 pAddr;
|
|
ULONG64 pTemp;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pHook) {
|
|
dprintf("Error: Hook is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
/* Find out whether or not this hook is registered. */
|
|
GetFieldValue(pHook, HOOK, HOOK_FIELD_REGISTERED, dwValue);
|
|
|
|
dprintf(" Registered: %s\n", (dwValue) ? "Yes" : "No");
|
|
|
|
/* Find out how many references exist on this hook. */
|
|
GetFieldValue(pHook, HOOK, HOOK_FIELD_REFERENCES, dwValue);
|
|
|
|
dprintf(" References: %u\n", dwValue);
|
|
|
|
/* Get the offset of the hook function table. */
|
|
if (GetFieldOffset(HOOK, HOOK_FIELD_HOOK, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", HOOK_FIELD_HOOK, HOOK);
|
|
else {
|
|
pAddr = pHook + dwValue;
|
|
|
|
/* Get the pointer to the first team. */
|
|
pTemp = GetPointerFromAddress(pAddr);
|
|
|
|
dprintf(" Function callback: ");
|
|
|
|
PrintSymbol(pTemp, "\n");
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Function: PrintHooks
|
|
* Description: Print the state of the global NLB kernel-mode hooks.
|
|
* Author: Created by shouse, 12.20.01
|
|
*/
|
|
void PrintHooks (ULONG64 pHooks) {
|
|
ULONG dwValue;
|
|
ULONG64 pFilter;
|
|
ULONG64 pAddr;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pHooks) {
|
|
dprintf("Error: Hook table is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
dprintf(" Filter Hooks:\n");
|
|
|
|
/* Get the offset of the filter hook sub-structure. */
|
|
if (GetFieldOffset(HOOK_TABLE, HOOK_TABLE_FIELD_FILTER_HOOK, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", HOOK_TABLE_FIELD_FILTER_HOOK, HOOK_TABLE);
|
|
else {
|
|
pFilter = pHooks + dwValue;
|
|
|
|
/* Find out whether or not there is an operation in progress. */
|
|
GetFieldValue(pFilter, FILTER_HOOK_TABLE, FILTER_HOOK_TABLE_FIELD_OPERATION, dwValue);
|
|
|
|
dprintf(" Operation in progress: ");
|
|
|
|
switch (dwValue) {
|
|
case HOOK_OPERATION_REGISTERING:
|
|
dprintf("Register\n");
|
|
break;
|
|
case HOOK_OPERATION_DEREGISTERING:
|
|
dprintf("De-register\n");
|
|
break;
|
|
case HOOK_OPERATION_NONE:
|
|
dprintf("None\n");
|
|
break;
|
|
default:
|
|
dprintf("Unknown\n");
|
|
break;
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the offset of the filter hook interface. */
|
|
if (GetFieldOffset(FILTER_HOOK_TABLE, FILTER_HOOK_TABLE_FIELD_INTERFACE, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", FILTER_HOOK_TABLE_FIELD_INTERFACE, FILTER_HOOK_TABLE);
|
|
else {
|
|
pAddr = pFilter + dwValue;
|
|
|
|
dprintf(" Interface:\n");
|
|
|
|
/* Print the send hook interface state and configuration. */
|
|
PrintHookInterface(pAddr);
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the offset of the send filter hook. */
|
|
if (GetFieldOffset(FILTER_HOOK_TABLE, FILTER_HOOK_TABLE_FIELD_SEND_HOOK, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", FILTER_HOOK_TABLE_FIELD_SEND_HOOK, FILTER_HOOK_TABLE);
|
|
else {
|
|
pAddr = pFilter + dwValue;
|
|
|
|
dprintf(" Send Hook:\n");
|
|
|
|
/* Print the send hook state and configuration. */
|
|
PrintHook(pAddr);
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
/* Get the offset of the receive filter hook. */
|
|
if (GetFieldOffset(FILTER_HOOK_TABLE, FILTER_HOOK_TABLE_FIELD_RECEIVE_HOOK, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", FILTER_HOOK_TABLE_FIELD_RECEIVE_HOOK, FILTER_HOOK_TABLE);
|
|
else {
|
|
pAddr = pFilter + dwValue;
|
|
|
|
dprintf(" Receive Hook:\n");
|
|
|
|
/* Print the send hook state and configuration. */
|
|
PrintHook(pAddr);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintNetworkAddresses
|
|
* Description: Prints the unicast and multicast MAC addresses configured on an NLB adapter.
|
|
* Author: Created by shouse, 1.8.02
|
|
*/
|
|
void PrintNetworkAddresses (ULONG64 pContext) {
|
|
WCHAR szString[256];
|
|
UCHAR szMAC[256];
|
|
ULONG dwValue;
|
|
ULONG64 pOpen;
|
|
ULONG64 pMiniport;
|
|
ULONG64 pName;
|
|
ULONG64 pAddr;
|
|
ULONG64 pFilter;
|
|
|
|
/* Make sure the address is non-NULL. */
|
|
if (!pContext) {
|
|
dprintf("Error: NLB context block is NULL.\n");
|
|
return;
|
|
}
|
|
|
|
/* Get the MAIN_CTXT_CODE from the structure to make sure that this address
|
|
indeed points to a valid NLB context block. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_CODE, dwValue);
|
|
|
|
if (dwValue != MAIN_CTXT_CODE) {
|
|
dprintf(" Error: Invalid NLB context block. Wrong code found (0x%08x).\n", dwValue);
|
|
return;
|
|
}
|
|
|
|
/* Get the MAC handle from the context block; this is a NDIS_OPEN_BLOCK pointer. */
|
|
GetFieldValue(pContext, MAIN_CTXT, MAIN_CTXT_FIELD_MAC_HANDLE, pOpen);
|
|
|
|
/* Get the miniport handle from the open block; this is a NDIS_MINIPORT_BLOCK pointer. */
|
|
GetFieldValue(pOpen, NDIS_OPEN_BLOCK, NDIS_OPEN_BLOCK_FIELD_MINIPORT_HANDLE, pMiniport);
|
|
|
|
/* Get a pointer to the adapter name from the miniport block. */
|
|
GetFieldValue(pMiniport, NDIS_MINIPORT_BLOCK, NDIS_MINIPORT_BLOCK_FIELD_ADAPTER_NAME, pName);
|
|
|
|
/* Get the length of the unicode string. */
|
|
GetFieldValue(pName, UNICODE_STRING, UNICODE_STRING_FIELD_LENGTH, dwValue);
|
|
|
|
/* Get the maximum length of the unicode string. */
|
|
GetFieldValue(pName, UNICODE_STRING, UNICODE_STRING_FIELD_BUFFER, pAddr);
|
|
|
|
/* Retrieve the contexts of the string and store it in a buffer. */
|
|
GetString(pAddr, szString, dwValue);
|
|
|
|
dprintf("%ls\n", szString);
|
|
|
|
/* Get the maximum length of the unicode string. */
|
|
GetFieldValue(pMiniport, NDIS_MINIPORT_BLOCK, NDIS_MINIPORT_BLOCK_FIELD_ETHDB, pFilter);
|
|
|
|
/* Get the offset of the network address. */
|
|
if (GetFieldOffset(_X_FILTER, _X_FILTER_FIELD_ADAPTER_ADDRESS, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", _X_FILTER_FIELD_ADAPTER_ADDRESS, _X_FILTER);
|
|
else {
|
|
pAddr = pFilter + dwValue;
|
|
|
|
/* Retrieve the MAC addressand store it in a buffer. */
|
|
GetMAC(pAddr, szMAC, ETH_LENGTH_OF_ADDRESS);
|
|
|
|
dprintf(" Network address: %02X-%02X-%02X-%02X-%02X-%02X\n",
|
|
((PUCHAR)(szMAC))[0], ((PUCHAR)(szMAC))[1], ((PUCHAR)(szMAC))[2],
|
|
((PUCHAR)(szMAC))[3], ((PUCHAR)(szMAC))[4], ((PUCHAR)(szMAC))[5]);
|
|
}
|
|
|
|
/* Get the number of MAC addresses in the multicast list. */
|
|
GetFieldValue(pFilter, _X_FILTER, _X_FILTER_FIELD_NUM_ADDRESSES, dwValue);
|
|
|
|
dprintf(" Multicast MAC addresses (%u): ", dwValue);
|
|
|
|
/* Get the number of MAC addresses in the multicast list. */
|
|
GetFieldValue(pFilter, _X_FILTER, _X_FILTER_FIELD_MCAST_ADDRESS_BUF, pAddr);
|
|
|
|
for ( ; dwValue > 0; dwValue--, pAddr += ETH_LENGTH_OF_ADDRESS) {
|
|
|
|
/* Retrieve the MAC addressand store it in a buffer. */
|
|
GetMAC(pAddr, szMAC, ETH_LENGTH_OF_ADDRESS);
|
|
|
|
dprintf("%02X-%02X-%02X-%02X-%02X-%02X\n",
|
|
((PUCHAR)(szMAC))[0], ((PUCHAR)(szMAC))[1], ((PUCHAR)(szMAC))[2],
|
|
((PUCHAR)(szMAC))[3], ((PUCHAR)(szMAC))[4], ((PUCHAR)(szMAC))[5]);
|
|
|
|
if (dwValue != 1)
|
|
dprintf(" ");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: PrintDIPList
|
|
* Description: Prints the list of known dedicated IP addresses in the cluster.
|
|
* Author: Created by shouse, 4.8.02
|
|
*/
|
|
void PrintDIPList (ULONG64 pList) {
|
|
IN_ADDR dwIPAddr;
|
|
CHAR * szString;
|
|
ULONG dwValue;
|
|
ULONG dwSize;
|
|
ULONG64 pAddr = 0;
|
|
BOOLEAN bFound = FALSE;
|
|
INT i;
|
|
|
|
/* Get the offset of the dedicated IP address array for this DIP list. */
|
|
if (GetFieldOffset(DIPLIST, DIPLIST_FIELD_ITEMS, &dwValue))
|
|
dprintf("Can't get offset of %s in %s\n", DIPLIST_FIELD_ITEMS, DIPLIST);
|
|
else
|
|
pAddr = pList + dwValue;
|
|
|
|
/* Get the size of a ULONG. */
|
|
dwSize = GetTypeSize(ULONG_T);
|
|
|
|
for (i = 0; i < MAX_ITEMS; i++) {
|
|
/* Get the dedicated IP address, which is a DWORD, and convert it to a string. */
|
|
dwValue = GetUlongFromAddress(pAddr);
|
|
|
|
/* If the DIP is present for this entry, print it. */
|
|
if (dwValue != NULL_VALUE) {
|
|
/* Note the fact that we found at least one DIP. */
|
|
bFound = TRUE;
|
|
|
|
dwIPAddr.S_un.S_addr = dwValue;
|
|
szString = inet_ntoa(dwIPAddr);
|
|
|
|
dprintf(" Host %2u: %s\n", i+1, szString);
|
|
}
|
|
|
|
/* Move the pointer to the next DIP. */
|
|
pAddr += dwSize;
|
|
}
|
|
|
|
/* If no DIPs were printed, print "None". */
|
|
if (!bFound)
|
|
dprintf(" None\n");
|
|
|
|
if (ChkTarget)
|
|
{
|
|
dprintf("\n");
|
|
|
|
/* Get the number of DIP list checks so far. */
|
|
GetFieldValue(pList, DIPLIST, DIPLIST_FIELD_NUM_CHECKS, dwValue);
|
|
|
|
dprintf(" Number of checks: %u\n");
|
|
|
|
/* Get the number of checks that required ONLY the bit-vector lookup. */
|
|
GetFieldValue(pList, DIPLIST, DIPLIST_FIELD_NUM_FAST_CHECKS, dwValue);
|
|
|
|
dprintf(" Number of fast checks: %u\n");
|
|
|
|
/* Get the number of checks that required an array access. */
|
|
GetFieldValue(pList, DIPLIST, DIPLIST_FIELD_NUM_ARRAY_LOOKUPS, dwValue);
|
|
|
|
dprintf(" Number of array lookups: %u\n");
|
|
}
|
|
}
|
|
|