Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

653 lines
21 KiB

/*
* File: nlbkd.c
* Description: This file contains the implementation of the NLB KD
* debugging extensions. Use '!load nlbkd.dll' to load
* the extensions and '!nlbkd.help' to see the supported
* extensions.
* Author: Created by shouse, 1.4.01
*/
#include "nlbkd.h"
#include "utils.h"
#include "print.h"
WINDBG_EXTENSION_APIS ExtensionApis;
EXT_API_VERSION ApiVersion = { 1, 0, EXT_API_VERSION_NUMBER64, 0 };
#define NL 1
#define NONL 0
USHORT SavedMajorVersion;
USHORT SavedMinorVersion;
BOOL ChkTarget;
/*
* Function: WinDbgExtensionDllInit
* Description: Initializes the KD extension DLL.
* Author: Created by shouse, 1.4.01 - copied largely from ndiskd.dll
*/
VOID WinDbgExtensionDllInit (PWINDBG_EXTENSION_APIS64 lpExtensionApis, USHORT MajorVersion, USHORT MinorVersion) {
ExtensionApis = *lpExtensionApis;
SavedMajorVersion = MajorVersion;
SavedMinorVersion = MinorVersion;
ChkTarget = (SavedMajorVersion == 0x0c) ? TRUE : FALSE;
}
/*
* Function: CheckVersion
* Description: Checks the extension DLL version against the target version.
* Author: Created by shouse, 1.4.01 - copied largely from ndiskd.dll
*/
VOID CheckVersion (VOID) {
/* For now, do nothing. */
return;
#if DBG
if ((SavedMajorVersion != 0x0c) || (SavedMinorVersion != VER_PRODUCTBUILD)) {
dprintf("\r\n*** Extension DLL(%d Checked) does not match target system(%d %s)\r\n\r\n",
VER_PRODUCTBUILD, SavedMinorVersion, (SavedMajorVersion==0x0f) ? "Free" : "Checked" );
}
#else
if ((SavedMajorVersion != 0x0f) || (SavedMinorVersion != VER_PRODUCTBUILD)) {
dprintf("\r\n*** Extension DLL(%d Free) does not match target system(%d %s)\r\n\r\n",
VER_PRODUCTBUILD, SavedMinorVersion, (SavedMajorVersion==0x0f) ? "Free" : "Checked" );
}
#endif
}
/*
* Function: ExtensionApiVersion
* Description: Returns the API version information.
* Author: Created by shouse, 1.4.01 - copied largely from ndiskd.dll
*/
LPEXT_API_VERSION ExtensionApiVersion (VOID) {
return &ApiVersion;
}
/*
* Function: help
* Description: Prints the usage of the NLB KD debugger extensions.
* Author: Created by shouse, 1.4.01
*/
DECLARE_API (help) {
dprintf("Network Load Balancing debugger extensions:\n");
dprintf(" version print nlbkd version\n");
dprintf(" nlbadapters [Verbosity] show all NLB adapter blocks\n");
dprintf(" nlbadapter <Adapter Block> [Verbosity] dump an NLB adapter block\n");
dprintf(" nlbctxt <Context Block> [Verbosity] dump an NLB context block\n");
dprintf(" nlbload <Load Block> [Verbosity] dump an NLB load block\n");
dprintf(" nlbparams <Params Block> [Verbosity] dump an NLB parameters block\n");
dprintf(" nlbresp <Packet> [Direction] dump the NLB private data for the specified packet\n");
dprintf(" nlbconnq <Queue> [MaxEntries] dump the contents of a connection descriptor queue\n");
dprintf(" nlbhash <Packet> determine whether or not NLB will accept this packet\n");
dprintf(" nlbpkt <Packet> dump an NLB-specific packet whose type is determined\n");
dprintf(" automagically (heartbeat, IGMP, or remote-control)\n");
dprintf(" nlbmap <Load Block> <Client IP> <Client Port> <Server IP> <Server Port> [Protocol] [Packet Type]\n");
dprintf(" query map function and retrieve any existing state for this tuple\n");
dprintf(" nlbteams dump the linked list of NLB BDA teams\n");
dprintf("\n");
dprintf(" [Verbosity] is an optional integer from 0 to 2 that determines the level of detail displayed.\n");
dprintf(" [Direction] is an optional integer that specifies the direction of the packet (RCV=0, SND=1).\n");
dprintf(" [Protocol] is an optional protocol specification, which can be TCP or UDP.\n");
dprintf(" [Packet Type] is an optional TCP packet type specification, which can be SYN, DATA, FIN or RST.\n");
dprintf("\n");
dprintf(" IP addresses can be in dotted notation or network byte order DWORDs.\n");
}
/*
* Function: version
* Description: Prints the NLB KD debugger extension version information.
* Author: Created by shouse, 1.4.01 - copied largely from ndiskd.dll
*/
DECLARE_API (version) {
#if DBG
PCSTR kind = "Checked";
#else
PCSTR kind = "Free";
#endif
dprintf("%s NLB Extension DLL for Build %d debugging %s kernel for Build %d\n", kind,
VER_PRODUCTBUILD, SavedMajorVersion == 0x0c ? "Checked" : "Free", SavedMinorVersion);
}
/*
* Function: nlbadapters
* Description: Prints all NLB adapter strucutres in use. Verbosity is always LOW.
* Author: Created by shouse, 1.5.01
*/
DECLARE_API (nlbadapters) {
ULONG dwVerbosity = VERBOSITY_LOW;
CHAR szArgList[10][MAX_PATH];
CHAR szArgBuffer[MAX_PATH];
ULONG64 pNumAdapters;
DWORD dwAdapterSize;
ULONG dwNumAdapters;
ULONG64 pAdapter;
ULONG dwIndex;
INT index = 0;
CHAR * str;
CHAR * p;
if (args && (*args)) {
/* Copy the argument list into a temporary buffer. */
strcpy(szArgBuffer, args);
/* Peel out all of the tokenized strings. */
for (p = mystrtok(szArgBuffer, " \t," ); p && *p; p = mystrtok(NULL, " \t,"))
strcpy(&szArgList[index++][0], p);
/* If a verbosity was specified, get it. */
if (index == 1) dwVerbosity = atoi(&szArgList[0][0]);
/* If too many arguments were given, or the verbosity was out of range, complain. */
if ((index > 1) || (dwVerbosity > VERBOSITY_HIGH)) {
PrintUsage(USAGE_ADAPTERS);
return;
}
}
/* Get the address of the global variable containing the number of NLB adapters in use. */
pNumAdapters = GetExpression(UNIV_ADAPTERS_COUNT);
if (!pNumAdapters) {
ErrorCheckSymbols(UNIV_ADAPTERS_COUNT);
return;
}
/* Get the number of adapters from the address. */
dwNumAdapters = GetUlongFromAddress(pNumAdapters);
dprintf("Network Load Balancing is currently bound to %u adapter(s).\n", dwNumAdapters);
/* Get the base address of the global array of NLB adapter structures. */
pAdapter = GetExpression(UNIV_ADAPTERS);
if (!pAdapter) {
ErrorCheckSymbols(UNIV_ADAPTERS);
return;
}
/* Find out the size of a MAIN_ADAPTER structure. */
dwAdapterSize = GetTypeSize(MAIN_ADAPTER);
/* Loop through all adapters in use and print some information about them. */
for (dwIndex = 0; dwIndex < CVY_MAX_ADAPTERS; dwIndex++) {
ULONG dwValue;
/* Retrieve the used/unused state of the adapter. */
GetFieldValue(pAdapter, MAIN_ADAPTER, MAIN_ADAPTER_FIELD_USED, dwValue);
/* If the adapter is in use, or the user specified HIGH verbosity, print the adapter. */
if (dwValue || (dwVerbosity == VERBOSITY_HIGH)) {
/* Print the adapter index. */
dprintf("\n[%u] ", dwIndex);
/* Print the adapter contents. If verbosity is high, change it to
medium - we don't want to recurse into context from here. */
PrintAdapter(pAdapter, (dwVerbosity == VERBOSITY_HIGH) ? VERBOSITY_MEDIUM : dwVerbosity);
}
/* Advance the pointer to the next index in the array of structures. */
pAdapter += dwAdapterSize;
}
}
/*
* Function: nlbadapter
* Description: Prints NLB adapter information. Takes an adapter pointer and an
* optional verbosity as arguments. Default verbosity is MEDIUM.
* Author: Created by shouse, 1.5.01
*/
DECLARE_API (nlbadapter) {
ULONG dwVerbosity = VERBOSITY_LOW;
CHAR szArgList[10][MAX_PATH];
CHAR szArgBuffer[MAX_PATH];
ULONG64 pAdapter;
INT index = 0;
CHAR * str;
CHAR * p;
/* Make sure at least one argument, the adapter pointer, is there. */
if (!args || !(*args)) {
PrintUsage(USAGE_ADAPTER);
return;
}
/* Get the address of the NLB adapter block from the command line. */
pAdapter = (ULONG64)GetExpression(args);
/* Copy the argument list into a temporary buffer. */
strcpy(szArgBuffer, args);
/* Peel out all of the tokenized strings. */
for (p = mystrtok(szArgBuffer, " \t," ); p && *p; p = mystrtok(NULL, " \t,"))
strcpy(&szArgList[index++][0], p);
/* If a verbosity was specified, get it. */
if (index == 2) dwVerbosity = atoi(&szArgList[1][0]);
/* If too many arguments were given, or the verbosity was out of range, complain. */
if ((index > 2) || (dwVerbosity > VERBOSITY_HIGH)) {
PrintUsage(USAGE_ADAPTER);
return;
}
/* Print the adapter contents. */
PrintAdapter(pAdapter, dwVerbosity);
}
/*
* Function: nlbctxt
* Description: Prints NLB context information. Takes a context pointer and an
* optional verbosity as arguments. Default verbosity is LOW.
* Author: Created by shouse, 1.21.01
*/
DECLARE_API (nlbctxt) {
ULONG dwVerbosity = VERBOSITY_LOW;
CHAR szArgList[10][MAX_PATH];
CHAR szArgBuffer[MAX_PATH];
ULONG64 pContext;
INT index = 0;
CHAR * str;
CHAR * p;
/* Make sure at least one argument, the context pointer, is there. */
if (!args || !(*args)) {
PrintUsage(USAGE_CONTEXT);
return;
}
/* Get the address of the NLB context block from the command line. */
pContext = (ULONG64)GetExpression(args);
/* Copy the argument list into a temporary buffer. */
strcpy(szArgBuffer, args);
/* Peel out all of the tokenized strings. */
for (p = mystrtok(szArgBuffer, " \t," ); p && *p; p = mystrtok(NULL, " \t,"))
strcpy(&szArgList[index++][0], p);
/* If a verbosity was specified, get it. */
if (index == 2) dwVerbosity = atoi(&szArgList[1][0]);
/* If too many arguments were given, or the verbosity was out of range, complain. */
if ((index > 2) || (dwVerbosity > VERBOSITY_HIGH)) {
PrintUsage(USAGE_CONTEXT);
return;
}
/* Print the context contents. */
PrintContext(pContext, dwVerbosity);
}
/*
* Function: nlbload
* Description: Prints NLB load information. Takes a load pointer and an optional
* verbosity as arguments. Default verbosity is LOW.
* Author: Created by shouse, 2.1.01
*/
DECLARE_API (nlbload) {
ULONG dwVerbosity = VERBOSITY_LOW;
CHAR szArgList[10][MAX_PATH];
CHAR szArgBuffer[MAX_PATH];
ULONG64 pLoad;
INT index = 0;
CHAR * str;
CHAR * p;
/* Make sure at least one argument, the load pointer, is there. */
if (!args || !(*args)) {
PrintUsage(USAGE_LOAD);
return;
}
/* Get the address of the NLB load block from the command line. */
pLoad = (ULONG64)GetExpression(args);
/* Copy the argument list into a temporary buffer. */
strcpy(szArgBuffer, args);
/* Peel out all of the tokenized strings. */
for (p = mystrtok(szArgBuffer, " \t," ); p && *p; p = mystrtok(NULL, " \t,"))
strcpy(&szArgList[index++][0], p);
/* If a verbosity was specified, get it. */
if (index == 2) dwVerbosity = atoi(&szArgList[1][0]);
/* If too many arguments were given, or the verbosity was out of range, complain. */
if ((index > 2) || (dwVerbosity > VERBOSITY_HIGH)) {
PrintUsage(USAGE_LOAD);
return;
}
/* Print the load contents. */
PrintLoad(pLoad, dwVerbosity);
}
/*
* Function: nlbparams
* Description: Prints NLB parameter information. Takes a parameter pointer and an
* optional verbosity as arguments. Default verbosity is LOW.
* Author: Created by shouse, 1.21.01
*/
DECLARE_API (nlbparams) {
ULONG dwVerbosity = VERBOSITY_LOW;
CHAR szArgList[10][MAX_PATH];
CHAR szArgBuffer[MAX_PATH];
ULONG64 pParams;
INT index = 0;
CHAR * str;
CHAR * p;
/* Make sure at least one argument, the params pointer, is there. */
if (!args || !(*args)) {
PrintUsage(USAGE_PARAMS);
return;
}
/* Get the address of the NLB params block from the command line. */
pParams = (ULONG64)GetExpression(args);
/* Copy the argument list into a temporary buffer. */
strcpy(szArgBuffer, args);
/* Peel out all of the tokenized strings. */
for (p = mystrtok(szArgBuffer, " \t," ); p && *p; p = mystrtok(NULL, " \t,"))
strcpy(&szArgList[index++][0], p);
/* If a verbosity was specified, get it. */
if (index == 2) dwVerbosity = atoi(&szArgList[1][0]);
/* If too many arguments were given, or the verbosity was out of range, complain. */
if ((index > 2) || (dwVerbosity > VERBOSITY_HIGH)) {
PrintUsage(USAGE_PARAMS);
return;
}
/* Print the parameter contents. */
PrintParams(pParams, dwVerbosity);
}
/*
* Function: nlbresp
* Description: Prints out the NLB private packet data for a given packet. Takes a
* packet pointer and an optional direction as arguments. If not specified,
* the packet is presumed to be on the receive path.
* Author: Created by shouse, 1.31.01
*/
DECLARE_API (nlbresp) {
ULONG dwDirection = DIRECTION_RECEIVE;
CHAR szArgList[10][MAX_PATH];
CHAR szArgBuffer[MAX_PATH];
ULONG64 pPacket;
INT index = 0;
CHAR * str;
CHAR * p;
/* Make sure at least one argument, the packet pointer, is there. */
if (!args || !(*args)) {
PrintUsage(USAGE_RESP);
return;
}
/* Get the address of the NDIS packet from the command line. */
pPacket = (ULONG64)GetExpression(args);
/* Copy the argument list into a temporary buffer. */
strcpy(szArgBuffer, args);
/* Peel out all of the tokenized strings. */
for (p = mystrtok(szArgBuffer, " \t," ); p && *p; p = mystrtok(NULL, " \t,"))
strcpy(&szArgList[index++][0], p);
/* If a direction was specified, get it. */
if (index == 2) dwDirection = atoi(&szArgList[1][0]);
/* If too many arguments were given, or the direction was out of range, complain. */
if ((index > 2) || (dwDirection > DIRECTION_SEND)) {
PrintUsage(USAGE_RESP);
return;
}
/* Print the NLB private data buffer contents. */
PrintResp(pPacket, dwDirection);
}
/*
* Function: nlbadapters
* Description: Prints all NLB adapter strucutres in use. Verbosity is always LOW.
* Author: Created by shouse, 1.5.01
*/
DECLARE_API (nlbteams) {
ULONG64 pTeam;
ULONG64 pAddr;
ULONG dwNumTeams = 0;
ULONG dwValue;
/* Get the base address of the global linked list of BDA teams. */
pAddr = GetExpression(UNIV_BDA_TEAMS);
if (!pAddr) {
ErrorCheckSymbols(UNIV_BDA_TEAMS);
return;
}
/* Get the pointer to the first team. */
pTeam = GetPointerFromAddress(pAddr);
dprintf("NLB bi-directional affinity teams:\n");
/* Loop through all teams in the list and print them out. */
while (pTeam) {
/* Increment the number of teams found - only used if none are found. */
dwNumTeams++;
dprintf("\n");
/* Print out the team. */
PrintBDATeam(pTeam);
/* Get the offset of the params 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. */
pTeam = GetPointerFromAddress(pAddr);
}
}
if (!dwNumTeams) dprintf("\nNone.\n");
}
/*
* Function: nlbconnq
* Description: This function prints out all connection descriptors in a given
* queue of descriptors.
* Author: Created by shouse, 4.15.01
*/
DECLARE_API (nlbconnq) {
ULONG dwMaxEntries = 0xffffffff;
CHAR szArgList[10][MAX_PATH];
CHAR szArgBuffer[MAX_PATH];
ULONG64 pQueue;
INT index = 0;
CHAR * str;
CHAR * p;
/* Make sure at least one argument, the queue pointer, is there. */
if (!args || !(*args)) {
PrintUsage(USAGE_CONNQ);
return;
}
/* Get the address of the queue from the command line. */
pQueue = (ULONG64)GetExpression(args);
/* Copy the argument list into a temporary buffer. */
strcpy(szArgBuffer, args);
/* Peel out all of the tokenized strings. */
for (p = mystrtok(szArgBuffer, " \t," ); p && *p; p = mystrtok(NULL, " \t,"))
strcpy(&szArgList[index++][0], p);
/* If a maximum number of entries to print was specified, get it. */
if (index == 2) dwMaxEntries = atoi(&szArgList[1][0]);
/* If too many arguments were given,complain. */
if (index > 2) {
PrintUsage(USAGE_RESP);
return;
}
/* Print the NLB private data buffer contents. */
PrintQueue(pQueue, dwMaxEntries);
}
/*
* Function: nlbmap
* Description: This function will perform the NLB hashing algorithm to determine
* whether a given packet - identified by a (Src IP, Src port, Dst IP,
* Dst port) tuple would be handled by this host or another host.
* Further, if the connection is a known TCP connection, the associated
* descriptor and state information are displayed.
* Author:
*/
DECLARE_API (nlbmap) {
CHAR szArgList[10][MAX_PATH];
CHAR szArgBuffer[MAX_PATH];
TCP_PACKET_TYPE ePktType = SYN;
ULONG64 pLoad;
ULONG dwClientIPAddress;
ULONG dwClientPort;
ULONG dwServerIPAddress;
ULONG dwServerPort;
BOOLEAN bIsTCP = TRUE;
INT index = 0;
CHAR * str;
CHAR * p;
/* Make sure that the load pointer is there. */
if (!args || !(*args)) {
PrintUsage(USAGE_MAP);
return;
}
/* Get the address of the load module from the command line. */
pLoad = (ULONG64)GetExpression(args);
/* Copy the argument list into a temporary buffer. */
strcpy(szArgBuffer, args);
/* Peel out all of the tokenized strings. */
for (p = mystrtok(szArgBuffer, " \t," ); p && *p; p = mystrtok(NULL, " \t,"))
strcpy(&szArgList[index++][0], p);
/* If too many arguments were given, complain. */
if ((index > 7) || (index < 5)) {
PrintUsage(USAGE_MAP);
return;
}
/* If we find a '.' in the IP address, then we need to convert it using inet_addr.
If there is no '.', then we assume its already a DWORD in network byte order. */
if (strchr(szArgList[1], '.'))
dwClientIPAddress = inet_addr(szArgList[1]);
else
dwClientIPAddress = atoi(&szArgList[1][0]);
dwClientPort = atoi(&szArgList[2][0]);
/* Make sure the port is between 0 and 65535. */
if (dwClientPort > CVY_MAX_PORT) {
dprintf("Invalid port: %s\n", dwClientPort);
return;
}
/* If we find a '.' in the IP address, then we need to convert it using inet_addr.
If there is no '.', then we assume its already a DWORD in network byte order. */
if (strchr(szArgList[1], '.'))
dwServerIPAddress = inet_addr(szArgList[3]);
else
dwServerIPAddress = atoi(&szArgList[3][0]);
dwServerPort = atoi(&szArgList[4][0]);
/* Make sure the port is between 0 and 65535. */
if (dwServerPort > CVY_MAX_PORT) {
dprintf("Invalid port: %s\n", dwServerPort);
return;
}
/* If a sixth argument has been specified, it is the protocol, which should be either TCP or UDP. */
if (index >= 6) {
if (!_stricmp(szArgList[5], "TCP")) {
bIsTCP = TRUE;
} else if (!_stricmp(szArgList[5], "UDP")) {
bIsTCP = FALSE;
} else {
dprintf("Invalid protocol: %s\n", szArgList[5]);
return;
}
}
/* If an seventh argument has been specified, it is TCP packet type, which should be SYN, DATA, FIN or RST. */
if (index >= 7) {
if (!bIsTCP) {
dprintf("UDP connections do not have packet types\n");
return;
}
if (!_stricmp(szArgList[6], "SYN")) {
ePktType = SYN;
} else if (!_stricmp(szArgList[6], "DATA")) {
ePktType = DATA;
} else if (!_stricmp(szArgList[6], "FIN")) {
ePktType = FIN;
} else if (!_stricmp(szArgList[6], "RST")) {
ePktType = RST;
} else {
dprintf("Invalid TCP packet type: %s\n", szArgList[6]);
return;
}
}
/* Hash on this tuple and print the results. */
PrintMap(pLoad, dwClientIPAddress, dwClientPort, dwServerIPAddress, dwServerPort, bIsTCP, ePktType);
}
/*
* Function: nlbhash
* Description:
* Author: Created by shouse, 4.15.01
*/
DECLARE_API (nlbhash) {
dprintf("This extension has not yet been implemented.\n");
}
/*
* Function: nlbpkt
* Description: Prints out the contents of an NLB-specific packet. Takes a packet
* pointer as an argument.
* Author: Created by shouse, 2.1.01
*/
DECLARE_API (nlbpkt) {
dprintf("This extension should take a packet pointer and parse the packet to\n");
dprintf(" determine whether it is an NLB heartbeat, remote control or IGMP join.\n");
dprintf(" If the packet is one of those NLB-specific types, it will dump the\n");
dprintf(" contents of the packet. Otherwise, it prints just basic packet info,\n");
dprintf(" such as source and destination IP addresses and port numbers.\n");
dprintf("\n");
dprintf("This extension has not yet been implemented.\n");
}