Repository: secdev/scapy (language: python)

Issue:
Some dot11 IEs don't use 'match_subclass' and cannot be searched via the Dot11Elem class
### Brief description

From review of the unit tests for dot11, I notice that searching for IEs can be obtained using the following pattern:

pkt[Dot11Elt::{"ID": <ID>}]

This works for most IEs, but the following do not work properly in isolation:

Dot11EltVHTOperation
Dot11EltOBSS
Dot11EltCSA

I have added unit tests to confirm operation of these IEs in isolation. Underlying this issue I suspect there are some issues in the getlayer implementation, as Dot11EltVHTOperation works as expected when part of a larger packet. As an example, in unit test 6FB08FA4, add in the line 'assert pkt[Dot11Elt::{"ID": 192}].len == 5' and the code will fail to find the given layer.

I am not confident I understand the motivation behind the 'match_subclass' class variable, and I suspect it is not robust given the above example.

The fix is seemingly to add 'match_subclass' to any subclass of Dot11Elt, and the search for IE via the Dot11Elt ID field works fully again.

### Scapy version

7233cb6fa6d6d876876f0845811512452a9505f8

### Python version

3.12.2

### Operating system

MacOS Sonoma 14.4

### Additional environment information

_No response_

### How to reproduce

See the PR I will submit - the new unit tests will fail with removal of 'match_subclass' from the appropriate IE class.

### Actual result

_No response_

### Expected result

_No response_

### Related resources

_No response_


Discussion / hints:

Fix patch:
diff --git a/scapy/layers/dot11.py b/scapy/layers/dot11.py
index 8ed4d38cd0e..3e0dced1d58 100644
--- a/scapy/layers/dot11.py
+++ b/scapy/layers/dot11.py
@@ -1450,6 +1450,7 @@ class Dot11EltMicrosoftWPA(Dot11EltVendorSpecific):
 
 class Dot11EltCSA(Dot11Elt):
     name = "802.11 CSA Element"
+    match_subclass = True
     fields_desc = [
         ByteEnumField("ID", 37, _dot11_id_enum),
         ByteField("len", 3),
@@ -1463,6 +1464,7 @@ class Dot11EltCSA(Dot11Elt):
 
 class Dot11EltOBSS(Dot11Elt):
     name = "802.11 OBSS Scan Parameters Element"
+    match_subclass = True
     fields_desc = [
         ByteEnumField("ID", 74, _dot11_id_enum),
         ByteField("len", 14),
@@ -1492,6 +1494,7 @@ def extract_padding(self, s):
 
 class Dot11EltVHTOperation(Dot11Elt):
     name = "802.11 VHT Operation Element"
+    match_subclass = True
     fields_desc = [
         ByteEnumField("ID", 192, _dot11_id_enum),
         ByteField("len", 5),


Test patch:
diff --git a/test/scapy/layers/dot11.uts b/test/scapy/layers/dot11.uts
index 944df86d370..06a45e01a29 100644
--- a/test/scapy/layers/dot11.uts
+++ b/test/scapy/layers/dot11.uts
@@ -763,3 +763,18 @@ assert pkt[Dot11EltVHTOperation].VHT_Operation_Info
 assert pkt[Dot11EltVHTOperation].VHT_Operation_Info.channel_width == 1
 assert pkt[Dot11EltVHTOperation].VHT_Operation_Info.channel_center0 == 42
 assert pkt[Dot11EltVHTOperation].VHT_Operation_Info.channel_center1 == 50
+
+= Dot11EltVHTOperation in isolation
+
+pkt = Dot11EltVHTOperation(b'\xc0\x05\x01*2\x00\x00')
+assert pkt[Dot11Elt::{"ID": 192}].len == 5
+
+= Dot11EltOBSS in isolation
+
+pkt = Dot11EltOBSS(b'J\x0e\x14\x00\n\x00,\x01\xc8\x00\x14\x00\x05\x00\x19\x00')
+assert pkt[Dot11Elt::{"ID": 74}].len == 14
+
+= Dot11EltCSA in isolation
+
+pkt = Dot11EltCSA(b'%\x03\x01\x0b\x05')
+assert pkt[Dot11Elt::{"ID": 37}].len == 3