From b0bcca49eda13399a3df80819376ef12c8648b4e Mon Sep 17 00:00:00 2001 From: Anthony Merlino Date: Wed, 1 Nov 2017 20:38:22 +0000 Subject: [PATCH] Merged in antmerlino/apps/i8shark (pull request #116) Adds i8shark, a sniffer "adapter" that captures all 802.15.4 traffic, packages it into a Wireshark ZEP packet, and sends it to a host running Wireshark. Approved-by: Gregory Nutt --- wireless/ieee802154/Kconfig | 1 + wireless/ieee802154/i8shark/.gitignore | 11 + wireless/ieee802154/i8shark/Kconfig | 25 ++ wireless/ieee802154/i8shark/Make.defs | 39 ++ wireless/ieee802154/i8shark/Makefile | 51 +++ wireless/ieee802154/i8shark/i8shark_main.c | 497 +++++++++++++++++++++ 6 files changed, 624 insertions(+) create mode 100644 wireless/ieee802154/i8shark/.gitignore create mode 100644 wireless/ieee802154/i8shark/Kconfig create mode 100644 wireless/ieee802154/i8shark/Make.defs create mode 100644 wireless/ieee802154/i8shark/Makefile create mode 100644 wireless/ieee802154/i8shark/i8shark_main.c diff --git a/wireless/ieee802154/Kconfig b/wireless/ieee802154/Kconfig index 47cb452cd..7cb6c896d 100644 --- a/wireless/ieee802154/Kconfig +++ b/wireless/ieee802154/Kconfig @@ -8,5 +8,6 @@ menu "IEEE 802.15.4 applications" source "$APPSDIR/wireless/ieee802154/libmac/Kconfig" source "$APPSDIR/wireless/ieee802154/libutils/Kconfig" source "$APPSDIR/wireless/ieee802154/i8sak/Kconfig" +source "$APPSDIR/wireless/ieee802154/i8shark/Kconfig" endmenu #ieee 802.15.4 apps diff --git a/wireless/ieee802154/i8shark/.gitignore b/wireless/ieee802154/i8shark/.gitignore new file mode 100644 index 000000000..fa1ec7579 --- /dev/null +++ b/wireless/ieee802154/i8shark/.gitignore @@ -0,0 +1,11 @@ +/Make.dep +/.depend +/.built +/*.asm +/*.obj +/*.rel +/*.lst +/*.sym +/*.adb +/*.lib +/*.src diff --git a/wireless/ieee802154/i8shark/Kconfig b/wireless/ieee802154/i8shark/Kconfig new file mode 100644 index 000000000..9adf4e831 --- /dev/null +++ b/wireless/ieee802154/i8shark/Kconfig @@ -0,0 +1,25 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config IEEE802154_I8SHARK + bool "IEEE 802.15.4 Wireshark adapter" + default n + select IEEE802154_LIBUTILS + select IEEE802154_LIBMAC + depends on IEEE802154_MACDEV && NET_UDP && NET_IPv4 + +if IEEE802154_I8SHARK + +config IEEE802154_I8SHARK_DEVPATH + string "MAC char driver path" + default "/dev/ieee0" + ---help--- + The default path to MAC character driver. Default: /dev/ieee0 + +config IEEE802154_I8SHARK_HOST_IPADDR + hex "Host IP address where Wireshark is running" + default 0x0a000001 + +endif diff --git a/wireless/ieee802154/i8shark/Make.defs b/wireless/ieee802154/i8shark/Make.defs new file mode 100644 index 000000000..11fce00ed --- /dev/null +++ b/wireless/ieee802154/i8shark/Make.defs @@ -0,0 +1,39 @@ +############################################################################ +# apps/wireless/ieee802154/i8shark/Make.defs +# Adds selected applications to apps/ build +# +# Copyright (C) 2017 Verge Inc. All rights reserved. +# Author: Anthony Merlino +# +# 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 NuttX 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 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 +# COPYRIGHT OWNER 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. +# +############################################################################ + +ifeq ($(CONFIG_IEEE802154_I8SHARK),y) +CONFIGURED_APPS += wireless/ieee802154/i8shark +endif diff --git a/wireless/ieee802154/i8shark/Makefile b/wireless/ieee802154/i8shark/Makefile new file mode 100644 index 000000000..809b1fdf3 --- /dev/null +++ b/wireless/ieee802154/i8shark/Makefile @@ -0,0 +1,51 @@ +############################################################################ +# apps/wireless/ieee802154/i8shark/Makefile +# +# Copyright (C) 2017 Verge Inc. All rights reserved. +# Author: Anthony Merlino +# +# 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 NuttX 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 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 +# COPYRIGHT OWNER 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. +# +############################################################################ + +-include $(TOPDIR)/Make.defs + +ASRCS = +CSRCS = +MAINSRC = i8shark_main.c + +CONFIG_XYZ_PROGNAME ?= i8shark$(EXEEXT) +PROGNAME = $(CONFIG_XYZ_PROGNAME) + +# application info + +APPNAME = i8shark +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = 2048 + +include $(APPDIR)/Application.mk diff --git a/wireless/ieee802154/i8shark/i8shark_main.c b/wireless/ieee802154/i8shark/i8shark_main.c new file mode 100644 index 000000000..7532f6037 --- /dev/null +++ b/wireless/ieee802154/i8shark/i8shark_main.c @@ -0,0 +1,497 @@ +/**************************************************************************** + * wireless/ieee802154/i8shark/i8shark_main.c + * + * Copyright (C) 2017 Verge Inc. All rights reserved. + * Author: Anthony Merlino + * + * 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 NuttX 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 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 + * COPYRIGHT OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "netutils/netlib.h" +#include "wireless/ieee802154.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +#ifndef CONFIG_IEEE802154_I8SHARK_DAEMON_PRIORITY +# define CONFIG_IEEE802154_I8SHARK_DAEMON_PRIORITY SCHED_PRIORITY_DEFAULT +#endif + +#ifndef CONFIG_IEEE802154_I8SHARK_DAEMON_STACKSIZE +# define CONFIG_IEEE802154_I8SHARK_DAEMON_STACKSIZE 2048 +#endif + +#ifndef CONFIG_IEEE802154_I8SHARK_CHANNEL +# define CONFIG_IEEE802154_I8SHARK_CHANNEL 11 +#endif + +#ifndef CONFIG_IEEE802154_I8SHARK_HOST_IPADDR +# error "You must define CONFIG_IEEE802154_I8SHARK_HOST_IPADDR" +#endif + +#ifndef CONFIG_IEEE802154_I8SHARK_FORWARDING_IFNAME +# define CONFIG_IEEE802154_I8SHARK_FORWARDING_IFNAME "eth0" +#endif + +#define I8SHARK_MAX_DEVPATH 15 + +#define ZEP_MAX_HDRSIZE 32 +#define I8SHARK_MAX_ZEPFRAME IEEE802154_MAX_PHY_PACKET_SIZE + ZEP_MAX_HDRSIZE + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct i8shark_state_s +{ + bool initialized : 1; + bool daemon_started : 1; + bool daemon_shutdown : 1; + + pid_t daemon_pid; + + /* User exposed settings */ + + uint8_t chan; + FAR char devpath[I8SHARK_MAX_DEVPATH]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int i8shark_init(FAR struct i8shark_state_s *i8shark); +static int i8shark_daemon(int argc, FAR char *argv[]); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct i8shark_state_s g_i8shark; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: i8shark_help + ****************************************************************************/ + +#ifdef CONFIG_NSH_BUILTIN_APPS +static void i8shark_help(void) +{ + printf("Usage: i8shark ![interface] [OPTIONS]\n"); + printf("\nArguments are \"sticky\".\n"); + printf("\nInterface only needs to be specified the first time\n"); + printf("OPTIONS include:\n"); + printf(" [-h] shows this message and exits\n"); +} +#endif + +/**************************************************************************** + * Name: arg_string + ****************************************************************************/ + +#ifdef CONFIG_NSH_BUILTIN_APPS +static int arg_string(FAR char **arg, FAR char **value) +{ + FAR char *ptr = *arg; + + if (ptr[2] == '\0') + { + *value = arg[1]; + return 2; + } + else + { + *value = &ptr[2]; + return 1; + } +} +#endif + +/**************************************************************************** + * Name: arg_decimal + ****************************************************************************/ + +#ifdef CONFIG_NSH_BUILTIN_APPS +static int arg_decimal(FAR char **arg, FAR long *value) +{ + FAR char *string; + int ret; + + ret = arg_string(arg, &string); + *value = strtol(string, NULL, 10); + return ret; +} +#endif + +/**************************************************************************** + * Name: parse_args + ****************************************************************************/ + +#ifdef CONFIG_NSH_BUILTIN_APPS +static void parse_args(FAR struct i8shark_state_s *i8shark, int argc, FAR char **argv) +{ + FAR char *ptr; + int index; + + for (index = 1; index < argc; ) + { + ptr = argv[index]; + if (ptr[0] != '-') + { + printf("Invalid options format: %s\n", ptr); + exit(0); + } + + switch (ptr[1]) + { + case 'h': + i8shark_help(); + exit(0); + + default: + printf("Unsupported option: %s\n", ptr); + i8shark_help(); + exit(1); + } + } +} +#endif + +/**************************************************************************** + * Name: i8shark_init + ****************************************************************************/ + +static int i8shark_init(FAR struct i8shark_state_s *i8shark) +{ + if (i8shark->initialized) + { + return OK; + } + + /* Set the default settings using config options */ + + i8shark->chan = CONFIG_IEEE802154_I8SHARK_CHANNEL; + strcpy(i8shark->devpath, CONFIG_IEEE802154_I8SHARK_DEVPATH); + + /* Flags for synchronzing with daemon state */ + + i8shark->daemon_started = false; + i8shark->daemon_shutdown = false; + i8shark->initialized = true; + + return OK; +} + +/**************************************************************************** + * Name : i8shark_daemon + * + * Description : + * This daemon reads all incoming IEEE 802.15.4 frames from a MAC802154 character + * driver, packages the frames into a Wireshark Zigbee Encapsulate Protocol (ZEP) + * packet and sends it over Ethernet to the specified host machine running + * Wireshark. + * + ****************************************************************************/ + +static int i8shark_daemon(int argc, FAR char *argv[]) +{ + int ret; + int fd; + struct sockaddr_in addr; + struct sockaddr_in raddr; + socklen_t addrlen; + int sockfd; + + fprintf(stderr, "i8shark: daemon started\n"); + g_i8shark.daemon_started = true; + + fd = open(g_i8shark.devpath, O_RDWR); + if (fd < 0) + { + fprintf(stderr, "ERROR: cannot open %s, errno=%d\n", g_i8shark.devpath, errno); + g_i8shark.daemon_started = false; + ret = errno; + return ret; + } + + /* Place the MAC into promiscuous mode */ + + ieee802154_setpromisc(fd, true); + + /* Always listen */ + + ieee802154_setrxonidle(fd, true); + + /* Create a UDP socket to send the data to Wireshark */ + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + { + fprintf(stderr, "ERROR: socket failure %d\n", errno); + g_i8shark.daemon_started = false; + return -1; + } + + /* We bind to the IP address of the outbound interface so that the OS knows + * which interface to use to send the packet. + */ + + netlib_get_ipv4addr(CONFIG_IEEE802154_I8SHARK_FORWARDING_IFNAME, &addr.sin_addr); + addr.sin_port = 0; + addr.sin_family = AF_INET; + addrlen = sizeof(struct sockaddr_in); + + if (bind(sockfd, (FAR struct sockaddr *)&addr, addrlen) < 0) + { + fprintf(stderr, "ERROR: Bind failure: %d\n", errno); + return -1; + } + + /* Setup our remote address. Wireshark expects ZEP packets over UDP on port 17754 */ + + raddr.sin_family = AF_INET; + raddr.sin_port = HTONS(17754); + raddr.sin_addr.s_addr = HTONL(CONFIG_IEEE802154_I8SHARK_HOST_IPADDR); + + /* Loop until the daemon is shutdown reading incoming IEEE 802.15.4 frames, + * packing them into Wireshark "Zigbee Encapsulation Packets" (ZEP) and sending + * them over UDP to Wireshark. + */ + + while (!g_i8shark.daemon_shutdown) + { + struct mac802154dev_rxframe_s frame; + enum ieee802154_frametype_e ftype; + uint8_t zepframe[I8SHARK_MAX_ZEPFRAME]; + systime_t systime; + int ind = 0; + int nbytes; + + /* Get an incoming frame from the MAC character driver */ + + ret = read(fd, &frame, sizeof(struct mac802154dev_rxframe_s)); + if (ret < 0) + { + continue; + } + + /* First 2 bytes of packet represent preamble. For ZEP, "EX" */ + + zepframe[ind++] = 'E'; + zepframe[ind++] = 'X'; + + /* The next byte is the version. We are using V2 */ + + zepframe[ind++] = 2; + + /* Next byte is type. ZEP only differentiates between ACK and Data. My + * assumption is that Data also includes MAC command frames and beacon + * frames. So we really only need to check if it's an ACK or not. + */ + + ftype = ((*(uint16_t *)frame.payload) & IEEE802154_FRAMECTRL_FTYPE) >> + IEEE802154_FRAMECTRL_SHIFT_FTYPE; + + if (ftype == IEEE802154_FRAME_ACK) + { + zepframe[ind++] = 2; + + /* Not sure why, but the ZEP header allows for a 4-byte sequence no. + * despite 802.15.4 sequence number only being 1-byte + */ + + zepframe[ind] = frame.meta.dsn; + ind += 4; + } + else + { + zepframe[ind++] = 1; + + /* Next bytes is the Channel ID */ + + zepframe[ind++] = g_i8shark.chan; + + /* For now, just hard code the device ID to an arbitrary value */ + + zepframe[ind++] = 0xFA; + zepframe[ind++] = 0xDE; + + /* Not completely sure what LQI mode is. My best guess as of now based + * on a few comments in the Wireshark code is that it determines whether + * the last 2 bytes of the frame portion of the packet is the CRC or the + * LQI. I believe it is CRC = 1, LQI = 0. We will assume the CRC is the + * last few bytes as that is what the MAC layer expects. However, this + * may be a bad assumption for certain radios. + */ + + zepframe[ind++] = 1; + + /* Next byte is the LQI value */ + + zepframe[ind++] = frame.meta.lqi; + + /* Need to use NTP to get time, but for now, include the system time */ + + systime = clock_systimer(); + memcpy(&zepframe[ind], &systime, 8); + ind += 8; + + /* Not sure why, but the ZEP header allows for a 4-byte sequence no. + * despite 802.15.4 sequence number only being 1-byte + */ + + zepframe[ind] = frame.meta.dsn; + zepframe[ind+1] = 0; + zepframe[ind+2] = 0; + zepframe[ind+3] = 0; + ind += 4; + + /* Skip 10-bytes for reserved fields */ + + ind += 10; + + /* Last byte is the length */ + + zepframe[ind++] = frame.length; + } + + /* The ZEP header is filled, now copy the frame in */ + + memcpy(&zepframe[ind], frame.payload, frame.length); + ind += frame.length; + + /* Send the encapsulated frame to Wireshark over UDP */ + + nbytes = sendto(sockfd, zepframe, ind, 0, (struct sockaddr*)&raddr, addrlen); + if (nbytes < ind) + { + fprintf(stderr, "ERROR: sendto() did not send all bytes. %d\n", errno); + } + } + + g_i8shark.daemon_started = false; + close(fd); + printf("i8shark: daemon closing\n"); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: i8shark_main + ****************************************************************************/ + +#ifdef CONFIG_BUILD_KERNEL +int main(int argc, FAR char *argv[]) +#else +int i8shark_main(int argc, char *argv[]) +#endif +{ + int argind = 1; + + if (!g_i8shark.initialized) + { + i8shark_init(&g_i8shark); + } + + if (argc > 1) + { + /* If the first argument is an interface, update our character device path */ + + if (strncmp(argv[argind], "/dev/", 5) == 0) + { + /* Check if the name is the same as the current one */ + + if (!strcmp(g_i8shark.devpath, argv[argind])) + { + /* Adapter daemon can't be running when we change device path */ + + if (g_i8shark.daemon_started) + { + printf("Can't change devpath when daemon is running.\n"); + exit(1); + } + + /* Copy the path into our state structure */ + + strcpy(g_i8shark.devpath, argv[1]); + } + + argind++; + } + } + + /* If the daemon is not running, start it. */ + + g_i8shark.daemon_pid = task_create("i8shark", + CONFIG_IEEE802154_I8SHARK_DAEMON_PRIORITY, + CONFIG_IEEE802154_I8SHARK_DAEMON_STACKSIZE, + i8shark_daemon, NULL); + if (g_i8shark.daemon_pid < 0) + { + fprintf(stderr, "failed to start daemon\n"); + return ERROR; + } + + return OK; +}