mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-27 12:20:56 +00:00
Add IEEE802.15.4 Wireshark Extcap (#1636)
This commit is contained in:
parent
79d617da38
commit
83dfb5b803
@ -18,5 +18,6 @@ exclude = [
|
||||
"examples",
|
||||
"extras/bench-server",
|
||||
"extras/esp-wifishark",
|
||||
"extras/ieee802154-sniffer",
|
||||
"hil-test",
|
||||
]
|
||||
|
75
examples/src/bin/ieee802154_sniffer.rs
Normal file
75
examples/src/bin/ieee802154_sniffer.rs
Normal file
@ -0,0 +1,75 @@
|
||||
//! While this can be used as an example it's meant to be used with `extras/ieee802154-sniffer`
|
||||
//!
|
||||
//! Besides the runtime changeable channel and the output format it's almost identical to ieee802154_receive_all_frames
|
||||
|
||||
//% CHIPS: esp32c6 esp32h2
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
clock::ClockControl,
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
reset::software_reset,
|
||||
system::SystemControl,
|
||||
uart::Uart,
|
||||
};
|
||||
use esp_ieee802154::*;
|
||||
use esp_println::println;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let mut peripherals = Peripherals::take();
|
||||
let system = SystemControl::new(peripherals.SYSTEM);
|
||||
let clocks = ClockControl::max(system.clock_control).freeze();
|
||||
|
||||
let mut uart0 = Uart::new(peripherals.UART0, &clocks);
|
||||
|
||||
// read two characters which get parsed as the channel
|
||||
let mut cnt = 0;
|
||||
let mut read = [0u8; 2];
|
||||
loop {
|
||||
let c = nb::block!(uart0.read_byte()).unwrap();
|
||||
if c == b'r' {
|
||||
continue;
|
||||
}
|
||||
|
||||
read[cnt] = c;
|
||||
cnt += 1;
|
||||
|
||||
if cnt >= 2 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let channel: u8 = unsafe { core::str::from_utf8_unchecked(&read) }
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
let radio = peripherals.IEEE802154;
|
||||
let mut ieee802154 = Ieee802154::new(radio, &mut peripherals.RADIO_CLK);
|
||||
|
||||
ieee802154.set_config(Config {
|
||||
channel,
|
||||
promiscuous: true,
|
||||
rx_when_idle: true,
|
||||
auto_ack_rx: false,
|
||||
auto_ack_tx: false,
|
||||
..Config::default()
|
||||
});
|
||||
|
||||
ieee802154.start_receive();
|
||||
|
||||
loop {
|
||||
if let Some(frame) = ieee802154.get_raw_received() {
|
||||
println!("@RAW {:02x?}", &frame.data);
|
||||
}
|
||||
|
||||
if let nb::Result::Ok(c) = uart0.read_byte() {
|
||||
if c == b'r' {
|
||||
software_reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
r-extcap = { git = "https://github.com/bjoernQ/r-extcap.git", branch = "fix-open-pipe-windows" }
|
||||
r-extcap = "0.2.4"
|
||||
pcap-file = "2.0.0"
|
||||
serialport = "4.2.1"
|
||||
clap = { version = "4.3.5", features = ["derive"] }
|
||||
|
11
extras/ieee802154-sniffer/Cargo.toml
Normal file
11
extras/ieee802154-sniffer/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "sniffer"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
r-extcap = "0.2.4"
|
||||
pcap-file = "2.0.0"
|
||||
serialport = "4.2.0"
|
||||
clap = { version = "4.1.7", features = ["derive"] }
|
||||
lazy_static = "1.4.0"
|
13
extras/ieee802154-sniffer/README.md
Normal file
13
extras/ieee802154-sniffer/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# IEEE 802.15.4 Sniffer
|
||||
|
||||
This is an extcap to be used with the `ieee802154_sniffer` example. (Make sure the ESP32-C6/ESP32-H2 is connected via UART-bridge, not JTAG-Serial)
|
||||
|
||||
To use it, build via `cargo build --release` and copy the resulting executable to the Wireshark's `extcap` folder.
|
||||
|
||||
Then you should see a new capture interface in Wireshark
|
||||
|
||||
If you are running the ieee802154_sniffer example this capture interface can connect via serialport to give you insights on what is going on.
|
||||
|
||||
By default it tries to identify exactly one serialport. If that doesn't work for you, you can configure the serialport via the Wireshark UI.
|
||||
|
||||
In Wireshark use `ITU-T-CRC-16` as `FCS format`
|
281
extras/ieee802154-sniffer/src/main.rs
Normal file
281
extras/ieee802154-sniffer/src/main.rs
Normal file
@ -0,0 +1,281 @@
|
||||
use std::{
|
||||
io::{stdout, BufRead, BufReader, Write},
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use lazy_static::lazy_static;
|
||||
use pcap_file::{
|
||||
pcap::{PcapHeader, PcapPacket, PcapWriter},
|
||||
DataLink,
|
||||
};
|
||||
use r_extcap::{
|
||||
config::{ConfigOptionValue, SelectorConfig, StringConfig},
|
||||
controls::{synchronous::ExtcapControlSender, ControlCommand, ControlPacket},
|
||||
interface::{Dlt, Interface, Metadata},
|
||||
ExtcapStep,
|
||||
};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct AppArgs {
|
||||
#[command(flatten)]
|
||||
extcap: r_extcap::ExtcapArgs,
|
||||
|
||||
#[arg(long, default_value = "")]
|
||||
serialport: String,
|
||||
|
||||
#[arg(long, default_value = "11")]
|
||||
channel: String,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref METADATA: Metadata = Metadata {
|
||||
help_url: "http://github.com/esp-rs".into(),
|
||||
display_description: "esp-ieee802154".into(),
|
||||
..r_extcap::cargo_metadata!()
|
||||
};
|
||||
static ref WIFI_CAPTURE_INTERFACE: Interface = Interface {
|
||||
value: "802.15.4".into(),
|
||||
display: "esp-ieee802154 Sniffer".into(),
|
||||
dlt: Dlt {
|
||||
data_link_type: DataLink::USER0,
|
||||
name: "USER0".into(),
|
||||
display: "IEEE802.15.4".into(),
|
||||
},
|
||||
};
|
||||
static ref CONFIG_SERIALPORT: StringConfig = StringConfig::builder()
|
||||
.config_number(1)
|
||||
.call("serialport")
|
||||
.display("Serialport")
|
||||
.tooltip("Serialport to connect to")
|
||||
.required(false)
|
||||
.placeholder("")
|
||||
.build();
|
||||
static ref CONFIG_CHANNEL: SelectorConfig = SelectorConfig::builder()
|
||||
.config_number(3)
|
||||
.call("channel")
|
||||
.display("Channel")
|
||||
.tooltip("Channel Selector")
|
||||
.default_options([
|
||||
ConfigOptionValue::builder()
|
||||
.value("11")
|
||||
.display("11")
|
||||
.default(true)
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("12")
|
||||
.display("12")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("13")
|
||||
.display("13")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("14")
|
||||
.display("14")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("15")
|
||||
.display("15")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("16")
|
||||
.display("16")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("17")
|
||||
.display("17")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("18")
|
||||
.display("18")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("19")
|
||||
.display("19")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("20")
|
||||
.display("20")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("21")
|
||||
.display("21")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("22")
|
||||
.display("22")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("23")
|
||||
.display("23")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("24")
|
||||
.display("24")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("25")
|
||||
.display("25")
|
||||
.build(),
|
||||
ConfigOptionValue::builder()
|
||||
.value("26")
|
||||
.display("26")
|
||||
.build(),
|
||||
])
|
||||
.build();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = AppArgs::parse();
|
||||
|
||||
if !args.extcap.capture {
|
||||
if let Some(_filter) = args.extcap.extcap_capture_filter {
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
match args.extcap.run().unwrap() {
|
||||
ExtcapStep::Interfaces(interfaces_step) => {
|
||||
interfaces_step.list_interfaces(&METADATA, &[&*WIFI_CAPTURE_INTERFACE], &[]);
|
||||
}
|
||||
ExtcapStep::Dlts(dlts_step) => {
|
||||
dlts_step
|
||||
.print_from_interfaces(&[&*WIFI_CAPTURE_INTERFACE])
|
||||
.unwrap();
|
||||
}
|
||||
ExtcapStep::Config(config_step) => {
|
||||
config_step.list_configs(&[&*CONFIG_SERIALPORT, &*CONFIG_CHANNEL])
|
||||
}
|
||||
ExtcapStep::ReloadConfig(_reload_config_step) => {
|
||||
panic!("Unsupported operation");
|
||||
}
|
||||
ExtcapStep::Capture(capture_step) => {
|
||||
let (data_link, prefix) = (DataLink::IEEE802_15_4, "@RAW [");
|
||||
|
||||
let mut controls = (
|
||||
capture_step.spawn_channel_control_reader(),
|
||||
capture_step.new_control_sender(),
|
||||
);
|
||||
|
||||
if let (Some(control_reader), Some(_control_sender)) = &mut controls {
|
||||
let packet = control_reader.read_packet().unwrap();
|
||||
assert_eq!(packet.command, ControlCommand::Initialized);
|
||||
}
|
||||
|
||||
let pcap_header = PcapHeader {
|
||||
datalink: data_link,
|
||||
endianness: pcap_file::Endianness::Big,
|
||||
..Default::default()
|
||||
};
|
||||
let mut pcap_writer = PcapWriter::with_header(capture_step.fifo, pcap_header).unwrap();
|
||||
|
||||
let channel = args.channel.clone();
|
||||
|
||||
let serialport = if args.serialport.is_empty() {
|
||||
let ports = serialport::available_ports().unwrap();
|
||||
if ports.len() != 1 {
|
||||
panic!("There are more or less than one serial ports. Don't know which one to use.");
|
||||
}
|
||||
ports[0].port_name.clone()
|
||||
} else {
|
||||
args.serialport.clone()
|
||||
};
|
||||
|
||||
let mut port = serialport::new(serialport, 115_200)
|
||||
.timeout(Duration::from_millis(100))
|
||||
.open()
|
||||
.expect("Failed to open port");
|
||||
|
||||
// drain the input ... just to be on the safe side
|
||||
while let Ok(len) = port.read(&mut [0u8; 128]) {
|
||||
if len == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// maybe reset the target
|
||||
port.write_all(&[b'r']).unwrap();
|
||||
|
||||
// wait for target to boot up
|
||||
std::thread::sleep(Duration::from_millis(1000));
|
||||
|
||||
// configure channel
|
||||
port.write_all(format!("{:02}", (channel).parse::<u16>().unwrap()).as_bytes())
|
||||
.unwrap();
|
||||
|
||||
let mut buf_read = BufReader::new(port);
|
||||
|
||||
let mut packet = Vec::<u8>::new();
|
||||
let mut line = String::new();
|
||||
|
||||
loop {
|
||||
if let (Some(control_reader), Some(control_sender)) = &mut controls {
|
||||
if let Some(control_packet) = control_reader.try_read_packet() {
|
||||
handle_control_packet(&control_packet, control_sender).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
packet.clear();
|
||||
line.clear();
|
||||
if let Ok(len) = buf_read.read_line(&mut line) {
|
||||
if len > 0 {
|
||||
if line.contains(prefix) {
|
||||
if !line.contains(']') {
|
||||
panic!("Unexpected {}", line);
|
||||
}
|
||||
|
||||
let start = line.find(prefix).unwrap() + prefix.len();
|
||||
let end = line.find(']').unwrap();
|
||||
let line = line[start..end].to_string();
|
||||
for hex in line.split(", ") {
|
||||
let byte = u8::from_str_radix(hex, 16).unwrap();
|
||||
packet.push(byte);
|
||||
}
|
||||
}
|
||||
|
||||
if !packet.is_empty() {
|
||||
let len = packet[0] - 2;
|
||||
let crc = crc(&packet[1..][..len as usize]);
|
||||
packet.insert(1 + (len as usize), crc[0]);
|
||||
packet.insert(1 + (len as usize) + 1, crc[1]);
|
||||
|
||||
pcap_writer
|
||||
.write_packet(&PcapPacket::new(
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).unwrap(),
|
||||
(len as u32) + 2,
|
||||
&packet[1..][..(len + 2) as usize],
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
stdout().flush().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn crc(buf: &[u8]) -> [u8; 2] {
|
||||
let mut c: u16 = 0;
|
||||
for &i in buf {
|
||||
for k in 0..8 {
|
||||
let b = ((i & (1 << k)) != 0) ^ ((c & 1) != 0);
|
||||
c >>= 1;
|
||||
if b {
|
||||
c ^= 1 << 15;
|
||||
c ^= 1 << 10;
|
||||
c ^= 1 << 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
[((c & 0xFF) as u8), ((c >> 8) as u8)]
|
||||
}
|
||||
|
||||
fn handle_control_packet(
|
||||
_control_packet: &ControlPacket<'_>,
|
||||
_control_sender: &mut ExtcapControlSender,
|
||||
) -> Result<(), ()> {
|
||||
// currently nothing to do here
|
||||
Ok(())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user