Skip to content

Commit 23dfff7

Browse files
authored
Fixed IEEE 802.11 Beacon byte ordering. (#628)
* Fixed Beacon byte ordering. * Added support for decoding fragment number and sequence number off of packets that include them.
1 parent a39d518 commit 23dfff7

File tree

2 files changed

+61
-6
lines changed

2 files changed

+61
-6
lines changed

dpkt/compat.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ def ntole(v):
4747
return unpack('<H', pack('!H', v))[0]
4848

4949

50+
def ntole64(v):
51+
"""
52+
Convert an 8-byte word from network byte order (big endian) to little endian.
53+
"""
54+
return unpack('<Q', pack('!Q', v))[0]
55+
56+
5057
def isstr(s):
5158
"""True if 's' is an instance of basestring in py2, or of str in py3"""
5259
bs = getattr(__builtins__, 'basestring', str)

dpkt/ieee80211.py

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import struct
88

99
from . import dpkt
10-
from .compat import ntole
10+
from .compat import ntole, ntole64
1111

1212
# Frame Types
1313
MGMT_TYPE = 0
@@ -66,6 +66,8 @@
6666
_MORE_DATA_MASK = 0x0020
6767
_WEP_MASK = 0x0040
6868
_ORDER_MASK = 0x0080
69+
_FRAGMENT_NUMBER_MASK = 0x000F
70+
_SEQUENCE_NUMBER_MASK = 0XFFF0
6971
_VERSION_SHIFT = 8
7072
_TYPE_SHIFT = 10
7173
_SUBTYPE_SHIFT = 12
@@ -77,6 +79,7 @@
7779
_MORE_DATA_SHIFT = 5
7880
_WEP_SHIFT = 6
7981
_ORDER_SHIFT = 7
82+
_SEQUENCE_NUMBER_SHIFT = 4
8083

8184
# IEs
8285
IE_SSID = 0
@@ -437,6 +440,15 @@ def unpack(self, buf):
437440
self.bmp = struct.unpack('128s', self.data[0:_BMP_LENGTH])[0]
438441
self.data = self.data[len(self.__hdr__) + len(self.bmp):]
439442

443+
class _FragmentNumSeqNumMixin(object):
444+
@property
445+
def fragment_number(self):
446+
return ntole(self.frag_seq) & _FRAGMENT_NUMBER_MASK
447+
448+
@property
449+
def sequence_number(self):
450+
return (ntole(self.frag_seq) & _SEQUENCE_NUMBER_MASK) >> _SEQUENCE_NUMBER_SHIFT
451+
440452
class RTS(dpkt.Packet):
441453
__hdr__ = (
442454
('dst', '6s', '\x00' * 6),
@@ -459,7 +471,7 @@ class CFEnd(dpkt.Packet):
459471
('src', '6s', '\x00' * 6),
460472
)
461473

462-
class MGMT_Frame(dpkt.Packet):
474+
class MGMT_Frame(dpkt.Packet, _FragmentNumSeqNumMixin):
463475
__hdr__ = (
464476
('dst', '6s', '\x00' * 6),
465477
('src', '6s', '\x00' * 6),
@@ -474,6 +486,11 @@ class Beacon(dpkt.Packet):
474486
('capability', 'H', 0)
475487
)
476488

489+
def unpack(self, buf):
490+
dpkt.Packet.unpack(self, buf)
491+
self.timestamp = ntole64(self.timestamp)
492+
self.interval = ntole(self.interval)
493+
477494
class Disassoc(dpkt.Packet):
478495
__hdr__ = (
479496
('reason', 'H', 0),
@@ -562,31 +579,31 @@ class BlockAckActionDelba(dpkt.Packet):
562579
# ('gcr_group_addr', '8s', '\x00' * 8), # Standard says it must be there, but it isn't?
563580
)
564581

565-
class Data(dpkt.Packet):
582+
class Data(dpkt.Packet, _FragmentNumSeqNumMixin):
566583
__hdr__ = (
567584
('dst', '6s', '\x00' * 6),
568585
('src', '6s', '\x00' * 6),
569586
('bssid', '6s', '\x00' * 6),
570587
('frag_seq', 'H', 0)
571588
)
572589

573-
class DataFromDS(dpkt.Packet):
590+
class DataFromDS(dpkt.Packet, _FragmentNumSeqNumMixin):
574591
__hdr__ = (
575592
('dst', '6s', '\x00' * 6),
576593
('bssid', '6s', '\x00' * 6),
577594
('src', '6s', '\x00' * 6),
578595
('frag_seq', 'H', 0)
579596
)
580597

581-
class DataToDS(dpkt.Packet):
598+
class DataToDS(dpkt.Packet, _FragmentNumSeqNumMixin):
582599
__hdr__ = (
583600
('bssid', '6s', '\x00' * 6),
584601
('src', '6s', '\x00' * 6),
585602
('dst', '6s', '\x00' * 6),
586603
('frag_seq', 'H', 0)
587604
)
588605

589-
class DataInterDS(dpkt.Packet):
606+
class DataInterDS(dpkt.Packet, _FragmentNumSeqNumMixin):
590607
__hdr__ = (
591608
('dst', '6s', '\x00' * 6),
592609
('src', '6s', '\x00' * 6),
@@ -726,6 +743,8 @@ def test_80211_data():
726743
assert ieee.data_frame.dst == b'\x00\x02\xb3\xd6\x26\x3c'
727744
assert ieee.data_frame.src == b'\x00\x16\x44\xb0\xae\xc6'
728745
assert ieee.data_frame.frag_seq == 0x807e
746+
assert ieee.data_frame.fragment_number == 0
747+
assert ieee.data_frame.sequence_number == 2024
729748
assert ieee.data == (b'\xaa\xaa\x03\x00\x00\x00\x08\x00\x45\x00\x00\x28\x07\x27\x40\x00\x80\x06'
730749
b'\x1d\x39\x8d\xd4\x37\x3d\x3f\xf5\xd1\x69\xc0\x5f\x01\xbb\xb2\xd6\xef\x23'
731750
b'\x38\x2b\x4f\x08\x50\x10\x42\x04')
@@ -754,6 +773,8 @@ def test_80211_data_qos():
754773
assert ieee.data_frame.dst == b'\x00\x26\xcb\x17\x44\xf0'
755774
assert ieee.data_frame.src == b'\x00\x23\xdf\xc9\xc0\x93'
756775
assert ieee.data_frame.frag_seq == 0x207b
776+
assert ieee.data_frame.fragment_number == 0
777+
assert ieee.data_frame.sequence_number == 1970
757778
assert ieee.data == (b'\xaa\xaa\x03\x00\x00\x00\x88\x8e\x01\x00\x00\x74\x02\x02\x00\x74\x19\x80'
758779
b'\x00\x00\x00\x6a\x16\x03\x01\x00\x65\x01\x00\x00\x61\x03\x01\x4b\x4c\xa7'
759780
b'\x7e\x27\x61\x6f\x02\x7b\x3c\x72\x39\xe3\x7b\xd7\x43\x59\x91\x7f\xaa\x22'
@@ -988,3 +1009,30 @@ def test_action_unpack():
9881009
)
9891010
with pytest.raises(dpkt.UnpackError, match="KeyError: category=1 code=0"):
9901011
IEEE80211.Action(buf)
1012+
1013+
1014+
def test_beacon_unpack():
1015+
beacon_payload = b"\xb9\x71\xfa\x45\x52\x02\x00\x00\x64\x00\x11\x04"
1016+
beacon = IEEE80211.Beacon(beacon_payload)
1017+
assert beacon.timestamp == 0x0000025245fa71b9
1018+
assert beacon.interval == 100
1019+
assert beacon.capability == 0x1104
1020+
1021+
1022+
def test_fragment_and_sequence_values():
1023+
from binascii import unhexlify
1024+
for raw_frag_seq, (expected_frag_num, expected_seq_num) in [
1025+
("0000", (0, 0)),
1026+
("0F00", (15, 0)),
1027+
("0111", (1, 272)),
1028+
("B3FF", (3, 4091))
1029+
]:
1030+
buf = unhexlify(
1031+
'000000000000' # dst
1032+
'000000000000' # src
1033+
'000000000000' # bssid
1034+
+ raw_frag_seq
1035+
)
1036+
data = IEEE80211.Data(buf)
1037+
assert data.fragment_number == expected_frag_num
1038+
assert data.sequence_number == expected_seq_num

0 commit comments

Comments
 (0)