Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 131 additions & 1 deletion scapy/layers/dot11.py
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,8 @@ def network_stats(self):
127: "Extended Capabilities",
191: "VHT Capabilities",
192: "VHT Operation",
221: "Vendor Specific"
221: "Vendor Specific",
255: "Element ID Extension"
}

# Backward compatibility
Expand Down Expand Up @@ -1555,6 +1556,135 @@ class Dot11EltVHTOperation(Dot11Elt):
]


# 802.11ax-2021 9.4.2.1, Table 9-92

class Dot11EltExtension(Dot11Elt):
# Element ID Extension is a registry for many IEs. Only HE Operation is
# specialized here; unknown and unsupported extension IDs stay generic.
name = "802.11 Element ID Extension"
match_subclass = True
ID = 255
fields_desc = [
ByteEnumField("ID", 255, _dot11_id_enum),
ByteField("len", 0),
ConditionalField(
ByteField("ext_ID", 0),
lambda pkt: pkt.len > 0
),
]

@classmethod
def dispatch_hook(cls, _pkt=None, *args, **kargs):
if not _pkt or len(_pkt) < 3:
return cls

if _pkt:
ext_id = orb(_pkt[2])
idcls = cls.registered_ext_ids.get(ext_id)
if idcls is not None:
return idcls
return Dot11EltExtensionGeneric
return cls

registered_ext_ids = {}

@classmethod
def register_variant(cls, ext_id=None):
ext_id = ext_id or cls.ext_ID.default
if not ext_id:
# This is Dot11EltExtension, register it in the super-class.
super().register_variant()
elif ext_id not in cls.registered_ext_ids:
# Register extension ID to class (e.g. Dot11EltHEOperation)
cls.registered_ext_ids[ext_id] = cls


# 802.11ax-2021 9.4.2.1, Table 9-92

class Dot11EltExtensionGeneric(Dot11EltExtension):
name = "802.11 Element ID Extension"
match_subclass = True
ID = 255
fields_desc = Dot11EltExtension.fields_desc + [
StrLenField("info", b"", length_from=lambda pkt: max(pkt.len - 1, 0))]


# 802.11ax-2021 9.4.2.249, Figure 9-788k

class Dot11HE6GOperationInfo(Packet):
name = "802.11 HE 6 GHz Operation Information"
fields_desc = [
ByteField("primary_channel", 0),
# Control: 1B
BitField("reserved", 0, 2, tot_size=-1),
BitField("regulatory_info", 0, 3),
BitField("duplicate_beacon", 0, 1),
BitField("channel_width", 0, 2, end_tot_size=-1),

ByteField("channel_center0", 0),
ByteField("channel_center1", 0),
ByteField("minimum_rate", 0),
]

def extract_padding(self, s):
return "", s


# 802.11ax-2021 9.4.2.249

class Dot11EltHEOperation(Dot11EltExtension):
name = "802.11 HE Operation"
match_subclass = True
ID = 255
ext_ID = 36
fields_desc = Dot11EltExtension.fields_desc + [
# HE Operation Parameters: 3B
BitField("reserved", 0, 6, tot_size=-3),
BitField("six_g_op_info_present", 0, 1),
BitField("er_su_disable", 0, 1),
BitField("co_hosted_bss", 0, 1),
BitField("vht_operation_info_present", 0, 1),
BitField("txop_duration_rts_threshold", 0, 10),
BitField("twt_required", 0, 1),
BitField("default_pe_duration", 0, 3, end_tot_size=-3),

# BSS Color Information: 1B
BitField("bss_color_disabled", 0, 1, tot_size=-1),
BitField("partial_bss_color", 0, 1),
BitField("bss_color", 0, 6, end_tot_size=-1),
# Basic HE-MCS and NSS Set: 2B
FieldListField(
"basic_he_mcs_and_nss_set",
[0x00],
BitField('SS', 0x00, size=2),
count_from=lambda x: 8
),

ConditionalField(
PacketField(
"vht_operation_info",
Dot11VHTOperationInfo(),
Dot11VHTOperationInfo
),
lambda p: p.vht_operation_info_present == 1),

ConditionalField(
ByteField("max_co_hosted_bssid", 0),
lambda p: p.co_hosted_bss == 1),

ConditionalField(
PacketField(
"he_6g_operation_info",
Dot11HE6GOperationInfo(),
Dot11HE6GOperationInfo
),
lambda p: p.six_g_op_info_present == 1)
]


Dot11EltHEOperation.register_variant(36)


######################
# 802.11 Frame types #
######################
Expand Down
46 changes: 46 additions & 0 deletions test/scapy/layers/dot11.uts
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,52 @@ assert pkt[Dot11EltVHTOperation].VHT_Operation_Info.channel_center1 == 50
pkt = Dot11EltVHTOperation(b'\xc0\x05\x01*2\x00\x00')
assert pkt[Dot11Elt::{"ID": 192}].len == 5

= Dot11EltExtension generic in isolation

data = b'\xff\x03\x30\x01\x02'
pkt = Dot11Elt(data)
assert Dot11EltExtensionGeneric in pkt
assert pkt[Dot11EltExtensionGeneric].ID == 255
assert pkt[Dot11EltExtensionGeneric].len == 3
assert pkt[Dot11EltExtensionGeneric].ext_ID == 48
assert pkt[Dot11EltExtensionGeneric].getfieldval("info") == b'\x01\x02'
assert raw(pkt) == data

= Dot11EltExtension short element in isolation

pkt = Dot11Elt(b'\xff\x00')
assert Dot11EltExtension in pkt
assert pkt[Dot11EltExtension].ID == 255
assert pkt[Dot11EltExtension].len == 0
assert raw(pkt) == b'\xff\x00'

= Dot11EltHEOperation in isolation

data = b'\xff\x07$\x00\x00\x00\x00\x00\x00'
pkt = Dot11Elt(data)
assert Dot11EltHEOperation in pkt
assert pkt[Dot11EltHEOperation].ID == 255
assert pkt[Dot11EltHEOperation].len == 7
assert pkt[Dot11EltHEOperation].ext_ID == 36
assert pkt[Dot11EltHEOperation].six_g_op_info_present == 0
assert pkt[Dot11EltHEOperation].vht_operation_info_present == 0
assert pkt[Dot11EltHEOperation].basic_he_mcs_and_nss_set == [0] * 8
assert raw(pkt) == data

= Dot11EltHEOperation with 6 GHz Operation Information in isolation

data = b'\xff\x0c$\x00\x00\x02\x00\x00\x00\x05\x02%\x00\x0c'
pkt = Dot11Elt(data)
assert Dot11EltHEOperation in pkt
assert Dot11HE6GOperationInfo in pkt
assert pkt[Dot11EltHEOperation].six_g_op_info_present == 1
assert pkt[Dot11HE6GOperationInfo].primary_channel == 5
assert pkt[Dot11HE6GOperationInfo].channel_width == 2
assert pkt[Dot11HE6GOperationInfo].channel_center0 == 37
assert pkt[Dot11HE6GOperationInfo].channel_center1 == 0
assert pkt[Dot11HE6GOperationInfo].minimum_rate == 12
assert raw(pkt) == data

= Dot11EltOBSS in isolation

pkt = Dot11EltOBSS(b'J\x0e\x14\x00\n\x00,\x01\xc8\x00\x14\x00\x05\x00\x19\x00')
Expand Down