ChuckMash / ESPythoNOW

A python library for sending, receiving, and monitoring ESP-NOW messages
MIT License
42 stars 6 forks source link

'scapy.all' has no attribute 'get_if_raw_hwaddr' #3

Open TechTheGuy opened 2 weeks ago

TechTheGuy commented 2 weeks ago

Hey everybody, I first of all want to let you all know how awesome this module is!! Anyways, I had to work a bit to get it working. I am using python 3.9.5 with scapy 2.6.0 and, when running the first example in the readme, it fails. Here's the code:

from ESPythoNOW import *
import time

def callback(from_mac, to_mac, msg):
  print("ESP-NOW message from %s to %s: %s" % (from_mac, to_mac, msg))

espnow = ESPythoNow(interface="wlan0mon", callback=callback)
espnow.start()

while True:
  msg=b'\x01'
  espnow.send("FF:FF:FF:FF:FF:FF", msg)
  time.sleep(3)

And, when ran with sudo python test.py, it fails with:

Traceback (most recent call last):
  File "/home/mindyourbusiness/fix_espnow/ESPythoNOW/test.py", line 7, in <module>
    espnow = ESPythoNow(interface="wlan0mon", callback=callback)
  File "/home/mindyourbusiness/fix_espnow/ESPythoNOW/ESPythoNOW.py", line 38, in __init__
    self.local_hw_mac        = self.hw_mac_as_str(self.interface)        # Interface's actual HW MAC
  File "/home/mindyourbusiness/fix_espnow/ESPythoNOW/ESPythoNOW.py", line 248, in hw_mac_as_str
    return ("%02X:" * 6)[:-1] % tuple(scapy.orb(x) for x in scapy.get_if_raw_hwaddr(self.l2_socket.iface)[1])
AttributeError: module 'scapy.all' has no attribute 'get_if_raw_hwaddr'

I think I got it to work with my pull request: Line 247 defines the function hw_mac_as_str in a pretty weird way (it's got an unused argument(interface) and reconstructs the string version of the interface's mac address. What's the point of that if there's scapy's get_if_hwaddr function for it?

I fixed it (now there are no errors and Wireshark detects the packets) and am about to open a PR, once I figure out how to upload the fix... Give me a few minutes so I can feel the glory of publishing my first PR please :-)

TechTheGuy commented 2 weeks ago

Here's the pull request.

4

TechTheGuy commented 2 weeks ago

There's a problem: Wireshark sees the packets but the esp8266 doesn't receive them. That's weird. Here's the sender code

from ESPythoNOW import *
import time

def callback(from_mac, to_mac, msg):
  print("ESP-NOW message from %s to %s: %s" % (from_mac, to_mac, msg))

espnow = ESPythoNow(interface="wlan0mon", callback=callback)
espnow.start()

while True:
  msg=b'\x01\x10'
  espnow.send("d8:bf:c0:11:2e:cd", msg)
  time.sleep(3)

And here's the esp8266 code:

//I mostly copied this code : https://randomnerdtutorials.com/esp-now-esp8266-nodemcu-arduino-ide/
#include <Arduino.h>
#include <espnow.h>
#include <ESP8266WiFi.h>

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
  uint8_t a, b;
} struct_message;

// Create a struct_message called myData
struct_message myData;

// Callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&myData, incomingData, sizeof(myData));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("a: ");
  Serial.println(myData.a);
  Serial.print("b: ");
  Serial.println(myData.b);
  Serial.println();
}

void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);

  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  // Init ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
  esp_now_register_recv_cb(OnDataRecv);
  Serial.println(WiFi.channel());
}

void loop() {
  yield(); 
}

The receiver mac and channel is right (1) The esp doesn't send any serial packets...

ChuckMash commented 2 weeks ago

Must be some differences in the scapy versions, I'll do some testing when I have some time and get back to you on this and the PR.

MRIIOT commented 2 weeks ago

There's a problem: Wireshark sees the packets but the esp8266 doesn't receive them. That's weird. Here's the sender code

from ESPythoNOW import *
import time

def callback(from_mac, to_mac, msg):
  print("ESP-NOW message from %s to %s: %s" % (from_mac, to_mac, msg))

espnow = ESPythoNow(interface="wlan0mon", callback=callback)
espnow.start()

while True:
  msg=b'\x01\x10'
  espnow.send("d8:bf:c0:11:2e:cd", msg)
  time.sleep(3)

And here's the esp8266 code:

//I mostly copied this code : https://randomnerdtutorials.com/esp-now-esp8266-nodemcu-arduino-ide/
#include <Arduino.h>
#include <espnow.h>
#include <ESP8266WiFi.h>

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
  uint8_t a, b;
} struct_message;

// Create a struct_message called myData
struct_message myData;

// Callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&myData, incomingData, sizeof(myData));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("a: ");
  Serial.println(myData.a);
  Serial.print("b: ");
  Serial.println(myData.b);
  Serial.println();
}

void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);

  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  // Init ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
  esp_now_register_recv_cb(OnDataRecv);
  Serial.println(WiFi.channel());
}

void loop() {
  yield(); 
}

The receiver mac and channel is right (1) The esp doesn't send any serial packets...

Is your Linux setup receiving messages with your changes?

TechTheGuy commented 2 weeks ago

I tried running the code on a laptop and a desktop which both have interfaces that support monitor mode and packet injection (I tested them with airplay and they work). However, the sender computer receives its own packets via Wireshark but the receiver rarely does. in about 1 h of tinkering I managed to get the packages received by the receiver only once and only in Wireshark. When they are received I can read their content in Wireshark but espythonow does not show them. And yes, I set the correct receiver mac... It's as if only a specified set of channels works. Edit for clarity: I didn't check if the sender can receive its own packets via the library. I will

ChuckMash commented 2 weeks ago

A couple things to check:

What is the output (if any) from prep.sh being run with the correct interface and channel set?

Try adding adding accept_all espnow = ESPythoNow(interface="wlan0mon", callback=callback, accept_all=True)

Can you describe your setup? I'll have time to examine this problem more deeply in about a week.

TechTheGuy commented 2 weeks ago

sudo bash prep.sh wlan0mon 8 Returns nothing and effectively works (it does almost exactly what I did manually: Iwconfig:

wlan0mon  IEEE 802.11  Mode:Monitor  Frequency:2.447 GHz  Tx-Power=20 dBm   
          Retry short limit:7   RTS thr=2347 B   Fragment thr:off
          Power Management:on

I also enabled the accept_all flag and it now somehow works: When either computer sends a message to the other one, the library on the recipient side sees the packet! It reads "ESP-NOW message from [src mac] to [destination mac]: b''" where destination mac is the mac of the computer that's outputting the text. So it works. However, the message is read as empty.

To sum up, I now used the prep script and enabled that accept_all flag. I can now receive the packets but not the messages.

Also, if I turn off the accept_all flag the program stops outputting data (even though its the right receiver mac). That's weird.

Edit: Wireshark on the sender side shows that each packet gets retransmitted a few times and only the first transmission contains the intended message. On the other hand, Wireshark on the receiver side never shows the payload.

ChuckMash commented 1 week ago

accept_all should only mean it will accept any message no matter the recipient MAC. Sound like there may be a MAC address mismatch.

As for the missing function, I used/use scapy version 2.5.0 for development, so the function must be removed for version 2.6.0. I'll take a look.

There was a reason for the strange method of gathering the local MAC, I'll have to try and track down why I did it like that initially. It may be related to local MAC spoofing.

I'll keeping looking.