From b4ad2c27298fd9be425f1d1fa56be943c3bf8345 Mon Sep 17 00:00:00 2001 From: skarg Date: Tue, 14 Jul 2015 20:31:32 +0000 Subject: [PATCH] Added Wireshark extcap command line parameter interface to mstpcap utility. Tested with Wireshark on Windows (mostly working). To use extcap, run Wireshark and go to the About-dialog. Find a tab located there named "Folders". Locate the extcap search path. Copy the mstpcap.exe to that folder, which may not exist. Restart Wireshark, and look for "BACnet MS/TP on COMx" interfaces. Configure the interface to change baud rate. Capture directly from the interface. --- bacnet-stack/demo/mstpcap/main.c | 262 ++++++++++++++++++++++--------- bacnet-stack/ports/linux/rs485.c | 8 + bacnet-stack/ports/linux/rs485.h | 2 + bacnet-stack/ports/win32/rs485.c | 20 +++ bacnet-stack/ports/win32/rs485.h | 2 + 5 files changed, 216 insertions(+), 78 deletions(-) diff --git a/bacnet-stack/demo/mstpcap/main.c b/bacnet-stack/demo/mstpcap/main.c index 0ab26669..9842dd69 100644 --- a/bacnet-stack/demo/mstpcap/main.c +++ b/bacnet-stack/demo/mstpcap/main.c @@ -47,11 +47,15 @@ #include "rs485.h" #include "crc.h" #include "mstptext.h" +#include "filename.h" #include "version.h" #include "dlmstp.h" /* I-Am decoding */ #include "iam.h" +/* define our Data Link Type for libPCAP */ +#define DLT_BACNET_MS_TP 165 +/* local min/max macros */ #ifndef max #define max(a,b) (((a) (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) @@ -309,12 +313,12 @@ static void packet_statistics_print( unsigned i; /* loop counter */ unsigned node_count = 0; - fprintf(stdout, "\r\n"); - fprintf(stdout, "==== MS/TP Frame Counts ====\r\n"); + fprintf(stdout, "\n"); + fprintf(stdout, "==== MS/TP Frame Counts ====\n"); fprintf(stdout, "%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-7s", "MAC", "Device", "Tokens", "PFM", "RPFM", "DER", "Postpd", "DNER", "TestReq", "TestRsp"); - fprintf(stdout, "\r\n"); + fprintf(stdout, "\n"); for (i = 0; i < MAX_MSTP_DEVICES; i++) { /* check for masters or slaves */ if ((MSTP_Statistics[i].token_count) || (MSTP_Statistics[i].der_reply) @@ -337,17 +341,17 @@ static void packet_statistics_print( (long unsigned int) MSTP_Statistics[i].dner_count, (long unsigned int) MSTP_Statistics[i].test_request_count, (long unsigned int) MSTP_Statistics[i].test_response_count); - fprintf(stdout, "\r\n"); + fprintf(stdout, "\n"); } } - fprintf(stdout, "Node Count: %u\r\n", node_count); + fprintf(stdout, "Node Count: %u\n", node_count); node_count = 0; - fprintf(stdout, "\r\n"); - fprintf(stdout, "==== MS/TP Usage and Timing Maximums ====\r\n"); + fprintf(stdout, "\n"); + fprintf(stdout, "==== MS/TP Usage and Timing Maximums ====\n"); fprintf(stdout, "%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-7s", "MAC", "MaxMstr", "Retries", "Npoll", "Self", "Treply", "Tusage", "Trpfm", "Tder", "Tpostpd"); - fprintf(stdout, "\r\n"); + fprintf(stdout, "\n"); for (i = 0; i < MAX_MSTP_DEVICES; i++) { /* check for masters or slaves */ if ((MSTP_Statistics[i].token_count) || (MSTP_Statistics[i].der_reply) @@ -365,11 +369,11 @@ static void packet_statistics_print( (long unsigned int) MSTP_Statistics[i].pfm_reply, (long unsigned int) MSTP_Statistics[i].der_reply, (long unsigned int) MSTP_Statistics[i].reply_postponed); - fprintf(stdout, "\r\n"); + fprintf(stdout, "\n"); } } - fprintf(stdout, "Node Count: %u\r\n", node_count); - fprintf(stdout, "Invalid Frame Count: %lu\r\n", + fprintf(stdout, "Node Count: %u\n", node_count); + fprintf(stdout, "Invalid Frame Count: %lu\n", (long unsigned int) Invalid_Frame_Count); } @@ -430,16 +434,45 @@ static char Capture_Filename[32] = "mstp_20090123091200.cap"; static FILE *pFile = NULL; /* stream pointer */ #if defined(_WIN32) static HANDLE hPipe = NULL; /* pipe handle */ - +static PSECURITY_DESCRIPTOR pPipeSD = NULL; static void named_pipe_create( char *name) { + SECURITY_ATTRIBUTES sa; + + pPipeSD = (PSECURITY_DESCRIPTOR)(malloc(SECURITY_DESCRIPTOR_MIN_LENGTH)); + if (!pPipeSD) { + Exit_Requested = true; + return; + } + if (!InitializeSecurityDescriptor(pPipeSD, SECURITY_DESCRIPTOR_REVISION)) { + free(pPipeSD); + pPipeSD = NULL; + Exit_Requested = true; + return; + } + /* When an object has no DACL (when the pDacl parameter is NULL), + no protection is assigned to the object, and all access requests + are granted. To help maintain security, restrict access by using + a DACL. */ + if (!SetSecurityDescriptorDacl(pPipeSD, TRUE, (PACL)NULL, FALSE)) { + free(pPipeSD); + pPipeSD = NULL; + Exit_Requested = true; + return; + } + // now set up the security attributes + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = pPipeSD; + sa.bInheritHandle = TRUE; fprintf(stdout, "mstpcap: Creating Named Pipe \"%s\"\n", name); hPipe = CreateNamedPipe(name, PIPE_ACCESS_OUTBOUND, - PIPE_TYPE_MESSAGE | PIPE_WAIT, 1, 65536, 65536, 300, NULL); + PIPE_TYPE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0L, &sa); if (hPipe == INVALID_HANDLE_VALUE) { RS485_Print_Error(); + Exit_Requested = true; return; } ConnectNamedPipe(hPipe, NULL); @@ -551,7 +584,7 @@ static void write_global_header( int32_t thiszone = 0; /* GMT to local correction */ uint32_t sigfigs = 0; /* accuracy of timestamps */ uint32_t snaplen = 65535; /* max length of captured packets, in octets */ - uint32_t network = 165; /* data link type - BACNET_MS_TP */ + uint32_t network = DLT_BACNET_MS_TP; /* data link type - BACNET_MS_TP */ /* create a new file. */ pFile = fopen(filename, "wb"); @@ -686,7 +719,7 @@ static bool test_global_header( return false; } count = fread(&network, sizeof(network), 1, pFile); - if ((count != 1) || (network != 165)) { + if ((count != 1) || (network != DLT_BACNET_MS_TP)) { fprintf(stderr, "mstpcap: invalid data link type (DLT)\n"); fclose(pFile); pFile = NULL; @@ -836,6 +869,9 @@ static BOOL WINAPI CtrlCHandler( DisconnectNamedPipe(hPipe); CloseHandle(hPipe); } + if (pPipeSD) { + free(pPipeSD); + } /* signal to main loop to exit */ Exit_Requested = true; while (Exit_Requested) { @@ -845,25 +881,6 @@ static BOOL WINAPI CtrlCHandler( return TRUE; } - -/************************************************************************* -* Description: print available COM ports -* Returns: none -* Notes: none -**************************************************************************/ -static void print_com_ports( - void) -{ - unsigned i = 0; - - printf("List of available COM ports:\r\n"); - /* try to open all 255 COM ports */ - for (i = 1; i < 256; i++) { - if (RS485_Interface_Valid(i)) { - printf("COM%u\r\n", i); - } - } -} #else static void sig_int( int signo) @@ -896,6 +913,51 @@ void filename_create_new( write_global_header(&Capture_Filename[0]); } +static void print_usage( + char *filename) +{ + printf("Usage: %s", filename); + printf(" [--scan ]\n"); + printf(" [--extcap-interface port]\n"); + printf(" [--extcap-interfaces][--extcap-dlts][--extcap-config]\n"); + printf(" [--capture][--baud baud][--fifo pipe]\n"); + printf(" [--version][--help]\n"); +} + +static void print_help(char *filename) { + printf("%s --scan \n" + "perform statistic analysis on MS/TP capture file.\n", + filename); + printf("\n"); + printf("Captures MS/TP packets from a serial interface\n" + "and saves them to a file. Saves packets in a\n" + "filename mstp_20090123091200.cap that has data and time.\n" + "After receiving 65535 packets, a new file is created.\n" "\n" + "Command line options:\n" + "[--extcap-interface port] - serial interface.\n" +#if defined(_WIN32) + " Supported values: COM1, COM2, etc.\n" +#else + " Supported values: /dev/ttyS0, /dev/ttyUSB0, etc.\n" +#endif + "[--baud baud] - MS/TP port baud rate.\n" + " Supported values: 9600, 19200, 38400, 57600, 76800, 115200.\n" + " Defaults to 38400.\n" + "[--fifo pipe] - FIFO pipe path and name\n" +#if defined(_WIN32) + " Supported values: \\\\.\\pipe\\wireshark\n" +#else + " Supported values: any file name\n" +#endif + " Use that name as the interface name in Wireshark.\n"); + printf("\n"); + printf("%s [--extcap-interfaces][--extcap-dlts][--extcap-config]\n" + "[--capture][--baud baud][--fifo pipe]\n" + "[--extcap-interface iface]\n" + "Usage from Wireshark ExtCap interface\n", + filename); +} + /* simple test to packetize the data and print it */ int main( int argc, @@ -904,6 +966,8 @@ int main( volatile struct mstp_port_struct_t *mstp_port; long my_baud = 38400; uint32_t packet_count = 0; + int argi = 0; + char *filename = NULL; MSTP_Port.InputBuffer = &RxBuffer[0]; MSTP_Port.InputBufferSize = sizeof(RxBuffer); @@ -918,40 +982,35 @@ int main( mstp_port = &MSTP_Port; MSTP_Init(mstp_port); packet_statistics_clear(); - /* initialize our interface */ - if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { - printf("mstpcap --scan \r\n" - "perform statistic analysis on MS/TP capture file.\r\n"); - printf("\r\n"); - printf("mstpcap [interface] [baud] [named pipe]\r\n" - "Captures MS/TP packets from a serial interface\r\n" - "and save them to a file. Saves packets in a\r\n" - "filename mstp_20090123091200.cap that has data and time.\r\n" - "After receiving 65535 packets, a new file is created.\r\n" "\r\n" - "Command line options:\r\n" "[interface] - serial interface.\r\n" - " defaults to COM4 on Windows, and /dev/ttyUSB0 on linux.\r\n" - "[baud] - baud rate. 9600, 19200, 38400, 57600, 115200\r\n" - " defaults to 38400.\r\n" - "[named pipe] - use \\\\.\\pipe\\wireshark as the name\r\n" - " and set that name as the interface name in Wireshark\r\n"); - return 0; - } - if ((argc > 1) && (strcmp(argv[1], "--version") == 0)) { - printf("mstpcap %s\r\n", BACNET_VERSION_TEXT); - printf("Copyright (C) 2011 by Steve Karg\r\n" - "This is free software; see the source for copying conditions.\r\n" - "There is NO warranty; not even for MERCHANTABILITY or\r\n" - "FITNESS FOR A PARTICULAR PURPOSE.\r\n"); - return 0; - } - if ((argc > 1) && (strcmp(argv[1], "--scan") == 0)) { - if (argc > 2) { - printf("Scanning %s\r\n", argv[2]); + /* decode any command line parameters */ + filename = filename_remove_path(argv[0]); + for (argi = 1; argi < argc; argi++) { + if (strcmp(argv[argi], "--help") == 0) { + print_usage(filename); + print_help(filename); + return 0; + } + if (strcmp(argv[argi], "--version") == 0) { + printf("mstpcap %s\n", BACNET_VERSION_TEXT); + printf("Copyright (C) 2011 by Steve Karg\n" + "This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or\n" + "FITNESS FOR A PARTICULAR PURPOSE.\n"); + return 0; + } + if (strcmp(argv[argi], "--scan") == 0) { + argi++; + if (argi >= argc) { + printf("An file name must be provided.\n"); + return 1; + } + printf("Scanning %s\n", argv[argi]); /* perform statistics on the file */ - if (test_global_header(argv[2])) { + if (test_global_header(argv[argi])) { while (read_received_packet(mstp_port)) { packet_count++; - fprintf(stderr, "\r%u packets", (unsigned) packet_count); + fprintf(stderr, "\r%u packets", + (unsigned) packet_count); } if (packet_count) { packet_statistics_print(); @@ -959,20 +1018,70 @@ int main( } else { fprintf(stderr, "File header does not match.\n"); } - return 1; + } + if (strcmp(argv[argi], "--extcap-interfaces") == 0) { + RS485_Print_Ports(); + return 0; + } + if (strcmp(argv[argi], "--extcap-dlts") == 0) { + argi++; + if (argi >= argc) { + printf("An interface must be provided.\n"); + return 0; + } + printf("dlt {number=%u}{name=BACnet MS/TP}" + "{display=BACnet MS/TP}\n", + DLT_BACNET_MS_TP); + Exit_Requested = true; + } + if (strcmp(argv[argi], "--extcap-config") == 0) { + printf("arg {number=0}{call=--baud}{display=Baud Rate}" + "{tooltip=Serial port baud rate in bits per second}" + "{type=selector}\n"); + printf("value {arg=0}{value=9600}{display=9600}{default=false}\n"); + printf("value {arg=0}{value=19200}{display=19200}{default=false}\n"); + printf("value {arg=0}{value=38400}{display=38400}{default=true}\n"); + printf("value {arg=0}{value=57600}{display=57600}{default=false}\n"); + printf("value {arg=0}{value=76800}{display=76800}{default=false}\n"); + printf("value {arg=0}{value=115200}{display=115200}{default=false}\n"); + return 0; + } + if (strcmp(argv[argi], "--capture") == 0) { + /* do nothing - fall through and start running! */ + } + if (strcmp(argv[argi], "--extcap-interface") == 0) { + argi++; + if (argi >= argc) { + printf("An interface must be provided or " + "the selection must be displayed.\n"); + return 0; + } + RS485_Set_Interface(argv[argi]); + } + if (strcmp(argv[argi], "--baud") == 0) { + argi++; + if (argi >= argc) { + printf("A baud rate must be provided.\n"); + return 0; + } + my_baud = strtol(argv[argi], NULL, 0); + RS485_Set_Baud_Rate(my_baud); + } + if (strcmp(argv[argi], "--fifo") == 0) { + argi++; + if (argi >= argc) { + printf("A named pipe must be provided.\n"); + return 0; + } + named_pipe_create(argv[argi]); } } - if (argc > 1) { - RS485_Set_Interface(argv[1]); - } else { -#if defined(_WIN32) - print_com_ports(); + if (Exit_Requested) { return 0; -#endif } - if (argc > 2) { - my_baud = strtol(argv[2], NULL, 0); - RS485_Set_Baud_Rate(my_baud); + if (argc <= 1) { + RS485_Print_Ports(); + return 0; } atexit(cleanup); RS485_Initialize(); @@ -985,9 +1094,6 @@ int main( #else signal_init(); #endif - if (argc > 3) { - named_pipe_create(argv[3]); - } filename_create_new(); /* run forever */ for (;;) { diff --git a/bacnet-stack/ports/linux/rs485.c b/bacnet-stack/ports/linux/rs485.c index 3be957a4..e60eed87 100644 --- a/bacnet-stack/ports/linux/rs485.c +++ b/bacnet-stack/ports/linux/rs485.c @@ -624,6 +624,14 @@ void RS485_Initialize( printf("=success!\n"); } +void RS485_Print_Ports(void) +{ + /* note: format for Wireshark ExtCap */ + //printf("interface {value=/dev/ttyUSB%u}" + // "{display=MS/TP Capture on /dev/ttyUSB%u}\n", i, i); + /* FIXME: add code to print ports */ +} + #ifdef TEST_RS485 #include int main( diff --git a/bacnet-stack/ports/linux/rs485.h b/bacnet-stack/ports/linux/rs485.h index e3e4e979..84a06df6 100644 --- a/bacnet-stack/ports/linux/rs485.h +++ b/bacnet-stack/ports/linux/rs485.h @@ -67,6 +67,8 @@ extern "C" { void RS485_Cleanup( void); + void RS485_Print_Ports( + void); #ifdef __cplusplus } diff --git a/bacnet-stack/ports/win32/rs485.c b/bacnet-stack/ports/win32/rs485.c index 0ab9a62f..6b543513 100644 --- a/bacnet-stack/ports/win32/rs485.c +++ b/bacnet-stack/ports/win32/rs485.c @@ -489,6 +489,26 @@ void RS485_Check_UART_Data( } } +/************************************************************************* +* Description: print available COM ports +* Returns: none +* Notes: none +**************************************************************************/ +void RS485_Print_Ports( + void) +{ + unsigned i = 0; + + /* try to open all 255 COM ports */ + for (i = 1; i < 256; i++) { + if (RS485_Interface_Valid(i)) { + /* note: format for Wireshark ExtCap */ + printf("interface {value=COM%u}" + "{display=BACnet MS/TP on COM%u}\n", i, i); + } + } +} + #ifdef TEST_RS485 #include "mstpdef.h" diff --git a/bacnet-stack/ports/win32/rs485.h b/bacnet-stack/ports/win32/rs485.h index c9a75304..1867b3f4 100644 --- a/bacnet-stack/ports/win32/rs485.h +++ b/bacnet-stack/ports/win32/rs485.h @@ -70,6 +70,8 @@ extern "C" { bool RS485_Interface_Valid( unsigned port_number); + void RS485_Print_Ports( + void); #ifdef __cplusplus