whad-team / whad-client

Wireless Hacking Devices Protocol client
MIT License
132 stars 8 forks source link

Improve ZigBee mac frame counters (save them into current state) #86

Open olijf opened 3 weeks ago

olijf commented 3 weeks ago

It seems that after sending several beacon frames the counter becomes > 255 and causes scapy to throw an exception.

I am trying to setup a Zigbee coordinator using the coordinator example, but I can not get any Zigbee lamps to join the network, they just keep exchanging beacon frames. Thats probably why the sequence counter got so high.

I have done some debugging but I can not find where the sequence counter is being tracked.

Below you can find the log.

Exception in thread Thread-2:
Traceback (most recent call last):
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/scapy/fields.py", line 243, in addfield
    return s + self.struct.pack(self.i2m(pkt, val))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
struct.error: ubyte format requires 0 <= number <= 255

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.11/threading.py", line 1038, in _bootstrap_inner
    self.run()
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/device/__init__.py", line 777, in run
    self.__device.process_messages()
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/device/__init__.py", line 1432, in process_messages
    self.dispatch_message(message)
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/device/__init__.py", line 1395, in dispatch_message
    self.on_domain_msg(domain, message)
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/device/__init__.py", line 1673, in on_domain_msg
    self.__connector.on_packet(packet)
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/dot15d4/connector/__init__.py", line 326, in on_packet
    self.on_pdu(packet)
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/zigbee/connector/coordinator.py", line 192, in on_pdu
    self.__stack.on_pdu(packet)
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/dot15d4/stack/__init__.py", line 52, in on_pdu
    self.send('mac', pdu, tag='pdu')
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/common/stack/layer.py", line 598, in send
    return self.send_from(self.name, destination, data, tag=tag, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/common/stack/layer.py", line 624, in send_from
    handler(data, **kwargs)
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/dot15d4/stack/mac/__init__.py", line 863, in on_pdu
    self.get_service("management").on_cmd_pdu(pdu)
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/dot15d4/stack/mac/__init__.py", line 747, in on_cmd_pdu
    self.beacon()
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/dot15d4/stack/service.py", line 81, in request_decorator
    result = func(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/dot15d4/stack/mac/__init__.py", line 249, in beacon
    self.manager.send_beacon(
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/dot15d4/stack/mac/__init__.py", line 1084, in send_beacon
    self.send('phy', packet, tag='pdu')
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/common/stack/layer.py", line 598, in send
    return self.send_from(self.name, destination, data, tag=tag, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/common/stack/layer.py", line 624, in send_from
    handler(data, **kwargs)
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/dot15d4/stack/__init__.py", line 107, in transmit
    self.__connector.send(packet)
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/zigbee/connector/coordinator.py", line 180, in send
    return super().send(pdu, channel=self.__channel)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/whad/dot15d4/connector/__init__.py", line 221, in send
    packet = Dot15d4FCS(raw(pdu) + Dot15d4FCS().compute_fcs(raw(pdu)))
                        ^^^^^^^^
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/scapy/compat.py", line 294, in raw
    return bytes(x)
           ^^^^^^^^
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/scapy/packet.py", line 589, in __bytes__
    return self.build()
           ^^^^^^^^^^^^
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/scapy/packet.py", line 730, in build
    p = self.do_build()
        ^^^^^^^^^^^^^^^
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/scapy/packet.py", line 710, in do_build
    pkt = self.self_build()
          ^^^^^^^^^^^^^^^^^
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/scapy/packet.py", line 689, in self_build
    raise ex
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/scapy/packet.py", line 680, in self_build
    p = f.addfield(self, p, val)
        ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/olaf/git/zigbee-learning/.venv/lib/python3.11/site-packages/scapy/fields.py", line 245, in addfield
    raise ValueError(
ValueError: While dissecting field 'seqnum': Incorrect type of value for field seqnum:
struct.error('ubyte format requires 0 <= number <= 255')
To inject bytes into the field regardless of the type, use RawVal. See help(RawVal)
olijf commented 1 week ago

I have narrowed it down to the fact that you do not check if the sequence numbers are actually flipped around back to zero after reaching 255: mac/init.py There are several sequence counters: macBeaconSequenceNumber and macDataSequenceNumber But these should be 0 <= number <= 255 otherwise they wont fit the ubyte

Maybe something like this works? on line 1083: self.database.set("macBeaconSequenceNumber", sequence_number + 1 % 255)

I think it would be best to keep control of the max value of the sequence counters inside your database.py otherwise the different layers are responsible for keeping track.

olijf commented 1 week ago

This works:

1013:self.database.set("macDataSequenceNumber", (sequence_number + 1) % 256)
1083:self.database.set("macBeaconSequenceNumber", (sequence_number + 1) % 256)

Pay attention to the ( and ) apparently % takes precedence over + in math.

virtualabs commented 6 days ago

Thank you for this fix, it has been applied and merged into main. I keep this issue open as a reminder for implementing better sequence number tracking.

olijf commented 6 days ago

Thank you! Could you maybe ask Romain to reply to my email? I really want to use WHAD for my thesis, but some things are just not working as I expect. They could either be because of bugs, or because of me. I would like to get in contact so I figure out what is the issue, but I do not have a current email address I think.