windows-nt/Source/XPSP1/NT/net/tcpip/tools/tcbmon/tcbmon.c
2020-09-26 16:20:57 +08:00

396 lines
12 KiB
C

/*++
Copyright (c) 1999, Microsoft Corporation
Module Name:
tcbmon.c
Abstract:
This module contains code for a utility program which monitors
the variables for the active TCP control blocks in the system.
The program optionally maintains a log for a specified TCB
in CSV format in a file specified by the user.
Author:
Abolade Gbadegesin (aboladeg) January-25-1999
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <ntddip.h>
#include <ntddtcp.h>
#include <ipinfo.h>
#include <iphlpapi.h>
#include <iphlpstk.h>
HANDLE ConsoleHandle;
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
ULONG DisplayInterval = 500;
SOCKADDR_IN LogLocal;
PCHAR LogPath;
SOCKADDR_IN LogRemote;
HANDLE StopEvent;
HANDLE TcpipHandle;
VOID
WriteLine(
COORD Coord,
CHAR* Format,
...
)
{
va_list arglist;
char Buffer[256];
ULONG Count;
ULONG Length;
va_start(arglist, Format);
Count = vsprintf(Buffer, Format, arglist);
FillConsoleOutputCharacter(
ConsoleHandle, ' ', ConsoleInfo.dwSize.X, Coord, &Length
);
WriteConsoleOutputCharacter(
ConsoleHandle, (LPCTSTR)Buffer, Count, Coord, &Length
);
}
VOID
ClearToEnd(
COORD Coord,
COORD End
)
{
ULONG Length;
while (Coord.Y <= End.Y) {
FillConsoleOutputCharacter(
ConsoleHandle, ' ', ConsoleInfo.dwSize.X, Coord, &Length
);
++Coord.Y;
}
}
ULONG WINAPI
DisplayThread(
PVOID Unused
)
{
COORD End = {0,0};
FILE* LogFile = NULL;
do {
COORD Coord = {0, 0};
DWORD Error;
ULONG i;
ULONG Length;
CHAR LocalAddr[20];
CHAR RemoteAddr[20];
char *DestString;
char *SrcString;
TCP_FINDTCB_REQUEST Request;
TCP_FINDTCB_RESPONSE Response;
PMIB_TCPTABLE Table;
if (LogPath && !LogFile) {
LogFile = fopen(LogPath, "w+");
if (!LogFile) {
perror("fopen");
break;
} else {
fprintf(
LogFile,
"#senduna,sendnext,sendmax,sendwin,unacked,maxwin,cwin,"
"mss,rtt,smrtt,rexmitcnt,rexmittimer,rexmit,retrans,state,"
"flags,rto,delta\n"
);
}
}
Error =
AllocateAndGetTcpTableFromStack(
&Table,
TRUE,
GetProcessHeap(),
0
);
if (Error) {
COORD Top = {0, 0};
WriteLine(Top, "AllocateAndGetTcpTableFromStack: %d", Error);
if (WaitForSingleObject(StopEvent, DisplayInterval)
== WAIT_OBJECT_0) {
break;
} else {
continue;
}
}
for (i = 0; i < Table->dwNumEntries; i++) {
if (Table->table[i].dwState < MIB_TCP_STATE_SYN_SENT ||
Table->table[i].dwState > MIB_TCP_STATE_TIME_WAIT) {
continue;
}
Request.Src = Table->table[i].dwLocalAddr;
Request.Dest = Table->table[i].dwRemoteAddr;
Request.SrcPort = (USHORT)Table->table[i].dwLocalPort;
Request.DestPort = (USHORT)Table->table[i].dwRemotePort;
if (!DeviceIoControl(
TcpipHandle,
IOCTL_TCP_FINDTCB,
&Request,
sizeof(Request),
&Response,
sizeof(Response),
&Length,
NULL
)) {
COORD Top = {0, 0};
WriteLine(Top, "DeviceIoControl: %d", GetLastError());
continue;
}
SrcString = inet_ntoa(*(PIN_ADDR)&Request.Src);
DestString = inet_ntoa(*(PIN_ADDR)&Request.Dest);
if (!SrcString || !DestString) {
continue;
}
lstrcpy(LocalAddr, SrcString);
lstrcpy(RemoteAddr, DestString);
++Coord.Y;
WriteLine(
Coord, "%s:%d %s:%d",
LocalAddr, ntohs(Request.SrcPort),
RemoteAddr, ntohs(Request.DestPort)
);
++Coord.Y;
WriteLine(
Coord, " smrtt: %-8d rexmit: %-8d rexmitcnt: %-8d",
Response.tcb_smrtt, Response.tcb_rexmit, Response.tcb_rexmitcnt
);
++Coord.Y;
if (Request.Src == LogLocal.sin_addr.s_addr &&
Request.Dest == LogRemote.sin_addr.s_addr &&
(LogLocal.sin_port == 0 ||
Request.SrcPort == LogLocal.sin_port) &&
(LogRemote.sin_port == 0 ||
Request.DestPort == LogRemote.sin_port)) {
LogLocal.sin_port = Request.SrcPort;
LogRemote.sin_port = Request.DestPort;
// senduna, sendnext
fprintf(
LogFile, "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,"
"%x,%u,%u\n",
Response.tcb_senduna,
Response.tcb_sendnext,
Response.tcb_sendmax,
Response.tcb_sendwin,
Response.tcb_unacked,
Response.tcb_maxwin,
Response.tcb_cwin,
Response.tcb_mss,
Response.tcb_rtt,
Response.tcb_smrtt,
Response.tcb_rexmitcnt,
Response.tcb_rexmittimer,
Response.tcb_rexmit,
Response.tcb_retrans,
Response.tcb_state,
0,
0,
0
);
}
}
HeapFree(GetProcessHeap(), 0, Table);
ClearToEnd(Coord, End);
End = Coord;
} while (WaitForSingleObject(StopEvent, DisplayInterval) != WAIT_OBJECT_0);
if (LogFile) { fclose(LogFile); }
NtClose(TcpipHandle);
CloseHandle(ConsoleHandle);
return 0;
}
void
DisplayUsage(
void
)
{
printf("tcbmon [-?] [-refresh <ms>] [-log <path> <session>\n");
printf("\t<session> = <local endpoint> <remote endpoint>\n");
printf("\t<endpoint> = <address> { <port> | * }\n");
}
void
DisplayTcbHelp(
void
)
{
printf("tcbmon: TCB Help\n");
printf("tcb fields:\n");
printf("\tsenduna = seq. of first unack'd byte\n");
printf("\tsendnext = seq. of next byte to send\n");
printf("\tsendmax = max. seq. sent so far\n");
printf("\tsendwin = size of send window in bytes\n");
printf("\tunacked = number of unack'd bytes\n");
printf("\tmaxwin = max. send window offered\n");
printf("\tcwin = size of congestion window in bytes\n");
printf("\tmss = max. segment size\n");
printf("\trtt = timestamp of current rtt measurement\n");
printf("\tsmrtt = smoothed rtt measurement\n");
printf("\trexmitcnt = number of rexmit'd segments\n");
printf("\trexmittimer = rexmit timer in ticks\n");
printf("\trexmit = rexmit timeout last computed\n");
printf("\tretrans = total rexmit'd segments (all sessions)\n");
printf("\tstate = connection state\n");
printf("\tflags = connection flags (see below)\n");
printf("\trto = real-time rto (compare rexmit)\n");
printf("\tdelta = rtt variance\n");
printf("\n");
printf("flags:\n");
printf("\t00000001 = window explicitly set\n");
printf("\t00000002 = has client options\n");
printf("\t00000004 = from accept\n");
printf("\t00000008 = from active open\n");
printf("\t00000010 = client notified of disconnect\n");
printf("\t00000020 = in delayed action queue\n");
printf("\t00000040 = completing receives\n");
printf("\t00000080 = in receive-indication handler\n");
printf("\t00000100 = needs receive-completes\n");
printf("\t00000200 = needs to send ack\n");
printf("\t00000400 = needs to output\n");
printf("\t00000800 = delayed sending ack\n");
printf("\t00001000 = probing for path-mtu bh\n");
printf("\t00002000 = using bsd urgent semantics\n");
printf("\t00004000 = in 'DeliverUrgent'\n");
printf("\t00008000 = seen urgent data and urgent data fields valid\n");
printf("\t00010000 = needs to send fin\n");
printf("\t00020000 = using nagle's algorithm\n");
printf("\t00040000 = in 'TCPSend'\n");
printf("\t00080000 = flow-controlled (received zero-window)\n");
printf("\t00100000 = disconnect-notif. pending\n");
printf("\t00200000 = time-wait transition pending\n");
printf("\t00400000 = output being forced\n");
printf("\t00800000 = send pending after receive\n");
printf("\t01000000 = graceful-close pending\n");
printf("\t02000000 = keepalives enabled\n");
printf("\t04000000 = processing urgent data inline\n");
printf("\t08000000 = inform acd about connection\n");
printf("\t10000000 = fin sent since last retransmit\n");
printf("\t20000000 = unack'd fin sent\n");
printf("\t40000000 = need to send rst when closing\n");
printf("\t80000000 = in tcb table\n");
}
int __cdecl
main(
int argc,
char* argv[]
)
{
LONG i;
IO_STATUS_BLOCK IoStatus;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
HANDLE ThreadHandle;
ULONG ThreadId;
UNICODE_STRING UnicodeString;
for (i = 1; i < argc; i++) {
if (lstrcmpi(argv[i], "-?") == 0 || lstrcmpi(argv[i], "/?") == 0) {
DisplayUsage();
return 0;
} else if (lstrcmpi(argv[i], "-tcbhelp") == 0) {
DisplayTcbHelp();
return 0;
} else if (lstrcmpi(argv[i], "-refresh") == 0 && (i + 1) >= argc) {
DisplayInterval = atol(argv[++i]);
if (!DisplayInterval) {
DisplayUsage();
return 0;
}
} else if (lstrcmpi(argv[i], "-log") == 0) {
if ((i + 5) >= argc) {
DisplayUsage();
return 0;
}
LogPath = argv[++i];
LogLocal.sin_addr.s_addr = inet_addr(argv[++i]);
if (lstrcmpi(argv[i+1], "*") == 0) {
LogLocal.sin_port = 0; ++i;
} else {
LogLocal.sin_port = htons((SHORT)atol(argv[++i]));
}
LogRemote.sin_addr.s_addr = inet_addr(argv[++i]);
if (lstrcmpi(argv[i+1], "*") == 0) {
LogRemote.sin_port = 0; ++i;
} else {
LogRemote.sin_port = htons((SHORT)atol(argv[++i]));
}
if (LogLocal.sin_addr.s_addr == INADDR_NONE ||
LogRemote.sin_addr.s_addr == INADDR_NONE) {
DisplayUsage();
return 0;
}
}
}
StopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
ConsoleHandle =
CreateConsoleScreenBuffer(
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL
);
SetConsoleActiveScreenBuffer(ConsoleHandle);
GetConsoleScreenBufferInfo(ConsoleHandle, &ConsoleInfo);
ConsoleInfo.dwSize.Y = 1000;
SetConsoleScreenBufferSize(ConsoleHandle, ConsoleInfo.dwSize);
RtlInitUnicodeString(&UnicodeString, DD_TCP_DEVICE_NAME);
InitializeObjectAttributes(
&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status =
NtCreateFile(
&TcpipHandle,
GENERIC_EXECUTE,
&ObjectAttributes,
&IoStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_OPEN_IF,
0,
NULL,
0
);
if (!NT_SUCCESS(Status)) {
printf("NtCreateFile: %x\n", Status);
return 0;
}
ThreadHandle =
CreateThread(
NULL,
0,
DisplayThread,
NULL,
0,
&ThreadId
);
CloseHandle(ThreadHandle);
getchar();
SetEvent(StopEvent);
return 0;
}