Created MS/TP capture tool that captures from USB->RS485 converter and sends data to SNAP Protocol for Wireshark capture.
This commit is contained in:
@@ -93,6 +93,7 @@ static int ethernet_bind(
|
||||
char *interface_name)
|
||||
{
|
||||
int sock_fd = -1; /* return value */
|
||||
int sockopt = 0;
|
||||
int uid = 0;
|
||||
|
||||
fprintf(stderr, "ethernet: opening \"%s\"\n", interface_name);
|
||||
@@ -108,6 +109,8 @@ static int ethernet_bind(
|
||||
/* modules.conf (or in modutils/alias on Debian with update-modules) */
|
||||
/* alias net-pf-17 af_packet */
|
||||
/* Then follow it by: # modprobe af_packet */
|
||||
/* Note: PF_INET/SOCK_PACKET has been replaced with
|
||||
PF_PACKET/(SOCK_PACKET, SOCK_DGRAM, SOCK_RAW).*/
|
||||
|
||||
/* Attempt to open the socket for 802.2 ethernet frames */
|
||||
if ((sock_fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_802_2))) < 0) {
|
||||
@@ -122,6 +125,15 @@ static int ethernet_bind(
|
||||
"# modprobe af_packet\n");
|
||||
exit(-1);
|
||||
}
|
||||
#if 0
|
||||
/* It is very advisable to do a IP_HDRINCL call, to make sure
|
||||
that the kernel knows the header is included in the data,
|
||||
and doesn't insert its own header into the packet before our data */
|
||||
if (setsockopt (sock_fd, IPPROTO_IP, IP_HDRINCL, &sockopt,
|
||||
sizeof(sockopt)) < 0) {
|
||||
printf ("Warning: Cannot set HDRINCL!\n");
|
||||
}
|
||||
#endif
|
||||
/* Bind the socket to an address */
|
||||
eth_addr->sa_family = PF_INET;
|
||||
/* Clear the memory before copying */
|
||||
@@ -187,6 +199,25 @@ bool ethernet_init(
|
||||
return ethernet_valid();
|
||||
}
|
||||
|
||||
int ethernet_send(
|
||||
uint8_t * mtu,
|
||||
int mtu_len)
|
||||
{
|
||||
int bytes = 0;
|
||||
|
||||
/* Send the packet */
|
||||
bytes =
|
||||
sendto(eth802_sockfd, &mtu, mtu_len, 0, (struct sockaddr *) ð_addr,
|
||||
sizeof(struct sockaddr));
|
||||
/* did it get sent? */
|
||||
if (bytes < 0)
|
||||
fprintf(stderr, "ethernet: Error sending packet: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
return bytes;
|
||||
|
||||
}
|
||||
|
||||
/* function to send a packet out the 802.2 socket */
|
||||
/* returns number of bytes sent on success, negative on failure */
|
||||
int ethernet_send_pdu(
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
/*####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>
|
||||
/* OS specific include*/
|
||||
#include "net.h"
|
||||
/* local includes */
|
||||
#include "bytes.h"
|
||||
#include "rs485.h"
|
||||
#include "crc.h"
|
||||
#include "mstp.h"
|
||||
#include "mstptext.h"
|
||||
#include "bacint.h"
|
||||
|
||||
/* local port data - shared with RS-485 */
|
||||
static volatile struct mstp_port_struct_t MSTP_Port;
|
||||
/* buffers needed by mstp port struct */
|
||||
static uint8_t RxBuffer[MAX_MPDU];
|
||||
static uint8_t TxBuffer[MAX_MPDU];
|
||||
static uint16_t SilenceTime;
|
||||
#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;}
|
||||
static uint16_t Timer_Silence(
|
||||
void)
|
||||
{
|
||||
return SilenceTime;
|
||||
}
|
||||
static void Timer_Silence_Reset(
|
||||
void)
|
||||
{
|
||||
SilenceTime = 0;
|
||||
}
|
||||
|
||||
static void dlmstp_millisecond_timer(
|
||||
void)
|
||||
{
|
||||
INCREMENT_AND_LIMIT_UINT16(SilenceTime);
|
||||
}
|
||||
|
||||
void *milliseconds_task(
|
||||
void *pArg)
|
||||
{
|
||||
struct timespec timeOut, remains;
|
||||
|
||||
timeOut.tv_sec = 0;
|
||||
timeOut.tv_nsec = 10000000; /* 1 milliseconds */
|
||||
|
||||
for (;;) {
|
||||
nanosleep(&timeOut, &remains);
|
||||
dlmstp_millisecond_timer();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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 int network_init(const char *name, int protocol)
|
||||
{
|
||||
/* check to see if we are being run as root */
|
||||
if (getuid() != 0) {
|
||||
fprintf(stderr,
|
||||
"Requires root priveleges.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sockfd = socket(PF_PACKET, SOCK_RAW, htons(protocol));
|
||||
|
||||
if (sockfd == -1) {
|
||||
perror("Unable to create socket");
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
struct ifreq ifr;
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, name, strlen(name));
|
||||
|
||||
if (ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1) {
|
||||
perror("Unable to get interface index");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_ll sll;
|
||||
|
||||
memset(&sll, 0, sizeof(sll));
|
||||
sll.sll_family = AF_PACKET;
|
||||
sll.sll_ifindex = ifr.ifr_ifindex;
|
||||
sll.sll_protocol = htons(protocol);
|
||||
|
||||
if (bind(sockfd, (struct sockaddr *) &sll, sizeof(sll)) == -1) {
|
||||
perror("Unable to bind socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
static void snap_received_packet(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
int sockfd)
|
||||
{
|
||||
uint16_t mtu_len = 0; /* number of octets of packet saved in file */
|
||||
unsigned i = 0; /* counter */
|
||||
static uint8_t mtu[1500] = {0};
|
||||
|
||||
mtu[0] = 0;
|
||||
mtu[1] = 0;
|
||||
mtu[2] = 0;
|
||||
mtu[3] = 0;
|
||||
mtu[4] = 0;
|
||||
mtu[5] = mstp_port->DestinationAddress;
|
||||
mtu[6] = 0;
|
||||
mtu[7] = 0;
|
||||
mtu[8] = 0;
|
||||
mtu[9] = 0;
|
||||
mtu[10] = 0;
|
||||
mtu[11]= mstp_port->SourceAddress;
|
||||
/* length - 12, 13 */
|
||||
mtu[14] = 0xaa; /* DSAP for SNAP */
|
||||
mtu[15] = 0xaa; /* SSAP for SNAP */
|
||||
mtu[16] = 0x03; /* Control Field for SNAP */
|
||||
mtu[17] = 0x00; /* Organization Code: Cimetrics */
|
||||
mtu[18] = 0x10; /* Organization Code: Cimetrics */
|
||||
mtu[19] = 0x90; /* Organization Code: Cimetrics */
|
||||
mtu[20] = 0x00; /* Protocol ID */
|
||||
mtu[21] = 0x01; /* Protocol ID */
|
||||
mtu[22] = 0x00; /* delta time */
|
||||
mtu[23] = 0x00; /* delta time */
|
||||
mtu[24] = 0x80; /* unknown byte */
|
||||
mtu[25] = mstp_port->FrameType;
|
||||
mtu[26] = mstp_port->DestinationAddress;
|
||||
mtu[27] = mstp_port->SourceAddress;
|
||||
mtu[28] = HI_BYTE(mstp_port->DataLength);
|
||||
mtu[29] = LO_BYTE(mstp_port->DataLength);
|
||||
mtu[30] = mstp_port->HeaderCRCActual;
|
||||
mtu_len = 31;
|
||||
if (mstp_port->DataLength) {
|
||||
for (i = 0; i < mstp_port->DataLength; i++) {
|
||||
mtu[31+i] = mstp_port->InputBuffer[i];
|
||||
}
|
||||
mtu[31+mstp_port->DataLength] = mstp_port->DataCRCActualMSB;
|
||||
mtu[31+mstp_port->DataLength+1] = mstp_port->DataCRCActualLSB;
|
||||
mtu_len += (mstp_port->DataLength+2);
|
||||
}
|
||||
/* Ethernet length is data only - not address or length bytes */
|
||||
encode_unsigned16(&mtu[12], mtu_len-14);
|
||||
write(sockfd,&mtu[0], mtu_len);
|
||||
}
|
||||
|
||||
|
||||
static void cleanup(
|
||||
void)
|
||||
{
|
||||
}
|
||||
|
||||
#if (!defined(_WIN32))
|
||||
static void sig_int(
|
||||
int signo)
|
||||
{
|
||||
(void) signo;
|
||||
|
||||
cleanup();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void signal_init(
|
||||
void)
|
||||
{
|
||||
signal(SIGINT, sig_int);
|
||||
signal(SIGHUP, sig_int);
|
||||
signal(SIGTERM, sig_int);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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;
|
||||
#if defined(_WIN32)
|
||||
unsigned long hThread = 0;
|
||||
uint32_t arg_value = 0;
|
||||
#else
|
||||
int rc = 0;
|
||||
pthread_t hThread;
|
||||
#endif
|
||||
int sockfd = -1;
|
||||
char *my_interface = "eth0";
|
||||
|
||||
/* mimic our pointer in the state machine */
|
||||
mstp_port = &MSTP_Port;
|
||||
/* initialize our interface */
|
||||
if (argc > 1) {
|
||||
RS485_Set_Interface(argv[1]);
|
||||
}
|
||||
if (argc > 2) {
|
||||
my_baud = strtol(argv[2], NULL, 0);
|
||||
}
|
||||
if (argc > 3) {
|
||||
my_interface = argv[3];
|
||||
}
|
||||
sockfd = network_init(my_interface, ETH_P_ALL);
|
||||
if (sockfd == -1) {
|
||||
return 1;
|
||||
}
|
||||
RS485_Set_Baud_Rate(my_baud);
|
||||
RS485_Initialize();
|
||||
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;
|
||||
MSTP_Init(mstp_port);
|
||||
mstp_port->Lurking = true;
|
||||
fprintf(stdout, "mstpcap: Using %s for capture at %ld bps.\n",
|
||||
RS485_Interface(), (long) RS485_Get_Baud_Rate());
|
||||
#if defined(_WIN32)
|
||||
hThread = _beginthread(milliseconds_task, 4096, &arg_value);
|
||||
if (hThread == 0) {
|
||||
fprintf(stderr, "Failed to start timer task\n");
|
||||
}
|
||||
(void) SetThreadPriority(GetCurrentThread(),
|
||||
THREAD_PRIORITY_TIME_CRITICAL);
|
||||
#else
|
||||
/* start our MilliSec task */
|
||||
rc = pthread_create(&hThread, NULL, milliseconds_task, NULL);
|
||||
signal_init();
|
||||
#endif
|
||||
atexit(cleanup);
|
||||
/* 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) {
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
snap_received_packet(mstp_port, sockfd);
|
||||
packet_count++;
|
||||
} else if (mstp_port->ReceivedInvalidFrame) {
|
||||
mstp_port->ReceivedInvalidFrame = false;
|
||||
fprintf(stderr, "ReceivedInvalidFrame\n");
|
||||
snap_received_packet(mstp_port, sockfd);
|
||||
packet_count++;
|
||||
}
|
||||
if (!(packet_count % 100)) {
|
||||
fprintf(stdout, "\r%hu packets", packet_count);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# Compiler to use
|
||||
CC = gcc
|
||||
# Executable file name
|
||||
TARGET = mstpsnap
|
||||
|
||||
# Configure the BACnet Datalink Layer
|
||||
BACDL_DEFINE = -DBACDL_MSTP
|
||||
BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL -DBACFILE
|
||||
DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE)
|
||||
|
||||
# Directories
|
||||
BACNET_PORT = linux
|
||||
BACNET_PORT_DIR = .
|
||||
BACNET_SOURCE_DIR = ../../src
|
||||
BACNET_INCLUDE = ../../include
|
||||
|
||||
# Compiler Setup
|
||||
INCLUDES = -I$(BACNET_INCLUDE) -I$(BACNET_PORT_DIR)
|
||||
ifeq (${BACNET_PORT},linux)
|
||||
PFLAGS = -pthread
|
||||
TARGET_BIN = ${TARGET}
|
||||
LIBRARIES=-lc,-lgcc,-lrt,-lm
|
||||
endif
|
||||
ifeq (${BACNET_PORT},win32)
|
||||
TARGET_BIN = ${TARGET}.exe
|
||||
LIBRARIES=-lws2_32,-lgcc,-lm,-liphlpapi
|
||||
endif
|
||||
#DEBUGGING = -g
|
||||
#OPTIMIZATION = -O0
|
||||
OPTIMIZATION = -Os
|
||||
CFLAGS = -Wall $(DEBUGGING) $(OPTIMIZATION) $(INCLUDES) $(DEFINES) -fdata-sections -ffunction-sections
|
||||
LFLAGS = -Wl,-Map=$(TARGET).map,$(LIBRARIES),--gc-sections
|
||||
|
||||
SRCS = mstpsnap.c \
|
||||
${BACNET_PORT_DIR}/rs485.c \
|
||||
${BACNET_SOURCE_DIR}/bacint.c \
|
||||
${BACNET_SOURCE_DIR}/mstp.c \
|
||||
${BACNET_SOURCE_DIR}/mstptext.c \
|
||||
${BACNET_SOURCE_DIR}/debug.c \
|
||||
${BACNET_SOURCE_DIR}/indtext.c \
|
||||
${BACNET_SOURCE_DIR}/crc.c
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
all: ${TARGET_BIN}
|
||||
size ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS}
|
||||
|
||||
include: .depend
|
||||
Reference in New Issue
Block a user