I ran across this thread while trying to find a way to connect the CC2531 USB dongle to Wireshark. I thought I'd reply in case anyone else is looking for a way to use wireshark as a sniffer front end.
This perl script runs on the PC running SmartRF Packet Sniffer and it formats/forwards packets to another PC running Wireshark. The packets show up in SmartRF one one PC and Wireshark on another PC. This is very useful if you have a custom protocol on top of 802.15.4 and/or ZigBee and want to use Wireshark to decode it. I didn't really test the timestamp stuff in the ZEP header, so that probably needs some work.
Hope it helps someone else down the line.
-Brian
# wireshark_adaptor.pl
# This script runs on the same PC that has the CC2531 USB
# dongle connected and is running SmartRF Packet Sniffer.
# Packet flow is as follows:
# - Dongle to SmartRF Sniffer program via USB
# - SmartRF to wireshark_adaptor.pl script via broadcast UDP:5000 socket on localhost
# - wireshark_adaptor.pl script to PC running wireshark via UDP:17754 socket
#
# I also run a simple script on the wireshark PC to open and
# accept packets on UDP:17754 so the wireshark PC doesn't complain
#
# I breifly tried to setup and send to a local loopback device
# so wireshark could run on the same PC as the SmartRF packet
# sniffer, but I couldn't get it to work.
#
# This script was adapted from the udp_receiver.pl script that
# comes with SmartRF
use IO::Socket::INET;
use Time::HiRes;
# Disable output buffering
$| = 1;
#Local port setup in TI SmartRF Packet Sniffer "Settings->Packet Broadcast"
$receiveSocketPort= 5000;
#Default UDP port for ZEP packet decoding in wireshark
$sendSocketPort = 17754;
#PC IP address running wireshark
$sendSocketIpAddress = '192.168.1.72';
# Create receiver socket to connect to TI tool
my $receiveSocket = IO::Socket::INET->new(
Proto=>'udp',
LocalPort=>$receiveSocketPort
) or die "Could not create socket: $!\n";
print "\nUDP Server: waiting for client on port $receiveSocketPort ...";
# Create socket to remote PC running wireshark
my $sendSocket = IO::Socket::INET->new(
Proto => 'udp',
PeerPort => $sendSocketPort,
PeerAddr => $sendSocketIpAddress,
) or die "Could not create socket: $!\n";
# Eventually it would be nice to imply channel from the port.
# For example: use port 5012 for channel 12 and 5013 for channel
# 13 and then the deviceId could just be the port number
$chId = 12;
$deviceId = 0;
# counter for number of received packets from SmartRF tool.
$cnt= 1;
while(1)
{
# Wait for received data
$receiveSocket->recv($buffer,1024);
$n = length $buffer;
# Keep some simple debug print per packet to show that something is going on.
print "\n $cnt received : $n bytes\n";
#####################################################################################
# Unpact the header from the TI CC2531 dongle
# This format is defined in Section 5 of SmartRF Packet Sniffer User Manual.pdf
#####################################################################################
$hdr_info = unpack("C", substr($buffer, 0, 1));
$hdr_infoLenWithFcs = ($hdr_info & 0x01);
$hdr_infoCorrelationUsed = (($hdr_info & 0x02) >> 1);
$hdr_infoIncompletePacket = (($hdr_info & 0x04) >> 2);
$hdr_number = unpack("V", substr($buffer, 1, 4));
$hdr_timeStamp = unpack("Q", substr($buffer, 5, 8));
$hdr_timeStampLsb = unpack("V", substr($buffer, 5, 4));
$hdr_timeStampMsb = unpack("V", substr($buffer, 9, 4));
$hdr_len = unpack("C", substr($buffer, 13, 1));
# Need to check info bit to see if FCS bytes are included in length
$payload_len = $hdr_len;
if( $hdr_infoLenWithFcs == 1 )
{
# Payload len includes FCS bytes so adjust the payload length
$payload_len -= 2;
}
$payload = substr($buffer, 14, $payload_len);
$fcs = substr($buffer, (14 + $payload_len), 2);
$fcs_absRssi = -73 + unpack("C", substr($fcs, 0, 1));
$fcs_crcOk = ((unpack("C", substr($fcs, 1, 1)) & 0x80) >> 7);
$fcs_lqiCorrelation = (unpack("C", substr($fcs, 1, 1)) & 0x7F);
# Some printouts to verify parsing was OK. Not really needed so
# comment out for now
#print sprintf( "Header \tInfo:0x%02X \n\tNumber:%u \n\tTimeStamp:%u \n\tLen:%u \n", $hdr_info, $hdr_number, $hdr_timeStamp, $hdr_len );
#print sprintf( "RSSI:%2d CRC-OK:%d ", $fcs_absRssi, $fcs_crcOk );
#if( $hdr_infoCorrelationUsed == 1 )
#{
# print sprintf( "Correlation:%d\n", $fcs_lqiCorrelation );
#}
#else
#{
# print sprintf( "LQI:%d\n", $fcs_lqiCorrelation );
#}
#####################################################################################
# Start Building the ZEP header. Online documentation of this header is terrible.
# I referenced source code from wireshark to get format. epan/dissectors/packet-zep.c
#####################################################################################
# Need to grab the MAC frame-type from the payload to figure out which
# ZEP header to use. MAC frame type is the 3 lsb bits of the first byte
# Reference 802.15.4 spec for bit-fields in MAC header.
$macFrameType = (ord(substr($payload, 0, 1)) & 0x07);
$macSeqNum = ord(substr($payload, 2, 1));
# Debug printout of frame type and payload contents.
#print sprintf( "Payload: type:%d \n\t", $macFrameType );
#for($i=0; $i<$payload_len; $i++ )
#{
# print sprintf("%02X ", ord(substr($payload, $i, 1)));
#}
#print "\n";
# ZEP packet decoder expects RSSI to be signed absolute dBm and TI dongle sends up
# relative dB from -73dBm. FCS bytes are expected by wireshark at the tail end of
# the packet and the ZEP length is expected to include these FCS bytes
$zepFcs = pack("CC", $fcs_absRssi, (($fcs_crcOk << 7) | $fcs_lqiCorrelation));
# build different ZEP header depending on the mac frame type
# originally I Build ZEPv2 header for ACK but I don't like the fact that you lose
# the RSSI, Channel Number, and Device Id info. I want to support multiple devices/channels
# so I want to see which channel/device the ack was received on. If you want to use ZEPv2
# header for ACK then you need to drop the $payload and $zepFcs from the $zepPacket
# and you probably want to replace the sequence number in the ZEPv2 header with teh MAC
# sequence number so you can align the ack with the packet being ack'd
#
if( $macFrameType == 2 )
{
# Build ZEPv1 header to get complete ACK info in wireshark
$zepHdr_len = 16;
$zepHdr = pack("aaCCnCCNnCC", 'E', 'X', 1,
$chId, $deviceId, 0, $fcs_lqiCorrelation, 0, 0, 0, ($payload_len + 2) );
$zepPacket = $zepHdr . $payload . $zepFcs;
$zepPacketLen = ($zepHdr_len + $payload_len + 2);
}
else
{
# Build ZEPv2 header for data note: timestamp is probably bogus
$zepHdr_len = 32;
$zepHdr = pack("aaCCCnCCNNNQnC", 'E', 'X', 2,
$macFrameType, $chId, $deviceId, 0, $fcs_lqiCorrelation, $hdr_timeStampMsb, $hdr_timeStampLsb, $hdr_number, 0, 0, ($payload_len + 2) );
$zepPacket = $zepHdr . $payload . $zepFcs;
$zepPacketLen = ($zepHdr_len + $payload_len + 2);
}
#More debug printing.
#for($i=0; $i<$zepPacketLen; $i++ )
#{
# print sprintf("%02X ", ord(substr($zepPacket, $i, 1)));
#}
#print "\n";
# Send properly formatted ZEP packet to PC running Wireshark.
$sendSocket->send($zepPacket) or die "ZEP Packet Send error: $!\n";
$cnt++;
}