mirror of
				https://github.com/stargieg/bacnet-stack
				synced 2025-10-26 23:35:52 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			1252 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1252 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*####COPYRIGHTBEGIN####
 | |
|  -------------------------------------------
 | |
|  Copyright (C) 2008 Steve Karg
 | |
| 
 | |
|  This program is free software; you can redistribute it and/or
 | |
|  modify it under the terms of the GNU General Public License
 | |
|  as published by the Free Software Foundation; either version 2
 | |
|  of the License, or (at your option) any later version.
 | |
| 
 | |
|  This program is distributed in the hope that it will be useful,
 | |
|  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  GNU General Public License for more details.
 | |
| 
 | |
|  You should have received a copy of the GNU General Public License
 | |
|  along with this program; if not, write to:
 | |
|  The Free Software Foundation, Inc.
 | |
|  59 Temple Place - Suite 330
 | |
|  Boston, MA  02111-1307
 | |
|  USA.
 | |
| 
 | |
|  As a special exception, if other files instantiate templates or
 | |
|  use macros or inline functions from this file, or you compile
 | |
|  this file and link it with other works to produce a work based
 | |
|  on this file, this file does not by itself cause the resulting
 | |
|  work to be covered by the GNU General Public License. However
 | |
|  the source code for this file must still be made available in
 | |
|  accordance with section (3) of the GNU General Public License.
 | |
| 
 | |
|  This exception does not invalidate any other reasons why a work
 | |
|  based on this file might be covered by the GNU General Public
 | |
|  License.
 | |
|  -------------------------------------------
 | |
| ####COPYRIGHTEND####*/
 | |
| #include <stddef.h>
 | |
| #include <stdint.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| #include <time.h>
 | |
| /* OS specific include*/
 | |
| #include "net.h"
 | |
| #include "timer.h"
 | |
| /* local includes */
 | |
| #include "bytes.h"
 | |
| #include "rs485.h"
 | |
| #include "crc.h"
 | |
| #include "mstptext.h"
 | |
| #include "filename.h"
 | |
| #include "version.h"
 | |
| #include "dlmstp.h"
 | |
| /* I-Am decoding */
 | |
| #include "iam.h"
 | |
| 
 | |
| #ifdef _WIN32
 | |
| #define strncasecmp(x,y,z) _strnicmp(x,y,z)
 | |
| #endif
 | |
| 
 | |
| /* 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))
 | |
| #endif
 | |
| 
 | |
| #define MSTP_HEADER_MAX (2+1+1+1+2+1)
 | |
| 
 | |
| /* local port data - shared with RS-485 */
 | |
| static volatile struct mstp_port_struct_t MSTP_Port;
 | |
| /* track the receive state to know when there is a broken packet */
 | |
| static MSTP_RECEIVE_STATE MSTP_Receive_State = MSTP_RECEIVE_STATE_IDLE;
 | |
| /* buffers needed by mstp port struct */
 | |
| static uint8_t RxBuffer[MAX_MPDU];
 | |
| static uint8_t TxBuffer[MAX_MPDU];
 | |
| /* method to tell main loop to exit from CTRL-C or other signals */
 | |
| static volatile bool Exit_Requested;
 | |
| /* flag to indicate Wireshark is running the show - no stdout or stderr */
 | |
| static bool Wireshark_Capture;
 | |
| 
 | |
| /* statistics derived from monitoring the network for each node */
 | |
| struct mstp_statistics {
 | |
|     /* counts how many times the node passes the token */
 | |
|     uint32_t token_count;
 | |
|     /* counts how many times the node receives the token */
 | |
|     uint32_t token_received_count;
 | |
|     /* counts how many times the node polls for another master */
 | |
|     uint32_t pfm_count;
 | |
|     /* counts how many times the node replies to polls from another master */
 | |
|     uint32_t rpfm_count;
 | |
|     /* counts how many times the node sends reply postponed */
 | |
|     uint32_t reply_postponed_count;
 | |
|     /* counts how many times the node sends a test request */
 | |
|     uint32_t test_request_count;
 | |
|     /* counts how many times the node sends a test response */
 | |
|     uint32_t test_response_count;
 | |
|     /* counts how many times the node sends a DER frame */
 | |
|     uint32_t der_count;
 | |
|     /* counts how many times the node sends a DNER frame */
 | |
|     uint32_t dner_count;
 | |
|     /* -- inferred data -- */
 | |
|     /* counts how many times the node gets a second token */
 | |
|     uint32_t token_retries;
 | |
|     /* delay after poll for master */
 | |
|     uint32_t tusage_timeout;
 | |
|     /* highest number MAC during poll for master */
 | |
|     uint8_t max_master;
 | |
|     /* highest number of frames sent during a token */
 | |
|     uint8_t max_info_frames;
 | |
|     /* how long it takes a node to pass the token */
 | |
|     uint32_t token_reply;
 | |
|     /* how long it takes a node to reply to PFM */
 | |
|     uint32_t pfm_reply;
 | |
|     /* how long it takes a node to reply to DER */
 | |
|     uint32_t der_reply;
 | |
|     /* how long it takes a node to send a reply post poned */
 | |
|     uint32_t reply_postponed;
 | |
|     /* number of tokens received before a Poll For Master cycle is executed */
 | |
|     uint32_t npoll;
 | |
|     /* number of total tokens at the last PFM */
 | |
|     uint32_t last_pfm_tokens;
 | |
|     /* Addendum 2008v - sending tokens to myself */
 | |
|     /* counts how many times the node passes the token */
 | |
|     uint32_t self_token_count;
 | |
|     /* counts how many times the node sends the token out-of-order (ooo) */
 | |
|     uint32_t ooo_token_count;
 | |
|     /* if we see an I-Am message from this node, store the Device ID */
 | |
|     uint32_t device_id;
 | |
| };
 | |
| 
 | |
| #define MAX_MSTP_DEVICES 256
 | |
| static struct mstp_statistics MSTP_Statistics[MAX_MSTP_DEVICES];
 | |
| static uint32_t Invalid_Frame_Count;
 | |
| 
 | |
| static uint32_t timeval_diff_ms(
 | |
|     struct timeval *old,
 | |
|     struct timeval *now)
 | |
| {
 | |
|     uint32_t ms = 0;
 | |
| 
 | |
|     /* convert to milliseconds */
 | |
|     ms = (now->tv_sec - old->tv_sec) * 1000 + (now->tv_usec -
 | |
|         old->tv_usec) / 1000;
 | |
| 
 | |
|     return ms;
 | |
| }
 | |
| 
 | |
| static void mstp_monitor_i_am(
 | |
|     uint8_t mac,
 | |
|     uint8_t * pdu,
 | |
|     uint16_t pdu_len)
 | |
| {
 | |
|     BACNET_ADDRESS src = { 0 };
 | |
|     BACNET_ADDRESS dest = { 0 };
 | |
|     BACNET_NPDU_DATA npdu_data = { 0 };
 | |
|     int apdu_offset = 0;
 | |
|     uint16_t apdu_len = 0;
 | |
|     uint8_t *apdu = NULL;
 | |
|     uint8_t pdu_type = 0;
 | |
|     uint8_t service_choice = 0;
 | |
|     uint8_t *service_request = NULL;
 | |
|     uint32_t device_id = 0;
 | |
|     int len = 0;
 | |
| 
 | |
|     if (pdu[0] == BACNET_PROTOCOL_VERSION) {
 | |
|         MSTP_Fill_BACnet_Address(&src, mac);
 | |
|         apdu_offset = npdu_decode(&pdu[0], &dest, &src, &npdu_data);
 | |
|         if ((!npdu_data.network_layer_message) && (apdu_offset > 0) &&
 | |
|             (apdu_offset < pdu_len) && (src.net == 0)) {
 | |
|             apdu_len = pdu_len - apdu_offset;
 | |
|             apdu = &pdu[apdu_offset];
 | |
|             pdu_type = apdu[0] & 0xF0;
 | |
|             if ((pdu_type == PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) &&
 | |
|                 (apdu_len >= 2)) {
 | |
|                 service_choice = apdu[1];
 | |
|                 service_request = &apdu[2];
 | |
|                 if (service_choice == SERVICE_UNCONFIRMED_I_AM) {
 | |
|                     len =
 | |
|                         iam_decode_service_request(service_request, &device_id,
 | |
|                         NULL, NULL, NULL);
 | |
|                     if (len != -1) {
 | |
|                         MSTP_Statistics[mac].device_id = device_id;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void packet_statistics(
 | |
|     struct timeval *tv,
 | |
|     volatile struct mstp_port_struct_t *mstp_port)
 | |
| {
 | |
|     static struct timeval old_tv = { 0 };
 | |
|     static uint8_t old_frame = 255;
 | |
|     static uint8_t old_src = 255;
 | |
|     static uint8_t old_dst = 255;
 | |
|     static uint8_t old_token_dst = 255;
 | |
|     uint8_t frame, src, dst;
 | |
|     uint32_t delta;
 | |
|     uint32_t npoll;
 | |
| 
 | |
|     dst = mstp_port->DestinationAddress;
 | |
|     src = mstp_port->SourceAddress;
 | |
|     frame = mstp_port->FrameType;
 | |
|     switch (frame) {
 | |
|         case FRAME_TYPE_TOKEN:
 | |
|             MSTP_Statistics[src].token_count++;
 | |
|             MSTP_Statistics[dst].token_received_count++;
 | |
|             if (src == dst) {
 | |
|                 MSTP_Statistics[src].self_token_count++;
 | |
|             }
 | |
|             if (old_frame == FRAME_TYPE_TOKEN) {
 | |
|                 if ((old_dst == dst) && (old_src == src)) {
 | |
|                     /* repeated token */
 | |
|                     MSTP_Statistics[dst].token_retries++;
 | |
|                     /* Tusage_timeout */
 | |
|                     delta = timeval_diff_ms(&old_tv, tv);
 | |
|                     if (delta > MSTP_Statistics[src].tusage_timeout) {
 | |
|                         MSTP_Statistics[src].tusage_timeout = delta;
 | |
|                     }
 | |
|                 } else if (old_dst == src) {
 | |
|                     /* token to token response time */
 | |
|                     delta = timeval_diff_ms(&old_tv, tv);
 | |
|                     if (delta > MSTP_Statistics[src].token_reply) {
 | |
|                         MSTP_Statistics[src].token_reply = delta;
 | |
|                     }
 | |
|                 }
 | |
|             } else if ((old_frame == FRAME_TYPE_POLL_FOR_MASTER) &&
 | |
|                 (old_src == src)) {
 | |
|                 /* Tusage_timeout */
 | |
|                 delta = timeval_diff_ms(&old_tv, tv);
 | |
|                 if (delta > MSTP_Statistics[src].tusage_timeout) {
 | |
|                     MSTP_Statistics[src].tusage_timeout = delta;
 | |
|                 }
 | |
|             }
 | |
|             if (old_token_dst != src) {
 | |
|                 /* out-of-order Token sender */
 | |
|                 MSTP_Statistics[src].ooo_token_count++;
 | |
|             }
 | |
|             old_token_dst = dst;
 | |
|             break;
 | |
|         case FRAME_TYPE_POLL_FOR_MASTER:
 | |
|             if (MSTP_Statistics[src].last_pfm_tokens) {
 | |
|                 npoll =
 | |
|                     MSTP_Statistics[src].token_received_count -
 | |
|                     MSTP_Statistics[src].last_pfm_tokens;
 | |
|                 if (npoll > MSTP_Statistics[src].npoll) {
 | |
|                     MSTP_Statistics[src].npoll = npoll;
 | |
|                 }
 | |
|             }
 | |
|             MSTP_Statistics[src].last_pfm_tokens =
 | |
|                 MSTP_Statistics[src].token_received_count;
 | |
|             MSTP_Statistics[src].pfm_count++;
 | |
|             if (dst > MSTP_Statistics[src].max_master) {
 | |
|                 MSTP_Statistics[src].max_master = dst;
 | |
|             }
 | |
|             if ((old_frame == FRAME_TYPE_POLL_FOR_MASTER) && (old_src == src)) {
 | |
|                 /* Tusage_timeout - sole master */
 | |
|                 delta = timeval_diff_ms(&old_tv, tv);
 | |
|                 if (delta > MSTP_Statistics[src].tusage_timeout) {
 | |
|                     MSTP_Statistics[src].tusage_timeout = delta;
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         case FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER:
 | |
|             MSTP_Statistics[src].rpfm_count++;
 | |
|             if (old_frame == FRAME_TYPE_POLL_FOR_MASTER) {
 | |
|                 delta = timeval_diff_ms(&old_tv, tv);
 | |
|                 if (delta > MSTP_Statistics[src].pfm_reply) {
 | |
|                     MSTP_Statistics[src].pfm_reply = delta;
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         case FRAME_TYPE_TEST_REQUEST:
 | |
|             MSTP_Statistics[src].test_request_count++;
 | |
|             break;
 | |
|         case FRAME_TYPE_TEST_RESPONSE:
 | |
|             MSTP_Statistics[src].test_response_count++;
 | |
|             break;
 | |
|         case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY:
 | |
|             MSTP_Statistics[src].der_count++;
 | |
|             break;
 | |
|         case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
 | |
|             MSTP_Statistics[src].dner_count++;
 | |
|             if ((old_frame == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) &&
 | |
|                 (old_dst == src)) {
 | |
|                 /* DER response time */
 | |
|                 delta = timeval_diff_ms(&old_tv, tv);
 | |
|                 if (delta > MSTP_Statistics[src].der_reply) {
 | |
|                     MSTP_Statistics[src].der_reply = delta;
 | |
|                 }
 | |
|             }
 | |
|             if ((mstp_port->ReceivedValidFrame) ||
 | |
|                 (mstp_port->ReceivedValidFrameNotForUs)) {
 | |
|                 if ((mstp_port->DataLength <= mstp_port->InputBufferSize) &&
 | |
|                     (mstp_port->DataLength > 0)) {
 | |
|                     mstp_monitor_i_am(src, &mstp_port->InputBuffer[0],
 | |
|                         mstp_port->DataLength);
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         case FRAME_TYPE_REPLY_POSTPONED:
 | |
|             MSTP_Statistics[src].reply_postponed_count++;
 | |
|             if ((old_frame == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) &&
 | |
|                 (old_dst == src)) {
 | |
|                 /* Postponed response time */
 | |
|                 delta = timeval_diff_ms(&old_tv, tv);
 | |
|                 if (delta > MSTP_Statistics[src].reply_postponed) {
 | |
|                     MSTP_Statistics[src].reply_postponed = delta;
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         default:
 | |
|             break;
 | |
|     }
 | |
| 
 | |
|     /* update the old variables */
 | |
|     old_dst = dst;
 | |
|     old_src = src;
 | |
|     old_frame = frame;
 | |
|     old_tv.tv_sec = tv->tv_sec;
 | |
|     old_tv.tv_usec = tv->tv_usec;
 | |
| }
 | |
| 
 | |
| static void packet_statistics_print(
 | |
|     void)
 | |
| {
 | |
|     unsigned i; /* loop counter */
 | |
|     unsigned node_count = 0;
 | |
|     long unsigned int self_or_ooo_count;
 | |
| 
 | |
|     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, "\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)
 | |
|             || (MSTP_Statistics[i].pfm_count)) {
 | |
|             node_count++;
 | |
|             fprintf(stdout, "%-8u", i);
 | |
|             if (MSTP_Statistics[i].device_id <= 4194303) {
 | |
|                 fprintf(stdout, "%-8lu",
 | |
|                     (long unsigned int) MSTP_Statistics[i].device_id);
 | |
|             } else {
 | |
|                 fprintf(stdout, "%-8s", "-");
 | |
|             }
 | |
|             fprintf(stdout, "%-8lu%-8lu%-8lu%-8lu",
 | |
|                 (long unsigned int) MSTP_Statistics[i].token_count,
 | |
|                 (long unsigned int) MSTP_Statistics[i].pfm_count,
 | |
|                 (long unsigned int) MSTP_Statistics[i].rpfm_count,
 | |
|                 (long unsigned int) MSTP_Statistics[i].der_count);
 | |
|             fprintf(stdout, "%-8lu%-8lu%-8lu%-7lu",
 | |
|                 (long unsigned int) MSTP_Statistics[i].reply_postponed_count,
 | |
|                 (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, "\n");
 | |
|         }
 | |
|     }
 | |
|     fprintf(stdout, "Node Count: %u\n", node_count);
 | |
|     node_count = 0;
 | |
|     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/TT", "Treply", "Tusage", "Trpfm",
 | |
|         "Tder", "Tpostpd");
 | |
|     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)
 | |
|             || (MSTP_Statistics[i].pfm_count)) {
 | |
|             node_count++;
 | |
|             self_or_ooo_count = MSTP_Statistics[i].self_token_count +
 | |
|                 MSTP_Statistics[i].ooo_token_count;
 | |
|             fprintf(stdout, "%-8u", i);
 | |
|             fprintf(stdout, "%-8lu%-8lu%-8lu%-8lu%-8lu",
 | |
|                 (long unsigned int) MSTP_Statistics[i].max_master,
 | |
|                 (long unsigned int) MSTP_Statistics[i].token_retries,
 | |
|                 (long unsigned int) MSTP_Statistics[i].npoll,
 | |
|                 self_or_ooo_count,
 | |
|                 (long unsigned int) MSTP_Statistics[i].token_reply);
 | |
|             fprintf(stdout, "%-8lu%-8lu%-8lu%-7lu",
 | |
|                 (long unsigned int) MSTP_Statistics[i].tusage_timeout,
 | |
|                 (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, "\n");
 | |
|         }
 | |
|     }
 | |
|     fprintf(stdout, "Node Count: %u\n", node_count);
 | |
|     fprintf(stdout, "Invalid Frame Count: %lu\n",
 | |
|         (long unsigned int) Invalid_Frame_Count);
 | |
| }
 | |
| 
 | |
| static void packet_statistics_clear(
 | |
|     void)
 | |
| {
 | |
|     unsigned i = 0;
 | |
| 
 | |
|     memset(&MSTP_Statistics[0], 0, sizeof(MSTP_Statistics));
 | |
|     for (i = 0; i < MAX_MSTP_DEVICES; i++) {
 | |
|         MSTP_Statistics[i].device_id = 0xFFFFFFFF;
 | |
|     }
 | |
|     Invalid_Frame_Count = 0;
 | |
| }
 | |
| 
 | |
| static uint32_t Timer_Silence(
 | |
|     void *pArg)
 | |
| {
 | |
|     return timer_milliseconds(TIMER_SILENCE);
 | |
| }
 | |
| 
 | |
| static void Timer_Silence_Reset(
 | |
|     void *pArg)
 | |
| {
 | |
|     timer_reset(TIMER_SILENCE);
 | |
| }
 | |
| 
 | |
| /* functions used by the MS/TP state machine to put or get data */
 | |
| uint16_t MSTP_Put_Receive(
 | |
|     volatile struct mstp_port_struct_t *mstp_port)
 | |
| {
 | |
|     (void) mstp_port;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* for the MS/TP state machine to use for getting data to send */
 | |
| /* Return: amount of PDU data */
 | |
| uint16_t MSTP_Get_Send(
 | |
|     volatile struct mstp_port_struct_t * mstp_port,
 | |
|     unsigned timeout)
 | |
| {       /* milliseconds to wait for a packet */
 | |
|     (void) mstp_port;
 | |
|     (void) timeout;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| uint16_t MSTP_Get_Reply(
 | |
|     volatile struct mstp_port_struct_t * mstp_port,
 | |
|     unsigned timeout)
 | |
| {       /* milliseconds to wait for a packet */
 | |
|     (void) mstp_port;
 | |
|     (void) timeout;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static char Capture_Filename[32] = "mstp_20090123091200.cap";
 | |
| static FILE *pFile = NULL;      /* stream pointer */
 | |
| #if defined(_WIN32)
 | |
| static HANDLE hPipe = INVALID_HANDLE_VALUE;     /* pipe handle */
 | |
| static void named_pipe_create(
 | |
|     char *pipe_name)
 | |
| {
 | |
|     if (!Wireshark_Capture) {
 | |
|         fprintf(stdout, "mstpcap: Creating Named Pipe \"%s\"\n", pipe_name);
 | |
|     }
 | |
|     /* create the pipe */
 | |
|     while (hPipe == INVALID_HANDLE_VALUE)
 | |
|     {
 | |
|         /* use CreateFile rather than CreateNamedPipe */
 | |
|         hPipe = CreateFile(
 | |
|             pipe_name,
 | |
|             GENERIC_READ |
 | |
|             GENERIC_WRITE,
 | |
|             0,
 | |
|             NULL,
 | |
|             OPEN_EXISTING,
 | |
|             0,
 | |
|             NULL);
 | |
|         if (hPipe != INVALID_HANDLE_VALUE) {
 | |
|             break;
 | |
|         }
 | |
|         /* if an error occured at handle creation */
 | |
|         if (!WaitNamedPipe(pipe_name, 20000)) {
 | |
|             printf("Could not open pipe: waited for 20sec!\n"
 | |
|                 "If this message was issued before the 20sec finished,\n"
 | |
|                 "then the pipe doesn't exist!\n");
 | |
|             Exit_Requested = true;
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     ConnectNamedPipe(hPipe, NULL);
 | |
| }
 | |
| 
 | |
| size_t data_write(
 | |
|     const void *ptr,
 | |
|     size_t size,
 | |
|     size_t nitems)
 | |
| {
 | |
|     DWORD cbWritten = 0;
 | |
|     if (hPipe != INVALID_HANDLE_VALUE) {
 | |
|         (void) WriteFile(hPipe, /* handle to pipe  */
 | |
|             ptr,        /* buffer to write from  */
 | |
|             size * nitems,      /* number of bytes to write  */
 | |
|             &cbWritten, /* number of bytes written  */
 | |
|             NULL);      /* not overlapped I/O  */
 | |
|     }
 | |
| 
 | |
|     return fwrite(ptr, size, nitems, pFile);
 | |
| }
 | |
| 
 | |
| size_t data_write_header(
 | |
|     const void *ptr,
 | |
|     size_t size,
 | |
|     size_t nitems,
 | |
|     bool pipe_enable)
 | |
| {
 | |
|     DWORD cbWritten = 0;
 | |
|     if (pipe_enable && (hPipe != INVALID_HANDLE_VALUE)) {
 | |
|         (void) WriteFile(hPipe, /* handle to pipe  */
 | |
|             ptr,        /* buffer to write from  */
 | |
|             size * nitems,      /* number of bytes to write  */
 | |
|             &cbWritten, /* number of bytes written  */
 | |
|             NULL);      /* not overlapped I/O  */
 | |
|     }
 | |
| 
 | |
|     return fwrite(ptr, size, nitems, pFile);
 | |
| }
 | |
| #else
 | |
| static int FD_Pipe = -1;
 | |
| static void named_pipe_create(
 | |
|     char *name)
 | |
| {
 | |
|     int rv = 0;
 | |
|     rv = mkfifo(name, 0666);
 | |
|     if ((rv == -1) && (errno != EEXIST)) {
 | |
|         perror("Error creating named pipe");
 | |
|         exit(1);
 | |
|     }
 | |
|     FD_Pipe = open(name, O_WRONLY);
 | |
|     if (FD_Pipe == -1) {
 | |
|         perror("Error connecting to named pipe");
 | |
|         exit(1);
 | |
|     }
 | |
| }
 | |
| 
 | |
| size_t data_write(
 | |
|     const void *ptr,
 | |
|     size_t size,
 | |
|     size_t nitems)
 | |
| {
 | |
|     ssize_t bytes = 0;
 | |
|     if (FD_Pipe != -1) {
 | |
|         bytes = write(FD_Pipe, ptr, size * nitems);
 | |
|         bytes = bytes;
 | |
|     }
 | |
|     return fwrite(ptr, size, nitems, pFile);
 | |
| }
 | |
| 
 | |
| size_t data_write_header(
 | |
|     const void *ptr,
 | |
|     size_t size,
 | |
|     size_t nitems,
 | |
|     bool pipe_enable)
 | |
| {
 | |
|     ssize_t bytes = 0;
 | |
|     if (pipe_enable && (FD_Pipe != -1)) {
 | |
|         bytes = write(FD_Pipe, ptr, size * nitems);
 | |
|         bytes = bytes;
 | |
|     }
 | |
|     return fwrite(ptr, size, nitems, pFile);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static void filename_create(
 | |
|     char *filename)
 | |
| {
 | |
|     time_t my_time;
 | |
|     struct tm *today;
 | |
| 
 | |
|     if (filename) {
 | |
|         my_time = time(NULL);
 | |
|         today = localtime(&my_time);
 | |
|         sprintf(filename, "mstp_%04d%02d%02d%02d%02d%02d.cap",
 | |
|             1900 + today->tm_year, 1 + today->tm_mon, today->tm_mday,
 | |
|             today->tm_hour, today->tm_min, today->tm_sec);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* write packet to file in libpcap format */
 | |
| static void write_global_header(
 | |
|     const char *filename)
 | |
| {
 | |
|     static bool pipe_enable = true;     /* don't write more than one header */
 | |
|     uint32_t magic_number = 0xa1b2c3d4; /* magic number */
 | |
|     uint16_t version_major = 2; /* major version number */
 | |
|     uint16_t version_minor = 4; /* minor version number */
 | |
|     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 = DLT_BACNET_MS_TP;     /* data link type - BACNET_MS_TP */
 | |
| 
 | |
|     /* create a new file. */
 | |
|     pFile = fopen(filename, "wb");
 | |
|     if (pFile) {
 | |
|         (void) data_write_header(&magic_number, sizeof(magic_number), 1,
 | |
|             pipe_enable);
 | |
|         (void) data_write_header(&version_major, sizeof(version_major), 1,
 | |
|             pipe_enable);
 | |
|         (void) data_write_header(&version_minor, sizeof(version_minor), 1,
 | |
|             pipe_enable);
 | |
|         (void) data_write_header(&thiszone, sizeof(thiszone), 1, pipe_enable);
 | |
|         (void) data_write_header(&sigfigs, sizeof(sigfigs), 1, pipe_enable);
 | |
|         (void) data_write_header(&snaplen, sizeof(snaplen), 1, pipe_enable);
 | |
|         (void) data_write_header(&network, sizeof(network), 1, pipe_enable);
 | |
|         fflush(pFile);
 | |
|         if (!Wireshark_Capture) {
 | |
|             fprintf(stdout, "mstpcap: saving capture to %s\n", filename);
 | |
|         }
 | |
|     } else {
 | |
|         fprintf(stderr, "mstpcap[header]: failed to open %s: %s\n", filename,
 | |
|             strerror(errno));
 | |
|     }
 | |
|     if (pipe_enable) {
 | |
|         pipe_enable = false;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void write_received_packet(
 | |
|     volatile struct mstp_port_struct_t *mstp_port,
 | |
|     size_t header_len)
 | |
| {
 | |
|     uint32_t ts_sec = 0;    /* timestamp seconds */
 | |
|     uint32_t ts_usec = 0;   /* timestamp microseconds */
 | |
|     uint32_t incl_len = 0;  /* number of octets of packet saved in file */
 | |
|     uint32_t orig_len = 0;  /* actual length of packet */
 | |
|     uint8_t header[MSTP_HEADER_MAX] = {0};  /* MS/TP header */
 | |
|     struct timeval tv;
 | |
|     size_t max_data = 0;
 | |
| 
 | |
|     if (pFile) {
 | |
|         gettimeofday(&tv, NULL);
 | |
|         ts_sec = tv.tv_sec;
 | |
|         ts_usec = tv.tv_usec;
 | |
|         if ((mstp_port->ReceivedValidFrame) ||
 | |
|             (mstp_port->ReceivedValidFrameNotForUs)) {
 | |
|             packet_statistics(&tv, mstp_port);
 | |
|         }
 | |
|         (void) data_write(&ts_sec, sizeof(ts_sec), 1);
 | |
|         (void) data_write(&ts_usec, sizeof(ts_usec), 1);
 | |
|         if (mstp_port->ReceivedInvalidFrame) {
 | |
|             if (mstp_port->Index) {
 | |
|                 max_data = min(mstp_port->InputBufferSize, mstp_port->Index);
 | |
|                 incl_len = orig_len = header_len + max_data + 2/* checksum*/;
 | |
|             } else {
 | |
|                 /* header only */
 | |
|                 incl_len = orig_len = header_len;
 | |
|             }
 | |
|         } else {
 | |
|             if (mstp_port->DataLength) {
 | |
|                 max_data = min(mstp_port->InputBufferSize, mstp_port->DataLength);
 | |
|                 incl_len = orig_len = header_len + max_data + 2/* checksum*/;
 | |
|             } else {
 | |
|                 /* header only - or at least some bytes of the header */
 | |
|                 incl_len = orig_len = header_len;
 | |
|             }
 | |
|         }
 | |
|         (void) data_write(&incl_len, sizeof(incl_len), 1);
 | |
|         (void) data_write(&orig_len, sizeof(orig_len), 1);
 | |
|         if (header_len == 1) {
 | |
|             header[0] = mstp_port->DataRegister;
 | |
|         } else if (header_len == 2) {
 | |
|             header[0] = 0x55;
 | |
|             header[1] = mstp_port->DataRegister;
 | |
|         } else {
 | |
|             header[0] = 0x55;
 | |
|             header[1] = 0xFF;
 | |
|             header[2] = mstp_port->FrameType;
 | |
|             header[3] = mstp_port->DestinationAddress;
 | |
|             header[4] = mstp_port->SourceAddress;
 | |
|             header[5] = HI_BYTE(mstp_port->DataLength);
 | |
|             header[6] = LO_BYTE(mstp_port->DataLength);
 | |
|             header[7] = mstp_port->HeaderCRCActual;
 | |
|         }
 | |
|         (void) data_write(header, header_len, 1);
 | |
|         if (max_data) {
 | |
|             (void) data_write(mstp_port->InputBuffer, max_data, 1);
 | |
|             (void) data_write((char *) &mstp_port->DataCRCActualMSB, 1, 1);
 | |
|             (void) data_write((char *) &mstp_port->DataCRCActualLSB, 1, 1);
 | |
|         }
 | |
|     } else {
 | |
|         fprintf(stderr, "mstpcap[packet]: failed to open %s: %s\n",
 | |
|             Capture_Filename, strerror(errno));
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* read header from file in libpcap format */
 | |
| static bool test_global_header(
 | |
|     const char *filename)
 | |
| {
 | |
|     uint32_t magic_number = 0;  /* magic number */
 | |
|     uint16_t version_major = 0; /* major version number */
 | |
|     uint16_t version_minor = 0; /* minor version number */
 | |
|     int32_t thiszone = 0;       /* GMT to local correction */
 | |
|     uint32_t sigfigs = 0;       /* accuracy of timestamps */
 | |
|     uint32_t snaplen = 0;       /* max length of captured packets, in octets */
 | |
|     uint32_t network = 0;       /* data link type - BACNET_MS_TP */
 | |
|     size_t count = 0;
 | |
| 
 | |
|     /* open existing file. */
 | |
|     pFile = fopen(filename, "rb");
 | |
|     if (pFile) {
 | |
|         count = fread(&magic_number, sizeof(magic_number), 1, pFile);
 | |
|         if ((count != 1) || (magic_number != 0xa1b2c3d4)) {
 | |
|             fprintf(stderr, "mstpcap: invalid magic number\n");
 | |
|             fclose(pFile);
 | |
|             pFile = NULL;
 | |
|             return false;
 | |
|         }
 | |
|         count = fread(&version_major, sizeof(version_major), 1, pFile);
 | |
|         if ((count != 1) || (version_major != 2)) {
 | |
|             fprintf(stderr, "mstpcap: invalid major version\n");
 | |
|             fclose(pFile);
 | |
|             pFile = NULL;
 | |
|             return false;
 | |
|         }
 | |
|         count = fread(&version_minor, sizeof(version_minor), 1, pFile);
 | |
|         if ((count != 1) || (version_minor != 4)) {
 | |
|             fprintf(stderr, "mstpcap: invalid minor version\n");
 | |
|             fclose(pFile);
 | |
|             pFile = NULL;
 | |
|             return false;
 | |
|         }
 | |
|         count = fread(&thiszone, sizeof(thiszone), 1, pFile);
 | |
|         if ((count != 1) || (thiszone != 0)) {
 | |
|             fprintf(stderr, "mstpcap: invalid time zone\n");
 | |
|             fclose(pFile);
 | |
|             pFile = NULL;
 | |
|             return false;
 | |
|         }
 | |
|         count = fread(&sigfigs, sizeof(sigfigs), 1, pFile);
 | |
|         if ((count != 1) || (sigfigs != 0)) {
 | |
|             fprintf(stderr, "mstpcap: invalid time stamp accuracy\n");
 | |
|             fclose(pFile);
 | |
|             pFile = NULL;
 | |
|             return false;
 | |
|         }
 | |
|         count = fread(&snaplen, sizeof(snaplen), 1, pFile);
 | |
|         if (count != 1) {
 | |
|             fprintf(stderr, "mstpcap: unable to read SNAP length\n");
 | |
|             fclose(pFile);
 | |
|             pFile = NULL;
 | |
|             return false;
 | |
|         }
 | |
|         count = fread(&network, sizeof(network), 1, pFile);
 | |
|         if ((count != 1) || (network != DLT_BACNET_MS_TP)) {
 | |
|             fprintf(stderr, "mstpcap: invalid data link type (DLT)\n");
 | |
|             fclose(pFile);
 | |
|             pFile = NULL;
 | |
|             return false;
 | |
|         }
 | |
|     } else {
 | |
|         fprintf(stderr, "mstpcap[scan]: failed to open %s: %s\n", filename,
 | |
|             strerror(errno));
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| static bool read_received_packet(
 | |
|     volatile struct mstp_port_struct_t *mstp_port)
 | |
| {
 | |
|     uint32_t ts_sec = 0;        /* timestamp seconds */
 | |
|     uint32_t ts_usec = 0;       /* timestamp microseconds */
 | |
|     uint32_t incl_len = 0;      /* number of octets of packet saved in file */
 | |
|     uint32_t orig_len = 0;      /* actual length of packet */
 | |
|     uint8_t header[8] = { 0 };  /* MS/TP header */
 | |
|     struct timeval tv;
 | |
|     size_t count = 0;
 | |
|     unsigned i = 0;
 | |
| 
 | |
|     if (pFile) {
 | |
|         count = fread(&ts_sec, sizeof(ts_sec), 1, pFile);
 | |
|         if (count != 1) {
 | |
|             fclose(pFile);
 | |
|             pFile = NULL;
 | |
|             return false;
 | |
|         }
 | |
|         count = fread(&ts_usec, sizeof(ts_usec), 1, pFile);
 | |
|         if (count != 1) {
 | |
|             fclose(pFile);
 | |
|             pFile = NULL;
 | |
|             return false;
 | |
|         }
 | |
|         tv.tv_sec = ts_sec;
 | |
|         tv.tv_usec = ts_usec;
 | |
|         count = fread(&incl_len, sizeof(incl_len), 1, pFile);
 | |
|         if (count != 1) {
 | |
|             fclose(pFile);
 | |
|             pFile = NULL;
 | |
|             return false;
 | |
|         }
 | |
|         count = fread(&orig_len, sizeof(orig_len), 1, pFile);
 | |
|         if (count != 1) {
 | |
|             fclose(pFile);
 | |
|             pFile = NULL;
 | |
|             return false;
 | |
|         }
 | |
|         count = fread(&header, sizeof(header), 1, pFile);
 | |
|         if (count != 1) {
 | |
|             fclose(pFile);
 | |
|             pFile = NULL;
 | |
|             return false;
 | |
|         }
 | |
|         mstp_port->FrameType = header[2];
 | |
|         mstp_port->DestinationAddress = header[3];
 | |
|         mstp_port->SourceAddress = header[4];
 | |
|         mstp_port->DataLength = MAKE_WORD(header[6], header[5]);
 | |
|         mstp_port->HeaderCRCActual = header[7];
 | |
|         mstp_port->HeaderCRC = 0xFF;
 | |
|         for (i = 2; i < 8; i++) {
 | |
|             mstp_port->HeaderCRC =
 | |
|                 CRC_Calc_Header(header[i], mstp_port->HeaderCRC);
 | |
|         }
 | |
|         if (mstp_port->HeaderCRC == 0x55) {
 | |
|             mstp_port->ReceivedValidFrame = true;
 | |
|             mstp_port->ReceivedInvalidFrame = false;
 | |
|             if (mstp_port->DataLength == 0) {
 | |
|                 mstp_port->ReceivedValidFrame = true;
 | |
|                 mstp_port->ReceivedValidFrameNotForUs = true;
 | |
|             }
 | |
|         } else {
 | |
|             mstp_port->ReceivedValidFrame = false;
 | |
|             mstp_port->ReceivedInvalidFrame = true;
 | |
|         }
 | |
|         if (orig_len > 8) {
 | |
|             /* packet includes data */
 | |
|             mstp_port->DataLength = orig_len - 8 - 2;
 | |
|             count =
 | |
|                 fread(mstp_port->InputBuffer, mstp_port->DataLength, 1, pFile);
 | |
|             if (count != 1) {
 | |
|                 fclose(pFile);
 | |
|                 pFile = NULL;
 | |
|                 return false;
 | |
|             }
 | |
|             count = fread((char *) &mstp_port->DataCRCActualMSB, 1, 1, pFile);
 | |
|             if (count != 1) {
 | |
|                 fclose(pFile);
 | |
|                 pFile = NULL;
 | |
|                 return false;
 | |
|             }
 | |
|             count = fread((char *) &mstp_port->DataCRCActualLSB, 1, 1, pFile);
 | |
|             if (count != 1) {
 | |
|                 fclose(pFile);
 | |
|                 pFile = NULL;
 | |
|                 return false;
 | |
|             }
 | |
|             mstp_port->DataCRC = 0xFFFF;
 | |
|             for (i = 0; i < mstp_port->DataLength; i++) {
 | |
|                 mstp_port->DataCRC =
 | |
|                     CRC_Calc_Data(mstp_port->InputBuffer[i],
 | |
|                     mstp_port->DataCRC);
 | |
|             }
 | |
|             mstp_port->DataCRC =
 | |
|                 CRC_Calc_Data(mstp_port->DataCRCActualMSB, mstp_port->DataCRC);
 | |
|             mstp_port->DataCRC =
 | |
|                 CRC_Calc_Data(mstp_port->DataCRCActualLSB, mstp_port->DataCRC);
 | |
|             if (mstp_port->DataCRC == 0xF0B8) {
 | |
|                 mstp_port->ReceivedInvalidFrame = false;
 | |
|                 mstp_port->ReceivedValidFrame = true;
 | |
|                 mstp_port->ReceivedValidFrameNotForUs = true;
 | |
|             } else {
 | |
|                 mstp_port->ReceivedInvalidFrame = true;
 | |
|                 mstp_port->ReceivedValidFrame = false;
 | |
|                 mstp_port->ReceivedValidFrameNotForUs = false;
 | |
|             }
 | |
|         } else {
 | |
|             mstp_port->DataLength = 0;
 | |
|         }
 | |
|         if (mstp_port->ReceivedInvalidFrame) {
 | |
|             Invalid_Frame_Count++;
 | |
|         } else if ((mstp_port->ReceivedValidFrame) ||
 | |
|             (mstp_port->ReceivedValidFrameNotForUs)) {
 | |
|             packet_statistics(&tv, mstp_port);
 | |
|         }
 | |
|     } else {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| static void cleanup(
 | |
|     void)
 | |
| {
 | |
|     if (!Wireshark_Capture) {
 | |
|         packet_statistics_print();
 | |
|     }
 | |
|     if (pFile) {
 | |
|         fflush(pFile);  /* stream pointer */
 | |
|         fclose(pFile);  /* stream pointer */
 | |
|     }
 | |
|     pFile = NULL;
 | |
| }
 | |
| 
 | |
| #if defined(_WIN32)
 | |
| static BOOL WINAPI CtrlCHandler(
 | |
|     DWORD dwCtrlType)
 | |
| {
 | |
|     dwCtrlType = dwCtrlType;
 | |
| 
 | |
|     if (hPipe != INVALID_HANDLE_VALUE) {
 | |
|         FlushFileBuffers(hPipe);
 | |
|         DisconnectNamedPipe(hPipe);
 | |
|         CloseHandle(hPipe);
 | |
|         hPipe = INVALID_HANDLE_VALUE;
 | |
|     }
 | |
|     /* signal to main loop to exit */
 | |
|     Exit_Requested = true;
 | |
|     while (Exit_Requested) {
 | |
|         Sleep(100);
 | |
|     }
 | |
|     exit(0);
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| #else
 | |
| static void sig_int(
 | |
|     int signo)
 | |
| {
 | |
|     (void) signo;
 | |
|     if (FD_Pipe != -1) {
 | |
|         close(FD_Pipe);
 | |
|     }
 | |
|     Exit_Requested = true;
 | |
|     exit(0);
 | |
| }
 | |
| 
 | |
| void signal_init(
 | |
|     void)
 | |
| {
 | |
|     signal(SIGINT, sig_int);
 | |
|     signal(SIGHUP, sig_int);
 | |
|     signal(SIGTERM, sig_int);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| void filename_create_new(
 | |
|     void)
 | |
| {
 | |
|     if (pFile) {
 | |
|         fclose(pFile);
 | |
|     }
 | |
|     pFile = NULL;
 | |
|     filename_create(&Capture_Filename[0]);
 | |
|     write_global_header(&Capture_Filename[0]);
 | |
| }
 | |
| 
 | |
| static void print_usage(
 | |
|     char *filename)
 | |
| {
 | |
|     printf("Usage: %s", filename);
 | |
|     printf(" [--scan <filename>]\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 <filename>\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);
 | |
| }
 | |
| 
 | |
| /* initialize some of the variables in the MS/TP Receive structure */
 | |
| static void mstp_structure_init(
 | |
|     volatile struct mstp_port_struct_t *mstp_port)
 | |
| {
 | |
|     if (mstp_port) {
 | |
|         mstp_port->FrameType = FRAME_TYPE_PROPRIETARY_MAX;
 | |
|         mstp_port->DestinationAddress = MSTP_BROADCAST_ADDRESS;
 | |
|         mstp_port->SourceAddress = MSTP_BROADCAST_ADDRESS;
 | |
|         mstp_port->DataLength = 0;
 | |
|         mstp_port->HeaderCRCActual = 0;
 | |
|         mstp_port->Index = 0;
 | |
|         mstp_port->EventCount = 0;
 | |
|         mstp_port->ReceivedInvalidFrame = false;
 | |
|         mstp_port->ReceivedValidFrame = false;
 | |
|         mstp_port->ReceivedValidFrameNotForUs = false;
 | |
|         mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* simple test to packetize the data and print it */
 | |
| int main(
 | |
|     int argc,
 | |
|     char *argv[])
 | |
| {
 | |
|     volatile struct mstp_port_struct_t *mstp_port;
 | |
|     long my_baud = 38400;
 | |
|     uint32_t packet_count = 0;
 | |
|     uint32_t header_len = 0;
 | |
|     int argi = 0;
 | |
|     char *filename = NULL;
 | |
| 
 | |
|     MSTP_Port.InputBuffer = &RxBuffer[0];
 | |
|     MSTP_Port.InputBufferSize = sizeof(RxBuffer);
 | |
|     MSTP_Port.OutputBuffer = &TxBuffer[0];
 | |
|     MSTP_Port.OutputBufferSize = sizeof(TxBuffer);
 | |
|     MSTP_Port.This_Station = 127;
 | |
|     MSTP_Port.Nmax_info_frames = 1;
 | |
|     MSTP_Port.Nmax_master = 127;
 | |
|     MSTP_Port.SilenceTimer = Timer_Silence;
 | |
|     MSTP_Port.SilenceTimerReset = Timer_Silence_Reset;
 | |
|     /* mimic our pointer in the state machine */
 | |
|     mstp_port = &MSTP_Port;
 | |
|     MSTP_Init(mstp_port);
 | |
|     packet_statistics_clear();
 | |
|     /* 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-2016 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[argi])) {
 | |
|                 while (read_received_packet(mstp_port)) {
 | |
|                     packet_count++;
 | |
|                     fprintf(stderr, "\r%u packets",
 | |
|                         (unsigned) packet_count);
 | |
|                 }
 | |
|                 if (packet_count) {
 | |
|                     packet_statistics_print();
 | |
|                 }
 | |
|                 Exit_Requested = true;
 | |
|             } 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! */
 | |
|             Wireshark_Capture = true;
 | |
|         }
 | |
|         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 defined(_WIN32)
 | |
|         if (strncasecmp(argv[argi], "com", 3) == 0) {
 | |
|             /* legacy command line options */
 | |
|             RS485_Set_Interface(argv[argi]);
 | |
|             if ((argi+1) < argc) {
 | |
|                 argi++;
 | |
|                 my_baud = strtol(argv[argi], NULL, 0);
 | |
|                 RS485_Set_Baud_Rate(my_baud);
 | |
|             }
 | |
|         }
 | |
| #else
 | |
|         if (strncasecmp(argv[argi], "/dev/", 5) == 0) {
 | |
|             /* legacy command line options */
 | |
|             RS485_Set_Interface(argv[argi]);
 | |
|             if ((argi+1) < argc) {
 | |
|                 argi++;
 | |
|                 my_baud = strtol(argv[argi], NULL, 0);
 | |
|                 RS485_Set_Baud_Rate(my_baud);
 | |
|             }
 | |
|         }
 | |
| #endif
 | |
|         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 (Exit_Requested) {
 | |
|         return 0;
 | |
|     }
 | |
|     if (argc <= 1) {
 | |
|         RS485_Print_Ports();
 | |
|         return 0;
 | |
|     }
 | |
|     atexit(cleanup);
 | |
|     RS485_Initialize();
 | |
|     timer_init();
 | |
|     if (!Wireshark_Capture) {
 | |
|         fprintf(stdout, "mstpcap: Using %s for capture at %ld bps.\n",
 | |
|             RS485_Interface(), (long) RS485_Get_Baud_Rate());
 | |
|     }
 | |
| #if defined(_WIN32)
 | |
|     SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT);
 | |
|     SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlCHandler, TRUE);
 | |
| #else
 | |
|     signal_init();
 | |
| #endif
 | |
|     filename_create_new();
 | |
|     /* run forever */
 | |
|     for (;;) {
 | |
|         RS485_Check_UART_Data(mstp_port);
 | |
|         MSTP_Receive_Frame_FSM(mstp_port);
 | |
|         /* process the data portion of the frame */
 | |
|         if (mstp_port->ReceivedValidFrame) {
 | |
|             write_received_packet(mstp_port, MSTP_HEADER_MAX);
 | |
|             mstp_structure_init(mstp_port);
 | |
|             packet_count++;
 | |
|         } else if (mstp_port->ReceivedValidFrameNotForUs) {
 | |
|             write_received_packet(mstp_port, MSTP_HEADER_MAX);
 | |
|             mstp_structure_init(mstp_port);
 | |
|             packet_count++;
 | |
|         } else if (mstp_port->ReceivedInvalidFrame) {
 | |
|             if (MSTP_Receive_State == MSTP_RECEIVE_STATE_HEADER) {
 | |
|                 mstp_port->Index = 0;
 | |
|             }
 | |
|             write_received_packet(mstp_port, MSTP_HEADER_MAX);
 | |
|             mstp_structure_init(mstp_port);
 | |
|             Invalid_Frame_Count++;
 | |
|             packet_count++;
 | |
|         } else if (mstp_port->receive_state == MSTP_RECEIVE_STATE_IDLE) {
 | |
|             if (MSTP_Receive_State == MSTP_RECEIVE_STATE_IDLE) {
 | |
|                 if (mstp_port->EventCount) {
 | |
|                     write_received_packet(mstp_port, 1);
 | |
|                     mstp_structure_init(mstp_port);
 | |
|                     Invalid_Frame_Count++;
 | |
|                 }
 | |
|             } else {
 | |
|                 /* invalid byte or timeout */
 | |
|                 if (MSTP_Receive_State == MSTP_RECEIVE_STATE_PREAMBLE) {
 | |
|                     if (mstp_port->EventCount) {
 | |
|                         header_len = 1;
 | |
|                     } else {
 | |
|                         header_len = 2;
 | |
|                     }
 | |
|                 } else {
 | |
|                     header_len = 3 + mstp_port->Index;
 | |
|                 }
 | |
|                 write_received_packet(mstp_port, header_len);
 | |
|                 mstp_structure_init(mstp_port);
 | |
|                 Invalid_Frame_Count++;
 | |
|             }
 | |
|         }
 | |
|         if (!Wireshark_Capture) {
 | |
|             if (!(packet_count % 100)) {
 | |
|                 fprintf(stdout, "\r%hu packets, %hu invalid frames", packet_count,
 | |
|                     Invalid_Frame_Count);
 | |
|             }
 | |
|             if (packet_count >= 65535) {
 | |
|                 packet_statistics_print();
 | |
|                 packet_statistics_clear();
 | |
|                 filename_create_new();
 | |
|                 packet_count = 0;
 | |
|             }
 | |
|         }
 | |
|         if (Exit_Requested) {
 | |
|             break;
 | |
|         }
 | |
|         /* track the packetizer state */
 | |
|         MSTP_Receive_State = mstp_port->receive_state;
 | |
|     }
 | |
|     /* tell signal interrupts we are done */
 | |
|     Exit_Requested = false;
 | |
| 
 | |
|     return 0;
 | |
| }
 | 
