460 lines
14 KiB
C
460 lines
14 KiB
C
/*******************************************************************
|
|
Copyright (C) 2009 FreakLabs
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
3. Neither the name of the the copyright holder nor the names of its contributors
|
|
may be used to endorse or promote products derived from this software
|
|
without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
SUCH DAMAGE.
|
|
|
|
Originally written by Christopher Wang aka Akiba.
|
|
Please post support questions to the FreakLabs forum.
|
|
|
|
*******************************************************************/
|
|
/*!
|
|
FreakLabs Freakduino/Wireshark Bridge
|
|
|
|
This program allows data from the Freakduino to be piped into wireshark.
|
|
When the sniffer firmware is loaded into the Freakduino, then the Freakduino
|
|
will be in promiscuous mode and will just dump any frames it sees. This
|
|
program takes the frame dump and sends it into Wireshark for analysis. The
|
|
global header is already set up to inform wireshark that the link layer for
|
|
all frames will be in IEEE 802.15.4 format. After that, it is up to the user
|
|
to choose any higher layer protocols to decode above 802.15.4 via the
|
|
wireshark "enable protocols" menu.
|
|
*/
|
|
|
|
/*
|
|
|
|
Modified for sensniff format. Magic[4] + Vers[1] + CMD[1] + len[1]
|
|
Robert Olsson <roolss@kth.se> 2015-11-10
|
|
|
|
*/
|
|
|
|
/**************************************************************************/
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <stdint.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
|
|
#define PORTBUFSIZE 32
|
|
#define BUFSIZE 1024
|
|
#define PACKET_FCS 2
|
|
#define DEBUG 1
|
|
#define PIPENAME "/tmp/wireshark"
|
|
#define BAUDRATE B38400
|
|
|
|
enum FSM {
|
|
START_CAPTURE,
|
|
PACKET_CAPTURE
|
|
};
|
|
|
|
static int FD_pipe = -1;
|
|
static int FD_com = -1;
|
|
static uint8_t port_buf[PORTBUFSIZE];
|
|
static uint8_t circ_buf[BUFSIZE];
|
|
static uint16_t rd_idx = 0;
|
|
static uint16_t wr_idx = 0;
|
|
static uint8_t len;
|
|
static uint8_t state = START_CAPTURE;
|
|
static uint8_t file_write = 0;
|
|
static fd_set fds;
|
|
static const uint8_t magic[] = { 0xC1, 0x1F, 0xFE, 0x72 };
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
Open the serial port that we'll be communicating with the Freakduino (sniffer)
|
|
through.
|
|
*/
|
|
/**************************************************************************/
|
|
|
|
int
|
|
serial_open(char *portname)
|
|
{
|
|
int baud = B38400;
|
|
int fd; /* file descriptor for the serial port */
|
|
struct termios tc, tp;
|
|
|
|
fd = open(portname, O_RDONLY | O_NOCTTY | O_NDELAY);
|
|
|
|
if(fd == -1) { /* if open is unsucessful */
|
|
printf("serial_open: Unable to open %s.\n", portname);
|
|
exit(-1);
|
|
}
|
|
|
|
fcntl(fd, F_GETFL);
|
|
fcntl(fd, F_SETFL, O_RDWR);
|
|
|
|
tp.c_cflag = baud | CS8 | CLOCAL | CREAD;
|
|
tp.c_oflag = 0; /* Raw Input */
|
|
tp.c_lflag = 0; /* No conoical */
|
|
tp.c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | OFDEL | NLDLY | CRDLY);
|
|
|
|
/* ignore CR, ignore parity */
|
|
tp.c_iflag = ~(IGNBRK | PARMRK | INPCK | INLCR | IUCLC | IXOFF) |
|
|
BRKINT | IGNPAR | ICRNL | IXON | ISIG | ICANON;
|
|
|
|
tp.c_lflag &= ~(ECHO | ECHONL);
|
|
tp.c_oflag = 0; /* Raw Input */
|
|
tp.c_iflag &= ~ISTRIP;
|
|
|
|
tcflush(fd, TCIFLUSH);
|
|
|
|
cfsetospeed(&tp, baud);
|
|
cfsetispeed(&tp, baud);
|
|
|
|
if(tcsetattr(fd, TCSANOW, &tp) < 0) {
|
|
perror("Couldn't set term attributes");
|
|
return -1;
|
|
}
|
|
return fd;
|
|
}
|
|
/**************************************************************************/
|
|
/*!
|
|
Create the named pipe that we will be communicating with wireshark through.
|
|
*/
|
|
/**************************************************************************/
|
|
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);
|
|
}
|
|
}
|
|
/**************************************************************************/
|
|
/*!
|
|
Write data to the pipe
|
|
*/
|
|
/**************************************************************************/
|
|
size_t
|
|
data_write(const void *ptr, size_t size)
|
|
{
|
|
ssize_t bytes = 0;
|
|
if(FD_pipe != -1) {
|
|
bytes = write(FD_pipe, ptr, size);
|
|
}
|
|
}
|
|
/**************************************************************************/
|
|
/*!
|
|
Write the global header to wireshark. This is only done once at the
|
|
beginning of the capture.
|
|
*/
|
|
/**************************************************************************/
|
|
static void
|
|
write_global_hdr()
|
|
{
|
|
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 = 195; /* data link type (DLT) - IEEE 802.15.4 */
|
|
|
|
data_write(&magic_number, sizeof(magic_number));
|
|
data_write(&version_major, sizeof(version_major));
|
|
data_write(&version_minor, sizeof(version_minor));
|
|
data_write(&thiszone, sizeof(thiszone));
|
|
data_write(&sigfigs, sizeof(sigfigs));
|
|
data_write(&snaplen, sizeof(snaplen));
|
|
data_write(&network, sizeof(network));
|
|
}
|
|
/**************************************************************************/
|
|
/*!
|
|
Write the frame header into wireshark. This is required for the libpcap
|
|
format and informs wireshark that a new frame is coming.
|
|
*/
|
|
/**************************************************************************/
|
|
static void
|
|
write_frame_hdr(uint8_t len)
|
|
{
|
|
uint32_t ts_sec; /* timestamp seconds */
|
|
uint32_t ts_usec; /* timestamp microseconds */
|
|
uint32_t incl_len; /* number of octets of packet saved in file */
|
|
uint32_t orig_len; /* actual length of packet */
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
ts_sec = tv.tv_sec;
|
|
ts_usec = tv.tv_usec;
|
|
incl_len = len;
|
|
orig_len = len + PACKET_FCS;
|
|
|
|
data_write(&ts_sec, sizeof(ts_sec));
|
|
data_write(&ts_usec, sizeof(ts_usec));
|
|
data_write(&incl_len, sizeof(incl_len));
|
|
data_write(&orig_len, sizeof(orig_len));
|
|
}
|
|
/**************************************************************************/
|
|
/*!
|
|
Write one frame into wireshark (via the pipe).
|
|
*/
|
|
/**************************************************************************/
|
|
static void
|
|
write_frame(uint8_t frame_len)
|
|
{
|
|
uint8_t i;
|
|
|
|
/* actual frame length for wireshark should not include FCS */
|
|
frame_len -= PACKET_FCS;
|
|
|
|
/* write header to inform WS that new frame has arrived */
|
|
write_frame_hdr(frame_len);
|
|
|
|
/* bump rd_idx. we don't want to write the length byte */
|
|
rd_idx = (rd_idx + 1) % BUFSIZE;
|
|
|
|
/* write frame into wireshark */
|
|
for(i = 0; i < frame_len; i++) {
|
|
data_write(&circ_buf[rd_idx], 1);
|
|
rd_idx = (rd_idx + 1) % BUFSIZE;
|
|
}
|
|
|
|
/* bump rd_idx. we're not using the trailing FCS value */
|
|
rd_idx = (rd_idx + 1) % BUFSIZE;
|
|
}
|
|
/**************************************************************************/
|
|
/*!
|
|
Calculate total number of bytes in buffer.
|
|
*/
|
|
/**************************************************************************/
|
|
static uint16_t
|
|
calc_bytes_in_buf()
|
|
{
|
|
if(rd_idx > wr_idx) {
|
|
/* read index is greater than write. we must have wrapped around */
|
|
return BUFSIZE - (rd_idx - wr_idx);
|
|
} else {
|
|
return wr_idx - rd_idx;
|
|
}
|
|
}
|
|
/**************************************************************************/
|
|
/*!
|
|
Deal with any received signals. This includes ctrl-C to stop the program.
|
|
*/
|
|
/**************************************************************************/
|
|
static void
|
|
sig_int(int signo)
|
|
{
|
|
(void)signo;
|
|
if(FD_pipe != -1) {
|
|
printf("\nClosing pipe.\n");
|
|
close(FD_pipe);
|
|
}
|
|
|
|
if(FD_com != -1) {
|
|
printf("\nClosing serial port.\n");
|
|
close(FD_com);
|
|
}
|
|
|
|
printf("\nSignal captured and devices shut down.\n");
|
|
|
|
exit(0);
|
|
}
|
|
/**************************************************************************/
|
|
/*!
|
|
Init the signals we'll be checking for.
|
|
*/
|
|
/**************************************************************************/
|
|
static void
|
|
signal_init(void)
|
|
{
|
|
signal(SIGINT, sig_int);
|
|
signal(SIGHUP, sig_int);
|
|
signal(SIGTERM, sig_int);
|
|
}
|
|
int got;
|
|
int debug;
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
Here's the meat of the code.
|
|
*/
|
|
/**************************************************************************/
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int nbytes;
|
|
uint8_t i;
|
|
|
|
got = 0;
|
|
|
|
/* capture any signals that will terminate program */
|
|
signal_init();
|
|
|
|
/* make sure the COM port is specified */
|
|
if(argc == 2) {
|
|
/* open the COM port */
|
|
if((FD_com = serial_open(argv[1])) == -1) {
|
|
printf("Serial port not opened.\n");
|
|
return 0;
|
|
} else {
|
|
/* set up the select statement for the COM port. */
|
|
FD_ZERO(&fds);
|
|
FD_SET(FD_com, &fds);
|
|
|
|
printf("Serial port connected. Waiting for wireshark connection.\n");
|
|
printf("Open wireshark and connect to local interface: %s\n", PIPENAME);
|
|
}
|
|
} else {
|
|
printf("Usage: wsbridge <portname>.\n");
|
|
return 0;
|
|
}
|
|
|
|
/* create and open pipe for wireshark */
|
|
named_pipe_create(PIPENAME);
|
|
|
|
/* wait for wireshark to connect to pipe. Once wireshark */
|
|
/* connects, then the global header will be written to it. */
|
|
if(FD_pipe != -1) {
|
|
write_global_hdr();
|
|
printf("Client connected to pipe.\n");
|
|
}
|
|
|
|
for(;;) {
|
|
uint16_t bytes_in_buf;
|
|
uint8_t frame_len, byte_ctr;
|
|
|
|
/* block until there is data in the serial port */
|
|
select(FD_com + 1, &fds, NULL, NULL, NULL);
|
|
if(FD_ISSET(FD_com, &fds)) {
|
|
int ii;
|
|
/* wait for data to come in on the serial port */
|
|
if((nbytes = read(FD_com, port_buf, PORTBUFSIZE)) > 0) {
|
|
|
|
if(debug) {
|
|
uint8_t p;
|
|
printf("read nbytes=%d\n", nbytes);
|
|
for(i = 0; i < nbytes; i++) {
|
|
printf(" %02X", port_buf[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
/* write data to circular buffer. loop through all received bytes */
|
|
for(i = 0; i < nbytes; i++) {
|
|
switch(state) {
|
|
case START_CAPTURE:
|
|
/* new frame starting */
|
|
if((got == 0) && (port_buf[i] == magic[0])) {
|
|
got = 1;
|
|
} else if((got == 1) && (port_buf[i] == magic[1])) {
|
|
got = 2;
|
|
} else if((got == 2) && (port_buf[i] == magic[2])) {
|
|
got = 3;
|
|
} else if((got == 3) && (port_buf[i] == magic[3])) {
|
|
got = 4;
|
|
if(debug) {
|
|
printf("GOT MAGIC i=%d\n", i);
|
|
}
|
|
} else if((got == 4) && (port_buf[i] == 1)) {
|
|
got = 5;
|
|
if(debug) {
|
|
printf("GOT VERSION i=%d\n", port_buf[i]);
|
|
}
|
|
} else if((got == 5) && (port_buf[i] == 0)) {
|
|
got = 6;
|
|
if(debug) {
|
|
printf("GOT COMMAND i=%d\n", port_buf[i]);
|
|
}
|
|
} else if(got == 6) {
|
|
len = port_buf[i];
|
|
byte_ctr = 0;
|
|
if(debug) {
|
|
printf("Len = %02X.\n", len);
|
|
}
|
|
circ_buf[wr_idx] = len;
|
|
wr_idx = (wr_idx + 1) % BUFSIZE;
|
|
state = PACKET_CAPTURE;
|
|
} else {
|
|
got = 0;
|
|
}
|
|
break;
|
|
|
|
case PACKET_CAPTURE:
|
|
/* continue capturing bytes until end of frame */
|
|
/* write data to circular buffer and increment index */
|
|
|
|
circ_buf[wr_idx] = port_buf[i];
|
|
/* ////printf("%02X ", circ_buf[wr_idx]); */
|
|
|
|
wr_idx = (wr_idx + 1) % BUFSIZE;
|
|
/* track number of received bytes. when received bytes */
|
|
/* equals frame length, then restart state machine and */
|
|
/* write bytes to wireshark */
|
|
byte_ctr++;
|
|
if(byte_ctr == (len - 1)) {
|
|
state = START_CAPTURE;
|
|
file_write = 1;
|
|
/* printf("\n"); */
|
|
got = 0;
|
|
}
|
|
break;
|
|
}
|
|
fflush(stdout);
|
|
}
|
|
/* at least one frame has been written. loop through circular buffer */
|
|
/* and write out all completed frames */
|
|
while(file_write) {
|
|
/* capture frame length and check buffer to see if one or more frames */
|
|
/* are available. */
|
|
frame_len = circ_buf[rd_idx];
|
|
bytes_in_buf = calc_bytes_in_buf();
|
|
|
|
if(bytes_in_buf > frame_len) {
|
|
/* if more than one frame is available, then write one frame to */
|
|
/* wireshark and then see if any more are available. */
|
|
write_frame(frame_len);
|
|
} else if(bytes_in_buf == frame_len) {
|
|
/* only one frame is available. write to wireshark and then quit */
|
|
/* the loop */
|
|
write_frame(frame_len);
|
|
file_write = 0;
|
|
} else {
|
|
/* less than one frame is available. quit the loop and collect more */
|
|
/* bytes. we normally should not get here. */
|
|
file_write = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|