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
178 changes: 178 additions & 0 deletions scapy/layers/dot11.py
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,8 @@ def network_stats(self):
33: "Power Capability",
36: "Supported Channels",
37: "Channel Switch Announcement",
38: "Measurement Request",
39: "Measurement Report",
42: "ERP",
45: "HT Capabilities",
46: "QoS Capability",
Expand Down Expand Up @@ -2041,6 +2043,179 @@ class Dot11CSA(Packet):
]


# 802.11-2020 9.4.2.20, 9.4.2.21
_dot11_measurement_type = {
5: "Beacon",
}


# 802.11-2020 9.6.6.1

class Dot11RadioMeasurement(Packet):
name = "802.11 Radio Measurement Action"
fields_desc = [
ByteEnumField("action", 0x00, {
0x00: "Radio Measurement Request",
0x01: "Radio Measurement Report",
0x02: "Link Measurement Request",
0x03: "Link Measurement Report",
0x04: "Neighbor Report Request",
0x05: "Neighbor Report Response",
})
]


# 802.11-2020 9.4.2.20.7

class Dot11RadioMeasurementBeaconRequest(Packet):
name = "802.11 Radio Measurement Beacon Request"
fields_desc = [
ByteField("operating_class", 0),
ByteField("channel_number", 0),
LEShortField("randomization_interval", 0),
LEShortField("measurement_duration", 0),
ByteField("measurement_mode", 0),
MACField("BSSID", ETHER_ANY),
]

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


# 802.11-2020 9.6.6.2, 9.4.2.20

class Dot11RadioMeasurementRequest(Packet):
name = "802.11 Radio Measurement Request"
fields_desc = [
ByteField("token", 0),
LEShortField("repetitions", 0),
ByteEnumField("ID", 38, _dot11_id_enum),
ByteField("len", 16),
ByteField("measurement_token", 0),
# Measurement Request Mode
BitField("reserved", 0, 3),
BitField("duration_mandatory", 0, 1),
BitField("report", 0, 1),
BitField("request", 0, 1),
BitField("enable", 0, 1),
BitField("parallel", 0, 1),
ByteEnumField("measurement_request_type", 5,
_dot11_measurement_type),
# Only Beacon requests are specialized for now. Other measurement
# request types stay raw instead of being mis-dissected.
ConditionalField(
PacketField(
"measurement_request",
Dot11RadioMeasurementBeaconRequest(),
Dot11RadioMeasurementBeaconRequest
),
lambda p: p.measurement_request_type == 5
),
ConditionalField(
StrLenField(
"measurement_request_data",
b"",
length_from=lambda p: max(p.len - 3, 0)
),
lambda p: p.measurement_request_type != 5
),
ConditionalField(
PacketListField(
"subelements",
[],
SubelemTLV,
length_from=lambda p: max(p.len - 16, 0)
),
lambda p: p.measurement_request_type == 5 and p.len > 16
)
]


# 802.11-2020 9.4.2.21.7

class Dot11RadioMeasurementBeaconReport(Packet):
name = "802.11 Radio Measurement Beacon Report"
fields_desc = [
ByteField("operating_class", 0),
ByteField("channel_number", 0),
LELongField("measurement_start_time", 0),
LEShortField("measurement_duration", 0),
# Reported Frame Information
BitField("reported_frame_type", 0, 1),
BitField("condensed_phy", 0, 7),
ByteField("RCPI", 0),
ByteField("RSNI", 0),
MACField("BSSID", ETHER_ANY),
ByteField("antenna_id", 0),
LEIntField("parent_tsf", 0),
]

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


# 802.11-2020 9.6.6.3, 9.4.2.21

class Dot11RadioMeasurementReport(Packet):
name = "802.11 Radio Measurement Report"
fields_desc = [
ByteField("token", 0),
ByteEnumField("ID", 39, _dot11_id_enum),
ByteField("len", 29),
ByteField("measurement_token", 0),
# Measurement Report Mode
BitField("reserved", 0, 5),
BitField("refused", 0, 1),
BitField("incapable", 0, 1),
BitField("late", 0, 1),
ByteEnumField("measurement_report_type", 5,
_dot11_measurement_type),
# Only Beacon reports are specialized for now. Other successful
# measurement report types stay raw.
ConditionalField(
PacketField(
"measurement_report",
Dot11RadioMeasurementBeaconReport(),
Dot11RadioMeasurementBeaconReport
),
lambda p: (
p.measurement_report_type == 5 and
p.late == 0 and
p.incapable == 0 and
p.refused == 0
)
),
ConditionalField(
StrLenField(
"measurement_report_data",
b"",
length_from=lambda p: max(p.len - 3, 0)
),
lambda p: (
p.measurement_report_type != 5 and
p.late == 0 and
p.incapable == 0 and
p.refused == 0
)
),
ConditionalField(
PacketListField(
"subelements",
[],
SubelemTLV,
length_from=lambda p: max(p.len - 29, 0)
),
lambda p: (
p.measurement_report_type == 5 and
p.late == 0 and
p.incapable == 0 and
p.refused == 0 and
p.len > 29
)
)
]


class Dot11S1GBeacon(_Dot11EltUtils):
name = "802.11 S1G Beacon"
fields_desc = [LEIntField("timestamp", 0),
Expand Down Expand Up @@ -2220,6 +2395,9 @@ class Dot11CCMP(Dot11Encrypted):
bind_layers(Dot11Action, Dot11WNM, category=0x0A)
bind_layers(Dot11WNM, Dot11BSSTMRequest, action=7)
bind_layers(Dot11WNM, Dot11BSSTMResponse, action=8)
bind_layers(Dot11Action, Dot11RadioMeasurement, category=0x05)
bind_layers(Dot11RadioMeasurement, Dot11RadioMeasurementRequest, action=0)
bind_layers(Dot11RadioMeasurement, Dot11RadioMeasurementReport, action=1)


conf.l2types.register(DLT_IEEE802_11, Dot11)
Expand Down
84 changes: 84 additions & 0 deletions test/scapy/layers/dot11.uts
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,90 @@ assert pkt[Dot11Action][Dot11WNM][Dot11BSSTMResponse].neighbor_report[0].op_clas
assert pkt[Dot11Action][Dot11WNM][Dot11BSSTMResponse].neighbor_report[0].channel == 12
assert pkt[Dot11Action][Dot11WNM][Dot11BSSTMResponse].neighbor_report[0].phy_type == 0

= Dot11RadioMeasurementRequest - Beacon dissection

data = b"\x05\x00\x7a\x01\x00\x26\x10\x11\x17\x05\x51\x06\x34\x12\x50\x00\x01\x01\x02\x03\x04\x05\x06"
pkt = Dot11Action(data)
assert Dot11RadioMeasurement in pkt
assert Dot11RadioMeasurementRequest in pkt
assert Dot11RadioMeasurementBeaconRequest in pkt

req = pkt[Dot11RadioMeasurementRequest]
assert pkt[Dot11Action].category == 5
assert pkt[Dot11Action][Dot11RadioMeasurement].action == 0
assert req.token == 0x7a
assert req.repetitions == 1
assert req.ID == 38
assert req.len == 16
assert req.measurement_token == 0x11
assert req.reserved == 0
assert req.duration_mandatory == 1
assert req.report == 0
assert req.request == 1
assert req.enable == 1
assert req.parallel == 1
assert req.measurement_request_type == 5

beacon_req = req[Dot11RadioMeasurementBeaconRequest]
assert beacon_req.operating_class == 81
assert beacon_req.channel_number == 6
assert beacon_req.randomization_interval == 0x1234
assert beacon_req.measurement_duration == 80
assert beacon_req.measurement_mode == 1
assert beacon_req.BSSID == "01:02:03:04:05:06"
assert raw(pkt) == data

= Dot11RadioMeasurementReport - Beacon dissection

data = b"\x05\x01\x7a\x27\x1d\x11\x00\x05\x51\x06\x08\x07\x06\x05\x04\x03\x02\x01\x50\x00\xaa\x37\x16\x01\x02\x03\x04\x05\x06\x03\x44\x33\x22\x11"
pkt = Dot11Action(data)
assert Dot11RadioMeasurement in pkt
assert Dot11RadioMeasurementReport in pkt
assert Dot11RadioMeasurementBeaconReport in pkt

report = pkt[Dot11RadioMeasurementReport]
assert pkt[Dot11Action].category == 5
assert pkt[Dot11Action][Dot11RadioMeasurement].action == 1
assert report.token == 0x7a
assert report.ID == 39
assert report.len == 29
assert report.measurement_token == 0x11
assert report.reserved == 0
assert report.refused == 0
assert report.incapable == 0
assert report.late == 0
assert report.measurement_report_type == 5

beacon_report = report[Dot11RadioMeasurementBeaconReport]
assert beacon_report.operating_class == 81
assert beacon_report.channel_number == 6
assert beacon_report.measurement_start_time == 0x0102030405060708
assert beacon_report.measurement_duration == 80
assert beacon_report.reported_frame_type == 1
assert beacon_report.condensed_phy == 42
assert beacon_report.RCPI == 55
assert beacon_report.RSNI == 22
assert beacon_report.BSSID == "01:02:03:04:05:06"
assert beacon_report.antenna_id == 3
assert beacon_report.parent_tsf == 0x11223344
assert raw(pkt) == data

= Dot11RadioMeasurementRequest - unsupported measurement type

data = b"\x05\x00\x01\x00\x00\x26\x05\x02\x00\x07\xaa\xbb"
pkt = Dot11Action(data)
assert Dot11RadioMeasurementRequest in pkt

req = pkt[Dot11RadioMeasurementRequest]
assert req.token == 1
assert req.repetitions == 0
assert req.ID == 38
assert req.len == 5
assert req.measurement_token == 2
assert req.measurement_request_type == 7
assert req.measurement_request_data == b"\xaa\xbb"
assert raw(pkt) == data

= Dot11Ack

pkt = Dot11(bytes(Dot11()/Dot11Ack()))
Expand Down
Loading