Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/layers/dot11.py: 73%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Philippe Biondi <phil@secdev.org>
6"""
7Wireless LAN according to IEEE 802.11.
9This file contains bindings for 802.11 layers and some usual linklayers:
10 - PRISM
11 - RadioTap
12"""
14import re
15import struct
16from zlib import crc32
18from scapy.config import conf, crypto_validator
19from scapy.data import ETHER_ANY, DLT_IEEE802_11, DLT_PRISM_HEADER, \
20 DLT_IEEE802_11_RADIO
21from scapy.compat import raw, plain_str, orb, chb
22from scapy.packet import Packet, bind_layers, bind_top_down, NoPayload
23from scapy.fields import (
24 BitEnumField,
25 BitField,
26 BitMultiEnumField,
27 ByteEnumField,
28 ByteField,
29 ConditionalField,
30 FCSField,
31 FieldLenField,
32 FieldListField,
33 FlagsField,
34 IntField,
35 LEFieldLenField,
36 LEIntField,
37 LELongField,
38 LEShortEnumField,
39 LEShortField,
40 LESignedIntField,
41 MayEnd,
42 MultipleTypeField,
43 OUIField,
44 PacketField,
45 PacketListField,
46 ReversePadField,
47 ScalingField,
48 ShortField,
49 StrField,
50 StrFixedLenField,
51 StrLenField,
52 XByteField,
53 XStrFixedLenField,
54)
55from scapy.ansmachine import AnsweringMachine
56from scapy.plist import PacketList
57from scapy.layers.l2 import Ether, LLC, MACField
58from scapy.layers.inet import IP, TCP
59from scapy.error import warning, log_loading
60from scapy.sendrecv import sniff, sendp
63if conf.crypto_valid:
64 from cryptography.hazmat.backends import default_backend
65 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
67 try:
68 # cryptography > 43.0
69 from cryptography.hazmat.decrepit.ciphers import (
70 algorithms as decrepit_algorithms,
71 )
72 except ImportError:
73 decrepit_algorithms = algorithms
74else:
75 default_backend = Ciphers = algorithms = decrepit_algorithms = None
76 log_loading.info("Can't import python-cryptography v2.0+. Disabled WEP decryption/encryption. (Dot11)") # noqa: E501
79#########
80# Prism #
81#########
83# http://www.martin.cc/linux/prism
85class PrismHeader(Packet):
86 """ iwpriv wlan0 monitor 3 """
87 name = "Prism header"
88 fields_desc = [LEIntField("msgcode", 68),
89 LEIntField("len", 144),
90 StrFixedLenField("dev", "", 16),
91 LEIntField("hosttime_did", 0),
92 LEShortField("hosttime_status", 0),
93 LEShortField("hosttime_len", 0),
94 LEIntField("hosttime", 0),
95 LEIntField("mactime_did", 0),
96 LEShortField("mactime_status", 0),
97 LEShortField("mactime_len", 0),
98 LEIntField("mactime", 0),
99 LEIntField("channel_did", 0),
100 LEShortField("channel_status", 0),
101 LEShortField("channel_len", 0),
102 LEIntField("channel", 0),
103 LEIntField("rssi_did", 0),
104 LEShortField("rssi_status", 0),
105 LEShortField("rssi_len", 0),
106 LEIntField("rssi", 0),
107 LEIntField("sq_did", 0),
108 LEShortField("sq_status", 0),
109 LEShortField("sq_len", 0),
110 LEIntField("sq", 0),
111 LEIntField("signal_did", 0),
112 LEShortField("signal_status", 0),
113 LEShortField("signal_len", 0),
114 LESignedIntField("signal", 0),
115 LEIntField("noise_did", 0),
116 LEShortField("noise_status", 0),
117 LEShortField("noise_len", 0),
118 LEIntField("noise", 0),
119 LEIntField("rate_did", 0),
120 LEShortField("rate_status", 0),
121 LEShortField("rate_len", 0),
122 LEIntField("rate", 0),
123 LEIntField("istx_did", 0),
124 LEShortField("istx_status", 0),
125 LEShortField("istx_len", 0),
126 LEIntField("istx", 0),
127 LEIntField("frmlen_did", 0),
128 LEShortField("frmlen_status", 0),
129 LEShortField("frmlen_len", 0),
130 LEIntField("frmlen", 0),
131 ]
133 def answers(self, other):
134 if isinstance(other, PrismHeader):
135 return self.payload.answers(other.payload)
136 else:
137 return self.payload.answers(other)
139############
140# RadioTap #
141############
143# https://www.radiotap.org/
145# Note: Radiotap alignment is crazy. See the doc:
146# https://www.radiotap.org/#alignment-in-radiotap
148# RadioTap constants
151_rt_present = ['TSFT', 'Flags', 'Rate', 'Channel', 'FHSS', 'dBm_AntSignal',
152 'dBm_AntNoise', 'Lock_Quality', 'TX_Attenuation',
153 'dB_TX_Attenuation', 'dBm_TX_Power', 'Antenna',
154 'dB_AntSignal', 'dB_AntNoise', 'RXFlags', 'TXFlags',
155 'b17', 'b18', 'ChannelPlus', 'MCS', 'A_MPDU',
156 'VHT', 'timestamp', 'HE', 'HE_MU', 'HE_MU_other_user',
157 'zero_length_psdu', 'L_SIG', 'TLV',
158 'RadiotapNS', 'VendorNS', 'Ext']
160# Note: Inconsistencies with wireshark
161# Wireshark ignores the suggested fields, whereas we implement some of them
162# (some are well-used even though not accepted)
163# However, flags that conflicts with Wireshark are not and MUST NOT be
164# implemented -> b17, b18
166_rt_flags = ['CFP', 'ShortPreamble', 'wep', 'fragment', 'FCS', 'pad',
167 'badFCS', 'ShortGI']
169_rt_channelflags = ['res1', 'res2', 'res3', 'res4', 'Turbo', 'CCK',
170 'OFDM', '2GHz', '5GHz', 'Passive', 'Dynamic_CCK_OFDM',
171 'GFSK', 'GSM', 'StaticTurbo', '10MHz', '5MHz']
173_rt_rxflags = ["res1", "BAD_PLCP", "res2"]
175_rt_txflags = ["TX_FAIL", "CTS", "RTS", "NOACK", "NOSEQ", "ORDER"]
177_rt_channelflags2 = ['res1', 'res2', 'res3', 'res4', 'Turbo', 'CCK',
178 'OFDM', '2GHz', '5GHz', 'Passive', 'Dynamic_CCK_OFDM',
179 'GFSK', 'GSM', 'StaticTurbo', '10MHz', '5MHz',
180 '20MHz', '40MHz_ext_channel_above',
181 '40MHz_ext_channel_below',
182 'res5', 'res6', 'res7', 'res8', 'res9']
184_rt_tsflags = ['32-bit_counter', 'Accuracy', 'res1', 'res2', 'res3',
185 'res4', 'res5', 'res6']
187_rt_knownmcs = ['MCS_bandwidth', 'MCS_index', 'guard_interval', 'HT_format',
188 'FEC_type', 'STBC_streams', 'Ness', 'Ness_MSB']
190_rt_bandwidth = {0: "20MHz", 1: "40MHz", 2: "ht40Mhz-", 3: "ht40MHz+"}
192_rt_a_mpdu_flags = ['Report0Subframe', 'Is0Subframe', 'KnownLastSubframe',
193 'LastSubframe', 'CRCerror', 'EOFsubframe', 'KnownEOF',
194 'res1', 'res2', 'res3', 'res4', 'res5', 'res6', 'res7',
195 'res8']
197_rt_vhtbandwidth = {
198 0: "20MHz", 1: "40MHz", 2: "40MHz", 3: "40MHz", 4: "80MHz", 5: "80MHz",
199 6: "80MHz", 7: "80MHz", 8: "80MHz", 9: "80MHz", 10: "80MHz", 11: "160MHz",
200 12: "160MHz", 13: "160MHz", 14: "160MHz", 15: "160MHz", 16: "160MHz",
201 17: "160MHz", 18: "160MHz", 19: "160MHz", 20: "160MHz", 21: "160MHz",
202 22: "160MHz", 23: "160MHz", 24: "160MHz", 25: "160MHz"
203}
205_rt_knownvht = ['STBC', 'TXOP_PS_NOT_ALLOWED', 'GuardInterval', 'SGINsysmDis',
206 'LDPCextraOFDM', 'Beamformed', 'Bandwidth', 'GroupID',
207 'PartialAID',
208 'res1', 'res2', 'res3', 'res4', 'res5', 'res6', 'res7']
210_rt_presentvht = ['STBC', 'TXOP_PS_NOT_ALLOWED', 'GuardInterval',
211 'SGINsysmDis', 'LDPCextraOFDM', 'Beamformed',
212 'res1', 'res2']
214_rt_hemuother_per_user_known = [
215 'user field position',
216 'STA-ID',
217 'NSTS',
218 'Tx Beamforming',
219 'Spatial Configuration',
220 'MCS',
221 'DCM',
222 'Coding',
223]
226# Radiotap utils
228# Note: extended presence masks are dissected pretty dumbly by
229# Wireshark.
231def _next_radiotap_extpm(pkt, lst, cur, s):
232 """Generates the next RadioTapExtendedPresenceMask"""
233 if cur is None or (cur.present and cur.present.Ext):
234 st = len(lst) + (cur is not None)
235 return lambda *args: RadioTapExtendedPresenceMask(*args, index=st)
236 return None
239class RadioTapExtendedPresenceMask(Packet):
240 """RadioTapExtendedPresenceMask should be instantiated by passing an
241 `index=` kwarg, stating which place the item has in the list.
243 Passing index will update the b[x] fields accordingly to the index.
244 e.g.
245 >>> a = RadioTapExtendedPresenceMask(present="b0+b12+b29+Ext")
246 >>> b = RadioTapExtendedPresenceMask(index=1, present="b33+b45+b59+b62")
247 >>> pkt = RadioTap(present="Ext", Ext=[a, b])
248 """
249 name = "RadioTap Extended presence mask"
250 fields_desc = [FlagsField('present', None, -32,
251 ["b%s" % i for i in range(0, 31)] + ["Ext"])]
253 def __init__(self, _pkt=None, index=0, **kwargs):
254 self._restart_indentation(index)
255 Packet.__init__(self, _pkt, **kwargs)
257 def _restart_indentation(self, index):
258 st = index * 32
259 self.fields_desc[0].names = ["b%s" % (i + st) for i in range(0, 31)] + ["Ext"] # noqa: E501
261 def guess_payload_class(self, pay):
262 return conf.padding_layer
265# This is still unimplemented in Wireshark
266# https://www.radiotap.org/fields/TLV.html
267class RadioTapTLV(Packet):
268 fields_desc = [
269 LEShortEnumField("type", 0, _rt_present),
270 LEShortField("length", None),
271 ConditionalField(
272 OUIField("oui", 0),
273 lambda pkt: pkt.type == 30 # VendorNS
274 ),
275 ConditionalField(
276 ByteField("subtype", 0),
277 lambda pkt: pkt.type == 30
278 ),
279 ConditionalField(
280 LEShortField("presence_type", 0),
281 lambda pkt: pkt.type == 30
282 ),
283 ConditionalField(
284 LEShortField("reserved", 0),
285 lambda pkt: pkt.type == 30
286 ),
287 StrLenField("data", b"",
288 length_from=lambda pkt: pkt.length),
289 StrLenField("pad", None, length_from=lambda pkt: -pkt.length % 4)
290 ]
292 def post_build(self, pkt, pay):
293 if self.length is None:
294 pkt = pkt[:2] + struct.pack("<H", len(self.data)) + pkt[4:]
295 if self.pad is None:
296 pkt += b"\x00" * (-len(self.data) % 4)
297 return pkt + pay
299 def extract_padding(self, s):
300 return "", s
303# RADIOTAP
305class RadioTap(Packet):
306 name = "RadioTap"
307 deprecated_fields = {
308 "Channel": ("ChannelFrequency", "2.4.3"),
309 "ChannelFlags2": ("ChannelPlusFlags", "2.4.3"),
310 "ChannelNumber": ("ChannelPlusNumber", "2.4.3"),
311 }
312 fields_desc = [
313 ByteField('version', 0),
314 ByteField('pad', 0),
315 LEShortField('len', None),
316 FlagsField('present', None, -32, _rt_present), # noqa: E501
317 # Extended presence mask
318 ConditionalField(PacketListField("Ext", [], next_cls_cb=_next_radiotap_extpm), lambda pkt: pkt.present and pkt.present.Ext), # noqa: E501
319 # RadioTap fields - each starts with a ReversePadField
320 # to handle padding
322 # TSFT
323 ConditionalField(
324 ReversePadField(
325 LELongField("mac_timestamp", 0),
326 8
327 ),
328 lambda pkt: pkt.present and pkt.present.TSFT),
329 # Flags
330 ConditionalField(
331 FlagsField("Flags", None, -8, _rt_flags),
332 lambda pkt: pkt.present and pkt.present.Flags),
333 # Rate
334 ConditionalField(
335 ScalingField("Rate", 0, scaling=0.5,
336 unit="Mbps", fmt="B"),
337 lambda pkt: pkt.present and pkt.present.Rate),
338 # Channel
339 ConditionalField(
340 ReversePadField(
341 LEShortField("ChannelFrequency", 0),
342 2
343 ),
344 lambda pkt: pkt.present and pkt.present.Channel),
345 ConditionalField(
346 FlagsField("ChannelFlags", None, -16, _rt_channelflags),
347 lambda pkt: pkt.present and pkt.present.Channel),
348 # dBm_AntSignal
349 ConditionalField(
350 ScalingField("dBm_AntSignal", 0,
351 unit="dBm", fmt="b"),
352 lambda pkt: pkt.present and pkt.present.dBm_AntSignal),
353 # dBm_AntNoise
354 ConditionalField(
355 ScalingField("dBm_AntNoise", 0,
356 unit="dBm", fmt="b"),
357 lambda pkt: pkt.present and pkt.present.dBm_AntNoise),
358 # Lock_Quality
359 ConditionalField(
360 ReversePadField(
361 LEShortField("Lock_Quality", 0),
362 2
363 ),
364 lambda pkt: pkt.present and pkt.present.Lock_Quality),
365 # Antenna
366 ConditionalField(
367 ByteField("Antenna", 0),
368 lambda pkt: pkt.present and pkt.present.Antenna),
369 # RX Flags
370 ConditionalField(
371 ReversePadField(
372 FlagsField("RXFlags", None, -16, _rt_rxflags),
373 2
374 ),
375 lambda pkt: pkt.present and pkt.present.RXFlags),
376 # TX Flags
377 ConditionalField(
378 ReversePadField(
379 FlagsField("TXFlags", None, -16, _rt_txflags),
380 2
381 ),
382 lambda pkt: pkt.present and pkt.present.TXFlags),
383 # ChannelPlus
384 ConditionalField(
385 ReversePadField(
386 FlagsField("ChannelPlusFlags", None, -32, _rt_channelflags2),
387 4
388 ),
389 lambda pkt: pkt.present and pkt.present.ChannelPlus),
390 ConditionalField(
391 LEShortField("ChannelPlusFrequency", 0),
392 lambda pkt: pkt.present and pkt.present.ChannelPlus),
393 ConditionalField(
394 ByteField("ChannelPlusNumber", 0),
395 lambda pkt: pkt.present and pkt.present.ChannelPlus),
396 # MCS
397 ConditionalField(
398 ReversePadField(
399 FlagsField("knownMCS", None, -8, _rt_knownmcs),
400 2
401 ),
402 lambda pkt: pkt.present and pkt.present.MCS),
403 ConditionalField(
404 BitField("Ness_LSB", 0, 1),
405 lambda pkt: pkt.present and pkt.present.MCS),
406 ConditionalField(
407 BitField("STBC_streams", 0, 2),
408 lambda pkt: pkt.present and pkt.present.MCS),
409 ConditionalField(
410 BitEnumField("FEC_type", 0, 1, {0: "BCC", 1: "LDPC"}),
411 lambda pkt: pkt.present and pkt.present.MCS),
412 ConditionalField(
413 BitEnumField("HT_format", 0, 1, {0: "mixed", 1: "greenfield"}),
414 lambda pkt: pkt.present and pkt.present.MCS),
415 ConditionalField(
416 BitEnumField("guard_interval", 0, 1, {0: "Long_GI", 1: "Short_GI"}), # noqa: E501
417 lambda pkt: pkt.present and pkt.present.MCS),
418 ConditionalField(
419 BitEnumField("MCS_bandwidth", 0, 2, _rt_bandwidth),
420 lambda pkt: pkt.present and pkt.present.MCS),
421 ConditionalField(
422 ByteField("MCS_index", 0),
423 lambda pkt: pkt.present and pkt.present.MCS),
424 # A_MPDU
425 ConditionalField(
426 ReversePadField(
427 LEIntField("A_MPDU_ref", 0),
428 4
429 ),
430 lambda pkt: pkt.present and pkt.present.A_MPDU),
431 ConditionalField(
432 FlagsField("A_MPDU_flags", None, -32, _rt_a_mpdu_flags),
433 lambda pkt: pkt.present and pkt.present.A_MPDU),
434 # VHT
435 ConditionalField(
436 ReversePadField(
437 FlagsField("KnownVHT", None, -16, _rt_knownvht),
438 2
439 ),
440 lambda pkt: pkt.present and pkt.present.VHT),
441 ConditionalField(
442 FlagsField("PresentVHT", None, -8, _rt_presentvht),
443 lambda pkt: pkt.present and pkt.present.VHT),
444 ConditionalField(
445 ByteEnumField("VHT_bandwidth", 0, _rt_vhtbandwidth),
446 lambda pkt: pkt.present and pkt.present.VHT),
447 ConditionalField(
448 StrFixedLenField("mcs_nss", 0, length=5),
449 lambda pkt: pkt.present and pkt.present.VHT),
450 ConditionalField(
451 ByteField("GroupID", 0),
452 lambda pkt: pkt.present and pkt.present.VHT),
453 ConditionalField(
454 ShortField("PartialAID", 0),
455 lambda pkt: pkt.present and pkt.present.VHT),
456 # timestamp
457 ConditionalField(
458 ReversePadField(
459 LELongField("timestamp", 0),
460 8
461 ),
462 lambda pkt: pkt.present and pkt.present.timestamp),
463 ConditionalField(
464 LEShortField("ts_accuracy", 0),
465 lambda pkt: pkt.present and pkt.present.timestamp),
466 ConditionalField(
467 BitEnumField("ts_unit", 0, 4, {
468 0: 'milliseconds',
469 1: 'microseconds',
470 2: 'nanoseconds'}),
471 lambda pkt: pkt.present and pkt.present.timestamp),
472 ConditionalField(
473 BitField("ts_position", 0, 4),
474 lambda pkt: pkt.present and pkt.present.timestamp),
475 ConditionalField(
476 FlagsField("ts_flags", None, 8, _rt_tsflags),
477 lambda pkt: pkt.present and pkt.present.timestamp),
478 # HE - XXX not complete
479 ConditionalField(
480 ReversePadField(
481 LEShortField("he_data1", 0),
482 2
483 ),
484 lambda pkt: pkt.present and pkt.present.HE),
485 ConditionalField(
486 LEShortField("he_data2", 0),
487 lambda pkt: pkt.present and pkt.present.HE),
488 ConditionalField(
489 LEShortField("he_data3", 0),
490 lambda pkt: pkt.present and pkt.present.HE),
491 ConditionalField(
492 LEShortField("he_data4", 0),
493 lambda pkt: pkt.present and pkt.present.HE),
494 ConditionalField(
495 LEShortField("he_data5", 0),
496 lambda pkt: pkt.present and pkt.present.HE),
497 ConditionalField(
498 LEShortField("he_data6", 0),
499 lambda pkt: pkt.present and pkt.present.HE),
500 # HE_MU
501 ConditionalField(
502 ReversePadField(
503 LEShortField("hemu_flags1", 0),
504 2
505 ),
506 lambda pkt: pkt.present and pkt.present.HE_MU),
507 ConditionalField(
508 LEShortField("hemu_flags2", 0),
509 lambda pkt: pkt.present and pkt.present.HE_MU),
510 ConditionalField(
511 FieldListField("RU_channel1", [], ByteField('', 0),
512 length_from=lambda x: 4),
513 lambda pkt: pkt.present and pkt.present.HE_MU),
514 ConditionalField(
515 FieldListField("RU_channel2", [], ByteField('', 0),
516 length_from=lambda x: 4),
517 lambda pkt: pkt.present and pkt.present.HE_MU),
518 # HE_MU_other_user
519 ConditionalField(
520 ReversePadField(
521 LEShortField("hemuou_per_user_1", 0x7fff),
522 2
523 ),
524 lambda pkt: pkt.present and pkt.present.HE_MU_other_user),
525 ConditionalField(
526 LEShortField("hemuou_per_user_2", 0x003f),
527 lambda pkt: pkt.present and pkt.present.HE_MU_other_user),
528 ConditionalField(
529 ByteField("hemuou_per_user_position", 0),
530 lambda pkt: pkt.present and pkt.present.HE_MU_other_user),
531 ConditionalField(
532 FlagsField("hemuou_per_user_known", 0, -16,
533 _rt_hemuother_per_user_known),
534 lambda pkt: pkt.present and pkt.present.HE_MU_other_user),
535 # L_SIG
536 ConditionalField(
537 ReversePadField(
538 FlagsField("lsig_data1", 0, -16, ["rate", "length"]),
539 2
540 ),
541 lambda pkt: pkt.present and pkt.present.L_SIG),
542 ConditionalField(
543 BitField("lsig_length", 0, 12, tot_size=-2),
544 lambda pkt: pkt.present and pkt.present.L_SIG),
545 ConditionalField(
546 BitField("lsig_rate", 0, 4, end_tot_size=-2),
547 lambda pkt: pkt.present and pkt.present.L_SIG),
548 # TLV fields
549 ConditionalField(
550 ReversePadField(
551 PacketListField("tlvs", [], RadioTapTLV),
552 4
553 ),
554 lambda pkt: pkt.present and pkt.present.TLV,
555 ),
556 # Remaining
557 StrLenField('notdecoded', "", length_from=lambda pkt: 0)
558 ]
560 def guess_payload_class(self, payload):
561 if self.present and self.present.Flags and self.Flags.FCS:
562 return Dot11FCS
563 return Dot11
565 def post_dissect(self, s):
566 length = max(self.len - len(self.original) + len(s), 0)
567 self.notdecoded = s[:length]
568 return s[length:]
570 def post_build(self, p, pay):
571 if self.len is None:
572 p = p[:2] + struct.pack("!H", len(p))[::-1] + p[4:]
573 return p + pay
576##########
577# 802.11 #
578##########
580# Note:
581# 802.11-2016 includes the spec for
582# 802.11abdghijekrywnpzvus,ae,aa,ad,ac,af
584# 802.11-2016 9.2
586# 802.11-2016 9.2.4.1.3
587_dot11_subtypes = {
588 0: { # Management
589 0: "Association Request",
590 1: "Association Response",
591 2: "Reassociation Request",
592 3: "Reassociation Response",
593 4: "Probe Request",
594 5: "Probe Response",
595 6: "Timing Advertisement",
596 8: "Beacon",
597 9: "ATIM",
598 10: "Disassociation",
599 11: "Authentication",
600 12: "Deauthentication",
601 13: "Action",
602 14: "Action No Ack",
603 },
604 1: { # Control
605 2: "Trigger",
606 3: "TACK",
607 4: "Beamforming Report Poll",
608 5: "VHT/HE NDP Announcement",
609 6: "Control Frame Extension",
610 7: "Control Wrapper",
611 8: "Block Ack Request",
612 9: "Block Ack",
613 10: "PS-Poll",
614 11: "RTS",
615 12: "CTS",
616 13: "Ack",
617 14: "CF-End",
618 15: "CF-End+CF-Ack",
619 },
620 2: { # Data
621 0: "Data",
622 1: "Data+CF-Ack",
623 2: "Data+CF-Poll",
624 3: "Data+CF-Ack+CF-Poll",
625 4: "Null (no data)",
626 5: "CF-Ack (no data)",
627 6: "CF-Poll (no data)",
628 7: "CF-Ack+CF-Poll (no data)",
629 8: "QoS Data",
630 9: "QoS Data+CF-Ack",
631 10: "QoS Data+CF-Poll",
632 11: "QoS Data+CF-Ack+CF-Poll",
633 12: "QoS Null (no data)",
634 14: "QoS CF-Poll (no data)",
635 15: "QoS CF-Ack+CF-Poll (no data)"
636 },
637 3: { # Extension
638 0: "DMG Beacon",
639 1: "S1G Beacon"
640 }
641}
643_dot11_cfe = {
644 2: "Poll",
645 3: "SPR",
646 4: "Grant",
647 5: "DMG CTS",
648 6: "DMG DTS",
649 7: "Grant Ack",
650 8: "SSW",
651 9: "SSW-Feedback",
652 10: "SSW-Ack",
653}
656_dot11_addr_meaning = [
657 [ # Management: 802.11-2016 9.3.3.2
658 "RA=DA", "TA=SA", "BSSID/STA", None,
659 ],
660 [ # Control
661 "RA", "TA", None, None
662 ],
663 [ # Data: 802.11-2016 9.3.2.1: Table 9-26
664 [["RA=DA", "RA=DA"], ["RA=BSSID", "RA"]],
665 [["TA=SA", "TA=BSSID"], ["TA=SA", "TA"]],
666 [["BSSID", "SA"], ["DA", "DA"]],
667 [[None, None], ["SA", "BSSID"]],
668 ],
669 [ # Extension
670 "BSSID", None, None, None
671 ],
672]
675class _Dot11MacField(MACField):
676 """
677 A MACField that displays the address type depending on the
678 802.11 flags
679 """
680 __slots__ = ["index"]
682 def __init__(self, name, default, index):
683 self.index = index
684 super(_Dot11MacField, self).__init__(name, default)
686 def i2repr(self, pkt, val):
687 s = super(_Dot11MacField, self).i2repr(pkt, val)
688 meaning = pkt.address_meaning(self.index)
689 if meaning:
690 return "%s (%s)" % (s, meaning)
691 return s
694# 802.11-2020 9.2.4.1.1
695class Dot11(Packet):
696 name = "802.11"
697 fields_desc = [
698 BitMultiEnumField("subtype", 0, 4, _dot11_subtypes,
699 lambda pkt: pkt.type),
700 BitEnumField("type", 0, 2, ["Management", "Control", "Data",
701 "Extension"]),
702 BitField("proto", 0, 2),
703 ConditionalField(
704 BitEnumField("cfe", 0, 4, _dot11_cfe),
705 lambda pkt: (pkt.type, pkt.subtype) == (1, 6)
706 ),
707 MultipleTypeField(
708 [
709 (
710 FlagsField("FCfield", 0, 4,
711 ["pw_mgt", "MD", "protected", "order"]),
712 lambda pkt: (pkt.type, pkt.subtype) == (1, 6)
713 ),
714 (
715 FlagsField("FCfield", 0, 2,
716 ["security", "AP_PM"]),
717 lambda pkt: (pkt.type, pkt.subtype) == (3, 1)
718 )
719 ],
720 FlagsField("FCfield", 0, 8,
721 ["to_DS", "from_DS", "MF", "retry",
722 "pw_mgt", "MD", "protected", "order"])
723 ),
724 ConditionalField(
725 BitField("FCfield_bw", 0, 3),
726 lambda pkt: (pkt.type, pkt.subtype) == (3, 1)
727 ),
728 ConditionalField(
729 FlagsField("FCfield2", 0, 3,
730 ["next_tbtt", "comp_ssid", "ano"]),
731 lambda pkt: (pkt.type, pkt.subtype) == (3, 1)
732 ),
733 LEShortField("ID", 0),
734 _Dot11MacField("addr1", ETHER_ANY, 1),
735 ConditionalField(
736 _Dot11MacField("addr2", ETHER_ANY, 2),
737 lambda pkt: (pkt.type not in {1, 3} or
738 pkt.subtype in [0x4, 0x5, 0x6, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf]),
739 ),
740 ConditionalField(
741 _Dot11MacField("addr3", ETHER_ANY, 3),
742 lambda pkt: (pkt.type in [0, 2] or
743 ((pkt.type, pkt.subtype) == (1, 6) and pkt.cfe == 6)),
744 ),
745 ConditionalField(LEShortField("SC", 0), lambda pkt: pkt.type not in {1, 3}),
746 ConditionalField(
747 _Dot11MacField("addr4", ETHER_ANY, 4),
748 lambda pkt: (pkt.type == 2 and
749 pkt.FCfield & 3 == 3), # from_DS+to_DS
750 )
751 ]
753 def mysummary(self):
754 # Supports both Dot11 and Dot11FCS
755 return self.sprintf("802.11 %%%s.type%% %%%s.subtype%% %%%s.addr2%% > %%%s.addr1%%" % ((self.__class__.__name__,) * 4)) # noqa: E501
757 def guess_payload_class(self, payload):
758 if self.type == 0x02 and (
759 0x08 <= self.subtype <= 0xF and self.subtype != 0xD):
760 return Dot11QoS
761 elif hasattr(self.FCfield, "protected") and self.FCfield.protected:
762 # When a frame is handled by encryption, the Protected Frame bit
763 # (previously called WEP bit) is set to 1, and the Frame Body
764 # begins with the appropriate cryptographic header.
765 return Dot11Encrypted
766 else:
767 return Packet.guess_payload_class(self, payload)
769 def answers(self, other):
770 if isinstance(other, Dot11):
771 if self.type == 0: # management
772 if self.addr1.lower() != other.addr2.lower(): # check resp DA w/ req SA # noqa: E501
773 return 0
774 if (other.subtype, self.subtype) in [(0, 1), (2, 3), (4, 5)]:
775 return 1
776 if self.subtype == other.subtype == 11: # auth
777 return self.payload.answers(other.payload)
778 elif self.type == 1: # control
779 return 0
780 elif self.type == 2: # data
781 return self.payload.answers(other.payload)
782 elif self.type == 3: # reserved
783 return 0
784 return 0
786 def address_meaning(self, index):
787 """
788 Return the meaning of the address[index] considering the context
789 """
790 if index not in [1, 2, 3, 4]:
791 raise ValueError("Wrong index: should be [1, 2, 3, 4]")
792 index = index - 1
793 if self.type == 0: # Management
794 return _dot11_addr_meaning[0][index]
795 elif self.type == 1: # Control
796 if (self.type, self.subtype) == (1, 6) and self.cfe == 6:
797 return ["RA", "NAV-SA", "NAV-DA"][index]
798 return _dot11_addr_meaning[1][index]
799 elif self.type == 2: # Data
800 meaning = _dot11_addr_meaning[2][index][
801 self.FCfield.to_DS
802 ][self.FCfield.from_DS]
803 if meaning and index in [2, 3]: # Address 3-4
804 if isinstance(self.payload, Dot11QoS):
805 # MSDU and Short A-MSDU
806 if self.payload.A_MSDU_Present:
807 meaning = "BSSID"
808 return meaning
809 elif self.type == 3: # Extension
810 return _dot11_addr_meaning[3][index]
811 return None
813 def unwep(self, key=None, warn=1):
814 if self.FCfield & 0x40 == 0:
815 if warn:
816 warning("No WEP to remove")
817 return
818 if isinstance(self.payload.payload, NoPayload):
819 if key or conf.wepkey:
820 self.payload.decrypt(key)
821 if isinstance(self.payload.payload, NoPayload):
822 if warn:
823 warning("Dot11 can't be decrypted. Check conf.wepkey.")
824 return
825 self.FCfield &= ~0x40
826 self.payload = self.payload.payload
829class Dot11FCS(Dot11):
830 name = "802.11-FCS"
831 match_subclass = True
832 fields_desc = Dot11.fields_desc + [FCSField("fcs", None, fmt="<I")]
834 def compute_fcs(self, s):
835 return struct.pack("!I", crc32(s) & 0xffffffff)[::-1]
837 def post_build(self, p, pay):
838 p += pay
839 if self.fcs is None:
840 p = p[:-4] + self.compute_fcs(p[:-4])
841 return p
844class Dot11QoS(Packet):
845 name = "802.11 QoS"
846 fields_desc = [BitField("A_MSDU_Present", 0, 1),
847 BitField("Ack_Policy", 0, 2),
848 BitField("EOSP", 0, 1),
849 BitField("TID", 0, 4),
850 ByteField("TXOP", 0)]
852 def guess_payload_class(self, payload):
853 if isinstance(self.underlayer, Dot11):
854 if self.underlayer.FCfield.protected:
855 return Dot11Encrypted
856 return Packet.guess_payload_class(self, payload)
859capability_list = ["res8", "res9", "short-slot", "res11",
860 "res12", "DSSS-OFDM", "res14", "res15",
861 "ESS", "IBSS", "CFP", "CFP-req",
862 "privacy", "short-preamble", "PBCC", "agility"]
864reason_code = {0: "reserved", 1: "unspec", 2: "auth-expired",
865 3: "deauth-ST-leaving",
866 4: "inactivity", 5: "AP-full", 6: "class2-from-nonauth",
867 7: "class3-from-nonass", 8: "disas-ST-leaving",
868 9: "ST-not-auth"}
870status_code = {0: "success", 1: "failure", 10: "cannot-support-all-cap",
871 11: "inexist-asso", 12: "asso-denied", 13: "algo-unsupported",
872 14: "bad-seq-num", 15: "challenge-failure",
873 16: "timeout", 17: "AP-full", 18: "rate-unsupported"}
876class _Dot11EltUtils(Packet):
877 """
878 Contains utils for classes that have Dot11Elt as payloads
879 """
880 def network_stats(self):
881 """Return a dictionary containing a summary of the Dot11
882 elements fields
883 """
884 summary = {}
885 crypto = set()
886 p = self.payload
887 while isinstance(p, Dot11Elt):
888 # Avoid overriding already-set SSID values because it is not part
889 # of the standard and it protects from parsing bugs,
890 # see https://github.com/secdev/scapy/issues/2683
891 if p.ID == 0 and "ssid" not in summary:
892 summary["ssid"] = plain_str(p.info)
893 elif p.ID == 3:
894 summary["channel"] = ord(p.info)
895 elif isinstance(p, Dot11EltCountry):
896 summary["country"] = plain_str(p.country_string[:2])
897 country_descriptor_types = {
898 b"I": "Indoor",
899 b"O": "Outdoor",
900 b"X": "Non-country",
901 b"\xff": "Ignored"
902 }
903 summary["country_desc_type"] = country_descriptor_types.get(
904 p.country_string[-1:]
905 )
906 elif isinstance(p, Dot11EltRates):
907 rates = [(x & 0x7f) / 2. for x in p.rates]
908 if "rates" in summary:
909 summary["rates"].extend(rates)
910 else:
911 summary["rates"] = rates
912 elif isinstance(p, Dot11EltRSN):
913 wpa_version = "WPA2"
914 # WPA3-only:
915 # - AP shall at least enable AKM suite selector 00-0F-AC:8
916 # - AP shall not enable AKM suite selector 00-0F-AC:2 and
917 # 00-0F-AC:6
918 # - AP shall set MFPC and MFPR to 1
919 # - AP shall not enable WEP and TKIP
920 # WPA3-transition:
921 # - AP shall at least enable AKM suite selector 00-0F-AC:2
922 # and 00-0F-AC:8
923 # - AP shall set MFPC to 1 and MFPR to 0
924 if any(x.suite == 8 for x in p.akm_suites) and \
925 all(x.suite not in [2, 6] for x in p.akm_suites) and \
926 p.mfp_capable and p.mfp_required and \
927 all(x.cipher not in [1, 2, 5]
928 for x in p.pairwise_cipher_suites):
929 # WPA3 only mode
930 wpa_version = "WPA3"
931 elif any(x.suite == 8 for x in p.akm_suites) and \
932 any(x.suite == 2 for x in p.akm_suites) and \
933 p.mfp_capable and not p.mfp_required:
934 # WPA3 transition mode
935 wpa_version = "WPA3-transition"
936 # Append suite
937 if p.akm_suites:
938 auth = p.akm_suites[0].sprintf("%suite%")
939 crypto.add(wpa_version + "/%s" % auth)
940 else:
941 crypto.add(wpa_version)
942 elif p.ID == 221:
943 if isinstance(p, Dot11EltMicrosoftWPA):
944 if p.akm_suites:
945 auth = p.akm_suites[0].sprintf("%suite%")
946 crypto.add("WPA/%s" % auth)
947 else:
948 crypto.add("WPA")
949 p = p.payload
950 if not crypto and hasattr(self, "cap"):
951 if self.cap.privacy:
952 crypto.add("WEP")
953 else:
954 crypto.add("OPN")
955 if crypto:
956 summary["crypto"] = crypto
957 return summary
960#############
961# 802.11 IE #
962#############
964# 802.11-2016 - 9.4.2
966_dot11_info_elts_ids = {
967 0: "SSID",
968 1: "Supported Rates",
969 2: "FHset",
970 3: "DSSS Set",
971 4: "CF Set",
972 5: "TIM",
973 6: "IBSS Set",
974 7: "Country",
975 10: "Request",
976 11: "BSS Load",
977 12: "EDCA Set",
978 13: "TSPEC",
979 14: "TCLAS",
980 15: "Schedule",
981 16: "Challenge text",
982 32: "Power Constraint",
983 33: "Power Capability",
984 36: "Supported Channels",
985 37: "Channel Switch Announcement",
986 38: "Measurement Request",
987 39: "Measurement Report",
988 42: "ERP",
989 45: "HT Capabilities",
990 46: "QoS Capability",
991 48: "RSN",
992 50: "Extended Supported Rates",
993 52: "Neighbor Report",
994 61: "HT Operation",
995 74: "Overlapping BSS Scan Parameters",
996 107: "Interworking",
997 127: "Extended Capabilities",
998 191: "VHT Capabilities",
999 192: "VHT Operation",
1000 221: "Vendor Specific",
1001 255: "Element ID Extension"
1002}
1004# Backward compatibility
1005_dot11_elt_deprecated_names = {
1006 "Rates": 1,
1007 "DSset": 3,
1008 "CFset": 4,
1009 "IBSSset": 6,
1010 "challenge": 16,
1011 "PowerCapability": 33,
1012 "Channels": 36,
1013 "ERPinfo": 42,
1014 "HTinfo": 45,
1015 "RSNinfo": 48,
1016 "ESRates": 50,
1017 "ExtendendCapatibilities": 127,
1018 "VHTCapabilities": 191,
1019 "Vendor": 221,
1020}
1022_dot11_info_elts_ids_rev = {v: k for k, v in _dot11_info_elts_ids.items()}
1023_dot11_info_elts_ids_rev.update(_dot11_elt_deprecated_names)
1024_dot11_id_enum = (
1025 lambda x: _dot11_info_elts_ids.get(x, x),
1026 lambda x: _dot11_info_elts_ids_rev.get(x, x)
1027)
1030# 802.11-2020 9.4.2.1
1032class Dot11Elt(Packet):
1033 """
1034 A Generic 802.11 Element
1035 """
1036 __slots__ = ["info"]
1037 name = "802.11 Information Element"
1038 fields_desc = [ByteEnumField("ID", 0, _dot11_id_enum),
1039 FieldLenField("len", None, "info", "B"),
1040 StrLenField("info", "", length_from=lambda x: x.len,
1041 max_length=255)]
1042 show_indent = 0
1044 def __setattr__(self, attr, val):
1045 if attr == "info":
1046 # Will be caught by __slots__: we need an extra call
1047 try:
1048 self.setfieldval(attr, val)
1049 except AttributeError:
1050 pass
1051 super(Dot11Elt, self).__setattr__(attr, val)
1053 def mysummary(self):
1054 if self.ID == 0:
1055 ssid = plain_str(self.info)
1056 return "SSID='%s'" % ssid, [Dot11]
1057 else:
1058 return ""
1060 registered_ies = {}
1062 @classmethod
1063 def register_variant(cls, id=None):
1064 id = id or cls.ID.default
1065 if id not in cls.registered_ies:
1066 cls.registered_ies[id] = cls
1068 @classmethod
1069 def dispatch_hook(cls, _pkt=None, *args, **kargs):
1070 if _pkt:
1071 _id = ord(_pkt[:1])
1072 idcls = cls.registered_ies.get(_id, cls)
1073 if idcls.dispatch_hook != cls.dispatch_hook:
1074 # Vendor has its own dispatch_hook
1075 return idcls.dispatch_hook(_pkt=_pkt, *args, **kargs)
1076 cls = idcls
1077 return cls
1079 def pre_dissect(self, s):
1080 # Backward compatibility: add info to all elements
1081 # This allows to introduce new Dot11Elt classes without breaking
1082 # previous code
1083 if len(s) >= 3:
1084 length = orb(s[1])
1085 if length > 0 and length <= 255:
1086 self.info = s[2:2 + length]
1087 return s
1089 def post_build(self, p, pay):
1090 if self.len is None:
1091 p = p[:1] + chb(len(p) - 2) + p[2:]
1092 return p + pay
1095# 802.11-2020 9.4.2.4
1097class Dot11EltDSSSet(Dot11Elt):
1098 name = "802.11 DSSS Parameter Set"
1099 match_subclass = True
1100 fields_desc = [
1101 ByteEnumField("ID", 3, _dot11_id_enum),
1102 ByteField("len", 1),
1103 ByteField("channel", 0),
1104 ]
1107# 802.11-2020 9.4.2.11
1109class Dot11EltERP(Dot11Elt):
1110 name = "802.11 ERP"
1111 match_subclass = True
1112 fields_desc = [
1113 ByteEnumField("ID", 42, _dot11_id_enum),
1114 ByteField("len", 1),
1115 BitField("NonERP_Present", 0, 1),
1116 BitField("Use_Protection", 0, 1),
1117 BitField("Barker_Preamble_Mode", 0, 1),
1118 BitField("res", 0, 5),
1119 ]
1122# 802.11-2020 9.4.2.24.2
1124class RSNCipherSuite(Packet):
1125 name = "Cipher suite"
1126 fields_desc = [
1127 OUIField("oui", 0x000fac),
1128 ByteEnumField("cipher", 0x04, {
1129 0x00: "Use group cipher suite",
1130 0x01: "WEP-40",
1131 0x02: "TKIP",
1132 0x03: "OCB",
1133 0x04: "CCMP-128",
1134 0x05: "WEP-104",
1135 0x06: "BIP-CMAC-128",
1136 0x07: "Group addressed traffic not allowed",
1137 0x08: "GCMP-128",
1138 0x09: "GCMP-256",
1139 0x0A: "CCMP-256",
1140 0x0B: "BIP-GMAC-128",
1141 0x0C: "BIP-GMAC-256",
1142 0x0D: "BIP-CMAC-256"
1143 })
1144 ]
1146 def extract_padding(self, s):
1147 return "", s
1150# 802.11-2020 9.4.2.24.3
1152class AKMSuite(Packet):
1153 name = "AKM suite"
1154 fields_desc = [
1155 OUIField("oui", 0x000fac),
1156 ByteEnumField("suite", 0x01, {
1157 0x00: "Reserved",
1158 0x01: "802.1X",
1159 0x02: "PSK",
1160 0x03: "FT-802.1X",
1161 0x04: "FT-PSK",
1162 0x05: "WPA-SHA256",
1163 0x06: "PSK-SHA256",
1164 0x07: "TDLS",
1165 0x08: "SAE",
1166 0x09: "FT-SAE",
1167 0x0A: "AP-PEER-KEY",
1168 0x0B: "WPA-SHA256-SUITE-B",
1169 0x0C: "WPA-SHA384-SUITE-B",
1170 0x0D: "FT-802.1X-SHA384",
1171 0x0E: "FILS-SHA256",
1172 0x0F: "FILS-SHA384",
1173 0x10: "FT-FILS-SHA256",
1174 0x11: "FT-FILS-SHA384",
1175 0x12: "OWE"
1176 })
1177 ]
1179 def extract_padding(self, s):
1180 return "", s
1183# 802.11-2020 9.4.2.24.5
1185class PMKIDListPacket(Packet):
1186 name = "PMKIDs"
1187 fields_desc = [
1188 LEFieldLenField("nb_pmkids", None, count_of="pmkid_list"),
1189 FieldListField(
1190 "pmkid_list",
1191 None,
1192 XStrFixedLenField("", "", length=16),
1193 count_from=lambda pkt: pkt.nb_pmkids
1194 )
1195 ]
1197 def extract_padding(self, s):
1198 return "", s
1201# 802.11-2020 9.4.2.24.1
1203class Dot11EltRSN(Dot11Elt):
1204 name = "802.11 RSN information"
1205 match_subclass = True
1206 fields_desc = [
1207 ByteEnumField("ID", 48, _dot11_id_enum),
1208 ByteField("len", None),
1209 LEShortField("version", 1),
1210 PacketField("group_cipher_suite", RSNCipherSuite(), RSNCipherSuite),
1211 LEFieldLenField(
1212 "nb_pairwise_cipher_suites",
1213 None,
1214 count_of="pairwise_cipher_suites"
1215 ),
1216 PacketListField(
1217 "pairwise_cipher_suites",
1218 [RSNCipherSuite()],
1219 RSNCipherSuite,
1220 count_from=lambda p: p.nb_pairwise_cipher_suites
1221 ),
1222 LEFieldLenField(
1223 "nb_akm_suites",
1224 None,
1225 count_of="akm_suites"
1226 ),
1227 PacketListField(
1228 "akm_suites",
1229 [AKMSuite()],
1230 AKMSuite,
1231 count_from=lambda p: p.nb_akm_suites
1232 ),
1233 # RSN Capabilities
1234 # 802.11-2020 9.4.2.24.4
1235 BitField("mfp_capable", 1, 1),
1236 BitField("mfp_required", 1, 1),
1237 BitField("gtksa_replay_counter", 0, 2),
1238 BitField("ptksa_replay_counter", 0, 2),
1239 BitField("no_pairwise", 0, 1),
1240 BitField("pre_auth", 0, 1),
1241 BitField("reserved", 0, 1),
1242 BitField("ocvc", 0, 1),
1243 BitField("extended_key_id", 0, 1),
1244 BitField("pbac", 0, 1),
1245 BitField("spp_a_msdu_required", 0, 1),
1246 BitField("spp_a_msdu_capable", 0, 1),
1247 BitField("peer_key_enabled", 0, 1),
1248 BitField("joint_multiband_rsna", 0, 1),
1249 # Theoretically we could use mfp_capable/mfp_required to know if those
1250 # fields are present, but some implementations poorly implement it.
1251 # In practice, do as wireshark: guess using offset.
1252 ConditionalField(
1253 PacketField("pmkids", PMKIDListPacket(), PMKIDListPacket),
1254 lambda pkt: (
1255 True if pkt.len is None else
1256 pkt.len - (
1257 12 +
1258 (pkt.nb_pairwise_cipher_suites or 0) * 4 +
1259 (pkt.nb_akm_suites or 0) * 4
1260 ) >= 2
1261 )
1262 ),
1263 ConditionalField(
1264 PacketField("group_management_cipher_suite",
1265 RSNCipherSuite(cipher=0x6), RSNCipherSuite),
1266 lambda pkt: (
1267 True if pkt.len is None else
1268 pkt.len - (
1269 12 +
1270 (pkt.nb_pairwise_cipher_suites or 0) * 4 +
1271 (pkt.nb_akm_suites or 0) * 4 +
1272 (2 if pkt.pmkids else 0) +
1273 (pkt.pmkids and pkt.pmkids.nb_pmkids or 0) * 16
1274 ) >= 4
1275 )
1276 )
1277 ]
1280class Dot11EltCountryConstraintTriplet(Packet):
1281 name = "802.11 Country Constraint Triplet"
1282 fields_desc = [
1283 ByteField("first_channel_number", 1),
1284 ByteField("num_channels", 24),
1285 ByteField("mtp", 0)
1286 ]
1288 def extract_padding(self, s):
1289 return b"", s
1292class Dot11EltCountry(Dot11Elt):
1293 name = "802.11 Country"
1294 match_subclass = True
1295 fields_desc = [
1296 ByteEnumField("ID", 7, _dot11_id_enum),
1297 ByteField("len", None),
1298 StrFixedLenField("country_string", b"\0\0\0", length=3),
1299 MayEnd(PacketListField(
1300 "descriptors",
1301 [],
1302 Dot11EltCountryConstraintTriplet,
1303 length_from=lambda pkt: (
1304 pkt.len - 3 - (pkt.len % 3)
1305 )
1306 )),
1307 # When this extension is last, padding appears to be omitted
1308 ConditionalField(
1309 ByteField("pad", 0),
1310 # The length should be 3 bytes per each triplet, and 3 bytes for the
1311 # country_string field. The standard dictates that the element length
1312 # must be even, so if the result is odd, add a padding byte.
1313 # Some transmitters don't comply with the standard, so instead of assuming
1314 # the length, we test whether there is a padding byte.
1315 # Some edge cases are still not covered, for example, if the tag length
1316 # (pkt.len) is an arbitrary number.
1317 lambda pkt: ((len(pkt.descriptors) + 1) % 2) if pkt.len is None else (pkt.len % 3) # noqa: E501
1318 )
1319 ]
1322class _RateField(ByteField):
1323 def i2repr(self, pkt, val):
1324 if val is None:
1325 return ""
1326 s = str((val & 0x7f) / 2.)
1327 if val & 0x80:
1328 s += "(B)"
1329 return s + " Mbps"
1332class Dot11EltRates(Dot11Elt):
1333 name = "802.11 Rates"
1334 match_subclass = True
1335 fields_desc = [
1336 ByteEnumField("ID", 1, _dot11_id_enum),
1337 ByteField("len", None),
1338 FieldListField(
1339 "rates",
1340 [0x82],
1341 _RateField("", 0),
1342 length_from=lambda p: p.len
1343 )
1344 ]
1347Dot11EltRates.register_variant(50) # Extended rates
1350class Dot11EltHTCapabilities(Dot11Elt):
1351 name = "802.11 HT Capabilities"
1352 match_subclass = True
1353 fields_desc = [
1354 ByteEnumField("ID", 45, _dot11_id_enum),
1355 ByteField("len", None),
1356 # HT Capabilities Info: 2B
1357 BitField("L_SIG_TXOP_Protection", 0, 1, tot_size=-2),
1358 BitField("Forty_Mhz_Intolerant", 0, 1),
1359 BitField("PSMP", 0, 1),
1360 BitField("DSSS_CCK", 0, 1),
1361 BitEnumField("Max_A_MSDU", 0, 1, {0: "3839 o", 1: "7935 o"}),
1362 BitField("Delayed_BlockAck", 0, 1),
1363 BitField("Rx_STBC", 0, 2),
1364 BitField("Tx_STBC", 0, 1),
1365 BitField("Short_GI_40Mhz", 0, 1),
1366 BitField("Short_GI_20Mhz", 0, 1),
1367 BitField("Green_Field", 0, 1),
1368 BitEnumField("SM_Power_Save", 0, 2,
1369 {0: "static SM", 1: "dynamic SM", 3: "disabled"}),
1370 BitEnumField("Supported_Channel_Width", 0, 1,
1371 {0: "20Mhz", 1: "20Mhz+40Mhz"}),
1372 BitField("LDPC_Coding_Capability", 0, 1, end_tot_size=-2),
1373 # A-MPDU Parameters: 1B
1374 BitField("res1", 0, 3, tot_size=-1),
1375 BitField("Min_MPDCU_Start_Spacing", 8, 3),
1376 BitField("Max_A_MPDU_Length_Exponent", 3, 2, end_tot_size=-1),
1377 # Supported MCS set: 16B
1378 BitField("res2", 0, 27, tot_size=-16),
1379 BitField("TX_Unequal_Modulation", 0, 1),
1380 BitField("TX_Max_Spatial_Streams", 0, 2),
1381 BitField("TX_RX_MCS_Set_Not_Equal", 0, 1),
1382 BitField("TX_MCS_Set_Defined", 0, 1),
1383 BitField("res3", 0, 6),
1384 BitField("RX_Highest_Supported_Data_Rate", 0, 10),
1385 BitField("res4", 0, 3),
1386 BitField("RX_MSC_Bitmask", 0, 77, end_tot_size=-16),
1387 # HT Extended capabilities: 2B
1388 BitField("res5", 0, 4, tot_size=-2),
1389 BitField("RD_Responder", 0, 1),
1390 BitField("HTC_HT_Support", 0, 1),
1391 BitField("MCS_Feedback", 0, 2),
1392 BitField("res6", 0, 5),
1393 BitField("PCO_Transition_Time", 0, 2),
1394 BitField("PCO", 0, 1, end_tot_size=-2),
1395 # TX Beamforming Capabilities TxBF: 4B
1396 BitField("res7", 0, 3, tot_size=-4),
1397 BitField("Channel_Estimation_Capability", 0, 2),
1398 BitField("CSI_max_n_Rows_Beamformer_Supported", 0, 2),
1399 BitField("Compressed_Steering_n_Beamformer_Antennas_Supported", 0, 2),
1400 BitField("Noncompressed_Steering_n_Beamformer_Antennas_Supported",
1401 0, 2),
1402 BitField("CSI_n_Beamformer_Antennas_Supported", 0, 2),
1403 BitField("Minimal_Grouping", 0, 2),
1404 BitField("Explicit_Compressed_Beamforming_Feedback", 0, 2),
1405 BitField("Explicit_Noncompressed_Beamforming_Feedback", 0, 2),
1406 BitField("Explicit_Transmit_Beamforming_CSI_Feedback", 0, 2),
1407 BitField("Explicit_Compressed_Steering", 0, 1),
1408 BitField("Explicit_Noncompressed_Steering", 0, 1),
1409 BitField("Explicit_CSI_Transmit_Beamforming", 0, 1),
1410 BitField("Calibration", 0, 2),
1411 BitField("Implicit_Trasmit_Beamforming", 0, 1),
1412 BitField("Transmit_NDP", 0, 1),
1413 BitField("Receive_NDP", 0, 1),
1414 BitField("Transmit_Staggered_Sounding", 0, 1),
1415 BitField("Receive_Staggered_Sounding", 0, 1),
1416 BitField("Implicit_Transmit_Beamforming_Receiving", 0, 1,
1417 end_tot_size=-4),
1418 # ASEL Capabilities: 1B
1419 FlagsField("ASEL", 0, 8, [
1420 "res",
1421 "Transmit_Sounding_PPDUs",
1422 "Receive_ASEL",
1423 "Antenna_Indices_Feedback",
1424 "Explicit_CSI_Feedback",
1425 "Explicit_CSI_Feedback_Based_Transmit_ASEL",
1426 "Antenna_Selection",
1427 ])
1428 ]
1431class Dot11EltVendorSpecific(Dot11Elt):
1432 name = "802.11 Vendor Specific"
1433 match_subclass = True
1434 fields_desc = [
1435 ByteEnumField("ID", 221, _dot11_id_enum),
1436 ByteField("len", None),
1437 OUIField("oui", 0x000000),
1438 StrLenField("info", "", length_from=lambda x: x.len - 3)
1439 ]
1441 @classmethod
1442 def dispatch_hook(cls, _pkt=None, *args, **kargs):
1443 if _pkt:
1444 oui = struct.unpack("!I", b"\x00" + _pkt[2:5])[0]
1445 ouicls = cls.registered_ouis.get(oui, cls)
1446 if ouicls.dispatch_hook != cls.dispatch_hook:
1447 # Sub-classes can have their own dispatch_hook
1448 return ouicls.dispatch_hook(_pkt=_pkt, *args, **kargs)
1449 cls = ouicls
1450 return cls
1452 registered_ouis = {}
1454 @classmethod
1455 def register_variant(cls):
1456 oui = cls.oui.default
1457 if not oui:
1458 # This is Dot11EltVendorSpecific, register it in the super-class.
1459 super().register_variant()
1460 elif oui not in cls.registered_ouis:
1461 # Sub-Vendor (e.g. Dot11EltMicrosoftWPA)
1462 cls.registered_ouis[oui] = cls
1465class Dot11EltMicrosoftWPA(Dot11EltVendorSpecific):
1466 name = "802.11 Microsoft WPA"
1467 match_subclass = True
1468 ID = 221
1469 oui = 0x0050f2
1470 # It appears many WPA implementations ignore the fact
1471 # that this IE should only have a single cipher and auth suite
1472 fields_desc = Dot11EltVendorSpecific.fields_desc[:3] + [
1473 XByteField("type", 0x01)
1474 ] + Dot11EltRSN.fields_desc[2:8]
1476 @classmethod
1477 def dispatch_hook(cls, _pkt=None, *args, **kargs):
1478 if _pkt:
1479 type_ = orb(_pkt[5])
1480 if type_ == 0x01:
1481 # MS WPA IE
1482 return Dot11EltMicrosoftWPA
1483 elif type_ == 0x02:
1484 # MS WME IE TODO
1485 # return Dot11EltMicrosoftWME
1486 pass
1487 elif type_ == 0x04:
1488 # MS WPS IE TODO
1489 # return Dot11EltWPS
1490 pass
1491 return Dot11EltVendorSpecific
1492 return cls
1495# 802.11-2016 9.4.2.19
1497class Dot11EltCSA(Dot11Elt):
1498 name = "802.11 CSA Element"
1499 match_subclass = True
1500 fields_desc = [
1501 ByteEnumField("ID", 37, _dot11_id_enum),
1502 ByteField("len", 3),
1503 ByteField("mode", 0),
1504 ByteField("new_channel", 0),
1505 ByteField("channel_switch_count", 0)
1506 ]
1509# 802.11-2016 9.4.2.59
1511class Dot11EltOBSS(Dot11Elt):
1512 name = "802.11 OBSS Scan Parameters Element"
1513 match_subclass = True
1514 fields_desc = [
1515 ByteEnumField("ID", 74, _dot11_id_enum),
1516 ByteField("len", 14),
1517 LEShortField("Passive_Dwell", 0),
1518 LEShortField("Active_Dwell", 0),
1519 LEShortField("Scan_Interval", 0),
1520 LEShortField("Passive_Total_Per_Channel", 0),
1521 LEShortField("Active_Total_Per_Channel", 0),
1522 LEShortField("Delay", 0),
1523 LEShortField("Activity_Threshold", 0),
1524 ]
1527# 802.11-2016 9.4.2.159
1529class Dot11VHTOperationInfo(Packet):
1530 name = "802.11 VHT Operation Information"
1531 fields_desc = [
1532 ByteField("channel_width", 0),
1533 ByteField("channel_center0", 36),
1534 ByteField("channel_center1", 0),
1535 ]
1537 def extract_padding(self, s):
1538 return "", s
1541class Dot11EltVHTOperation(Dot11Elt):
1542 name = "802.11 VHT Operation Element"
1543 match_subclass = True
1544 fields_desc = [
1545 ByteEnumField("ID", 192, _dot11_id_enum),
1546 ByteField("len", 5),
1547 PacketField(
1548 "VHT_Operation_Info",
1549 Dot11VHTOperationInfo(),
1550 Dot11VHTOperationInfo
1551 ),
1552 FieldListField(
1553 "mcs_set",
1554 [0x00],
1555 BitField('SS', 0x00, size=2),
1556 count_from=lambda x: 8
1557 )
1558 ]
1561# 802.11ax-2021 9.4.2.1, Table 9-92
1563class Dot11EltExtension(Dot11Elt):
1564 # Element ID Extension is a registry for many IEs. Only HE Operation is
1565 # specialized here; unknown and unsupported extension IDs stay generic.
1566 name = "802.11 Element ID Extension"
1567 match_subclass = True
1568 ID = 255
1569 fields_desc = [
1570 ByteEnumField("ID", 255, _dot11_id_enum),
1571 ByteField("len", 0),
1572 ConditionalField(
1573 ByteField("ext_ID", 0),
1574 lambda pkt: pkt.len > 0
1575 ),
1576 ]
1578 @classmethod
1579 def dispatch_hook(cls, _pkt=None, *args, **kargs):
1580 if not _pkt or len(_pkt) < 3:
1581 return cls
1583 if _pkt:
1584 ext_id = orb(_pkt[2])
1585 idcls = cls.registered_ext_ids.get(ext_id)
1586 if idcls is not None:
1587 return idcls
1588 return Dot11EltExtensionGeneric
1589 return cls
1591 registered_ext_ids = {}
1593 @classmethod
1594 def register_variant(cls, ext_id=None):
1595 ext_id = ext_id or cls.ext_ID.default
1596 if not ext_id:
1597 # This is Dot11EltExtension, register it in the super-class.
1598 super().register_variant()
1599 elif ext_id not in cls.registered_ext_ids:
1600 # Register extension ID to class (e.g. Dot11EltHEOperation)
1601 cls.registered_ext_ids[ext_id] = cls
1604# 802.11ax-2021 9.4.2.1, Table 9-92
1606class Dot11EltExtensionGeneric(Dot11EltExtension):
1607 name = "802.11 Element ID Extension"
1608 match_subclass = True
1609 ID = 255
1610 fields_desc = Dot11EltExtension.fields_desc + [
1611 StrLenField("info", b"", length_from=lambda pkt: max(pkt.len - 1, 0))]
1614# 802.11ax-2021 9.4.2.249, Figure 9-788k
1616class Dot11HE6GOperationInfo(Packet):
1617 name = "802.11 HE 6 GHz Operation Information"
1618 fields_desc = [
1619 ByteField("primary_channel", 0),
1620 # Control: 1B
1621 BitField("reserved", 0, 2, tot_size=-1),
1622 BitField("regulatory_info", 0, 3),
1623 BitField("duplicate_beacon", 0, 1),
1624 BitField("channel_width", 0, 2, end_tot_size=-1),
1626 ByteField("channel_center0", 0),
1627 ByteField("channel_center1", 0),
1628 ByteField("minimum_rate", 0),
1629 ]
1631 def extract_padding(self, s):
1632 return "", s
1635# 802.11ax-2021 9.4.2.249
1637class Dot11EltHEOperation(Dot11EltExtension):
1638 name = "802.11 HE Operation"
1639 match_subclass = True
1640 ID = 255
1641 ext_ID = 36
1642 fields_desc = Dot11EltExtension.fields_desc + [
1643 # HE Operation Parameters: 3B
1644 BitField("reserved", 0, 6, tot_size=-3),
1645 BitField("six_g_op_info_present", 0, 1),
1646 BitField("er_su_disable", 0, 1),
1647 BitField("co_hosted_bss", 0, 1),
1648 BitField("vht_operation_info_present", 0, 1),
1649 BitField("txop_duration_rts_threshold", 0, 10),
1650 BitField("twt_required", 0, 1),
1651 BitField("default_pe_duration", 0, 3, end_tot_size=-3),
1653 # BSS Color Information: 1B
1654 BitField("bss_color_disabled", 0, 1, tot_size=-1),
1655 BitField("partial_bss_color", 0, 1),
1656 BitField("bss_color", 0, 6, end_tot_size=-1),
1657 # Basic HE-MCS and NSS Set: 2B
1658 FieldListField(
1659 "basic_he_mcs_and_nss_set",
1660 [0x00],
1661 BitField('SS', 0x00, size=2),
1662 count_from=lambda x: 8
1663 ),
1665 ConditionalField(
1666 PacketField(
1667 "vht_operation_info",
1668 Dot11VHTOperationInfo(),
1669 Dot11VHTOperationInfo
1670 ),
1671 lambda p: p.vht_operation_info_present == 1),
1673 ConditionalField(
1674 ByteField("max_co_hosted_bssid", 0),
1675 lambda p: p.co_hosted_bss == 1),
1677 ConditionalField(
1678 PacketField(
1679 "he_6g_operation_info",
1680 Dot11HE6GOperationInfo(),
1681 Dot11HE6GOperationInfo
1682 ),
1683 lambda p: p.six_g_op_info_present == 1)
1684 ]
1687Dot11EltHEOperation.register_variant(36)
1690# 802.11n-2009 7.3.2.57, Figure 7-95o24, Table 7-43p
1692class Dot11EltHTOperation(Dot11Elt):
1693 name = "802.11 HT Operation"
1694 match_subclass = True
1695 fields_desc = [
1696 ByteEnumField("ID", 61, _dot11_id_enum),
1697 ByteField("len", 22),
1698 ByteField("primary_channel", 0),
1700 # HT Operation Information: 1B
1701 BitField("reserved_1", 0, 4, tot_size=-1),
1702 BitField("rifs_mode", 0, 1),
1703 BitField("channel_width", 0, 1),
1704 BitField("secondary_channel_offset", 0, 2, end_tot_size=-1),
1706 # HT Operation Information: 2B
1707 BitField("reserved_3", 0, 11, tot_size=-2),
1708 BitField("obss_non_ht_stas_present", 0, 1),
1709 BitField("reserved_2", 0, 1),
1710 BitField("nongreenfield_ht_stas_present", 0, 1),
1711 BitField("ht_protection", 0, 2, end_tot_size=-2),
1713 # HT Operation Information: 2B
1714 BitField("reserved_4", 0, 10, tot_size=-2),
1715 BitField("pco_phase", 0, 1),
1716 BitField("pco_active", 0, 1),
1717 BitField("lsig_txop_protection_full_support", 0, 1),
1718 BitField("stbc_beacon", 0, 1),
1719 BitField("dual_cts_protection", 0, 1),
1720 BitField("dual_beacon", 0, 1, end_tot_size=-2),
1722 StrFixedLenField("basic_mcs_set", b"\x00" * 16, 16),
1723 ]
1726######################
1727# 802.11 Frame types #
1728######################
1730# 802.11-2016 9.3
1732class Dot11Beacon(_Dot11EltUtils):
1733 name = "802.11 Beacon"
1734 fields_desc = [LELongField("timestamp", 0),
1735 LEShortField("beacon_interval", 0x0064),
1736 FlagsField("cap", 0, 16, capability_list)]
1739class Dot11ATIM(Packet):
1740 name = "802.11 ATIM"
1743class Dot11Disas(Packet):
1744 name = "802.11 Disassociation"
1745 fields_desc = [LEShortEnumField("reason", 1, reason_code)]
1748class Dot11AssoReq(_Dot11EltUtils):
1749 name = "802.11 Association Request"
1750 fields_desc = [FlagsField("cap", 0, 16, capability_list),
1751 LEShortField("listen_interval", 0x00c8)]
1754class Dot11AssoResp(_Dot11EltUtils):
1755 name = "802.11 Association Response"
1756 fields_desc = [FlagsField("cap", 0, 16, capability_list),
1757 LEShortField("status", 0),
1758 LEShortField("AID", 0)]
1761class Dot11ReassoReq(_Dot11EltUtils):
1762 name = "802.11 Reassociation Request"
1763 fields_desc = [FlagsField("cap", 0, 16, capability_list),
1764 LEShortField("listen_interval", 0x00c8),
1765 MACField("current_AP", ETHER_ANY)]
1768class Dot11ReassoResp(Dot11AssoResp):
1769 name = "802.11 Reassociation Response"
1772class Dot11ProbeReq(_Dot11EltUtils):
1773 name = "802.11 Probe Request"
1776class Dot11ProbeResp(_Dot11EltUtils):
1777 name = "802.11 Probe Response"
1778 fields_desc = [LELongField("timestamp", 0),
1779 LEShortField("beacon_interval", 0x0064),
1780 FlagsField("cap", 0, 16, capability_list)]
1783class Dot11Auth(_Dot11EltUtils):
1784 name = "802.11 Authentication"
1785 fields_desc = [LEShortEnumField("algo", 0, ["open", "sharedkey"]),
1786 LEShortField("seqnum", 0),
1787 LEShortEnumField("status", 0, status_code)]
1789 def answers(self, other):
1790 if self.algo != other.algo:
1791 return 0
1793 if (
1794 self.seqnum == other.seqnum + 1 or
1795 (self.algo == 3 and self.seqnum == other.seqnum)
1796 ):
1797 return 1
1798 return 0
1801class Dot11Deauth(Packet):
1802 name = "802.11 Deauthentication"
1803 fields_desc = [LEShortEnumField("reason", 1, reason_code)]
1806class Dot11Ack(Packet):
1807 name = "802.11 Ack packet"
1810# 802.11-2016 9.4.1.11
1812class Dot11Action(Packet):
1813 name = "802.11 Action"
1814 fields_desc = [
1815 ByteEnumField("category", 0x00, {
1816 0x00: "Spectrum Management",
1817 0x01: "QoS",
1818 0x02: "DLS",
1819 0x03: "Block",
1820 0x04: "Public",
1821 0x05: "Radio Measurement",
1822 0x06: "Fast BSS Transition",
1823 0x07: "HT",
1824 0x08: "SA Query",
1825 0x09: "Protected Dual of Public Action",
1826 0x0A: "WNM",
1827 0x0B: "Unprotected WNM",
1828 0x0C: "TDLS",
1829 0x0D: "Mesh",
1830 0x0E: "Multihop",
1831 0x0F: "Self-protected",
1832 0x10: "DMG",
1833 0x11: "Reserved Wi-Fi Alliance",
1834 0x12: "Fast Session Transfer",
1835 0x13: "Robust AV Streaming",
1836 0x14: "Unprotected DMG",
1837 0x15: "VHT"
1838 })
1839 ]
1842# 802.11-2016 9.6.14.1
1844class Dot11WNM(Packet):
1845 name = "802.11 WNM Action"
1846 fields_desc = [
1847 ByteEnumField("action", 0x00, {
1848 0x00: "Event Request",
1849 0x01: "Event Report",
1850 0x02: "Diagnostic Request",
1851 0x03: "Diagnostic Report",
1852 0x04: "Location Configuration Request",
1853 0x05: "Location Configuration Response",
1854 0x06: "BSS Transition Management Query",
1855 0x07: "BSS Transition Management Request",
1856 0x08: "BSS Transition Management Response",
1857 0x09: "FMS Request",
1858 0x0A: "FMS Response",
1859 0x0B: "Collocated Interference Request",
1860 0x0C: "Collocated Interference Report",
1861 0x0D: "TFS Request",
1862 0x0E: "TFS Response",
1863 0x0F: "TFS Notify",
1864 0x10: "WNM Sleep Mode Request",
1865 0x11: "WNM Sleep Mode Response",
1866 0x12: "TIM Broadcast Request",
1867 0x13: "TIM Broadcast Response",
1868 0x14: "QoS Traffic Capability Update",
1869 0x15: "Channel Usage Request",
1870 0x16: "Channel Usage Response",
1871 0x17: "DMS Request",
1872 0x18: "DMS Response",
1873 0x19: "Timing Measurement Request",
1874 0x1A: "WNM Notification Request",
1875 0x1B: "WNM Notification Response",
1876 0x1C: "WNM-Notify Response"
1877 })
1878 ]
1881# 802.11-2016 9.4.2.37
1883class SubelemTLV(Packet):
1884 fields_desc = [
1885 ByteField("type", 0),
1886 LEFieldLenField("len", None, fmt="B", length_of="value"),
1887 FieldListField(
1888 "value",
1889 [],
1890 ByteField('', 0),
1891 length_from=lambda p: p.len
1892 )
1893 ]
1896class BSSTerminationDuration(Packet):
1897 name = "BSS Termination Duration"
1898 fields_desc = [
1899 ByteField("id", 4),
1900 ByteField("len", 10),
1901 LELongField("TSF", 0),
1902 LEShortField("duration", 0)
1903 ]
1905 def extract_padding(self, s):
1906 return "", s
1909class NeighborReport(Packet):
1910 name = "Neighbor Report"
1911 fields_desc = [
1912 ByteField("type", 0),
1913 ByteField("len", 13),
1914 MACField("BSSID", ETHER_ANY),
1915 # BSSID Information
1916 BitField("AP_reach", 0, 2, tot_size=-4),
1917 BitField("security", 0, 1),
1918 BitField("key_scope", 0, 1),
1919 BitField("capabilities", 0, 6),
1920 BitField("mobility", 0, 1),
1921 BitField("HT", 0, 1),
1922 BitField("VHT", 0, 1),
1923 BitField("FTM", 0, 1),
1924 BitField("reserved", 0, 18, end_tot_size=-4),
1925 # BSSID Information end
1926 ByteField("op_class", 0),
1927 ByteField("channel", 0),
1928 ByteField("phy_type", 0),
1929 ConditionalField(
1930 PacketListField(
1931 "subelems",
1932 SubelemTLV(),
1933 SubelemTLV,
1934 length_from=lambda p: p.len - 13
1935 ),
1936 lambda p: p.len > 13
1937 )
1938 ]
1941# 802.11-2016 9.6.14.9
1943btm_request_mode = [
1944 "Preferred_Candidate_List_Included",
1945 "Abridged",
1946 "Disassociation_Imminent",
1947 "BSS_Termination_Included",
1948 "ESS_Disassociation_Imminent"
1949]
1952class Dot11BSSTMRequest(Packet):
1953 name = "BSS Transition Management Request"
1954 fields_desc = [
1955 ByteField("token", 0),
1956 FlagsField("mode", 0, 8, btm_request_mode),
1957 LEShortField("disassociation_timer", 0),
1958 ByteField("validity_interval", 0),
1959 ConditionalField(
1960 PacketField(
1961 "termination_duration",
1962 BSSTerminationDuration(),
1963 BSSTerminationDuration
1964 ),
1965 lambda p: p.mode and p.mode.BSS_Termination_Included
1966 ),
1967 ConditionalField(
1968 ByteField("url_len", 0),
1969 lambda p: p.mode and p.mode.ESS_Disassociation_Imminent
1970 ),
1971 ConditionalField(
1972 StrLenField("url", "", length_from=lambda p: p.url_len),
1973 lambda p: p.mode and p.mode.ESS_Disassociation_Imminent != 0
1974 ),
1975 ConditionalField(
1976 PacketListField(
1977 "neighbor_report",
1978 NeighborReport(),
1979 NeighborReport
1980 ),
1981 lambda p: p.mode and p.mode.Preferred_Candidate_List_Included
1982 )
1983 ]
1986# 802.11-2016 9.6.14.10
1988btm_status_code = [
1989 "Accept",
1990 "Reject-Unspecified_reject_reason",
1991 "Reject-Insufficient_Beacon_or_Probe_Response_frames",
1992 "Reject-Insufficient_available_capacity_from_all_candidates",
1993 "Reject-BSS_termination_undesired",
1994 "Reject-BSS_termination_delay_requested",
1995 "Reject-STA_BSS_Transition_Candidate_List_provided",
1996 "Reject-No_suitable_BSS_transition_candidates",
1997 "Reject-Leaving_ESS"
1998]
2001class Dot11BSSTMResponse(Packet):
2002 name = "BSS Transition Management Response"
2003 fields_desc = [
2004 ByteField("token", 0),
2005 ByteEnumField("status", 0, btm_status_code),
2006 ByteField("termination_delay", 0),
2007 ConditionalField(
2008 MACField("target", ETHER_ANY),
2009 lambda p: p.status == 0
2010 ),
2011 ConditionalField(
2012 PacketListField(
2013 "neighbor_report",
2014 NeighborReport(),
2015 NeighborReport
2016 ),
2017 lambda p: p.status == 6
2018 )
2019 ]
2022# 802.11-2016 9.6.2.1
2024class Dot11SpectrumManagement(Packet):
2025 name = "802.11 Spectrum Management Action"
2026 fields_desc = [
2027 ByteEnumField("action", 0x00, {
2028 0x00: "Measurement Request",
2029 0x01: "Measurement Report",
2030 0x02: "TPC Request",
2031 0x03: "TPC Report",
2032 0x04: "Channel Switch Announcement",
2033 })
2034 ]
2037# 802.11-2016 9.6.2.6
2039class Dot11CSA(Packet):
2040 name = "Channel Switch Announcement Frame"
2041 fields_desc = [
2042 PacketField("CSA", Dot11EltCSA(), Dot11EltCSA),
2043 ]
2046# 802.11-2020 9.4.2.20, 9.4.2.21
2047_dot11_measurement_type = {
2048 5: "Beacon",
2049}
2052# 802.11-2020 9.6.6.1
2054class Dot11RadioMeasurement(Packet):
2055 name = "802.11 Radio Measurement Action"
2056 fields_desc = [
2057 ByteEnumField("action", 0x00, {
2058 0x00: "Radio Measurement Request",
2059 0x01: "Radio Measurement Report",
2060 0x02: "Link Measurement Request",
2061 0x03: "Link Measurement Report",
2062 0x04: "Neighbor Report Request",
2063 0x05: "Neighbor Report Response",
2064 })
2065 ]
2068# 802.11-2020 9.4.2.20.7
2070class Dot11RadioMeasurementBeaconRequest(Packet):
2071 name = "802.11 Radio Measurement Beacon Request"
2072 fields_desc = [
2073 ByteField("op_class", 0),
2074 ByteField("channel", 0),
2075 LEShortField("randomization_interval", 0),
2076 LEShortField("measurement_duration", 0),
2077 ByteField("measurement_mode", 0),
2078 MACField("BSSID", ETHER_ANY),
2079 ]
2081 def extract_padding(self, s):
2082 return b"", s
2085# 802.11-2020 9.6.6.2, 9.4.2.20
2087class Dot11RadioMeasurementRequest(Packet):
2088 name = "802.11 Radio Measurement Request"
2089 fields_desc = [
2090 ByteField("token", 0),
2091 LEShortField("repetitions", 0),
2092 ByteEnumField("ID", 38, _dot11_id_enum),
2093 ByteField("len", 16),
2094 ByteField("measurement_token", 0),
2095 # Measurement Request Mode
2096 BitField("reserved", 0, 3),
2097 BitField("duration_mandatory", 0, 1),
2098 BitField("report", 0, 1),
2099 BitField("request", 0, 1),
2100 BitField("enable", 0, 1),
2101 BitField("parallel", 0, 1),
2102 ByteEnumField("measurement_request_type", 5,
2103 _dot11_measurement_type),
2104 # Only Beacon requests are specialized for now. Other measurement
2105 # request types stay raw instead of being mis-dissected.
2106 ConditionalField(
2107 PacketField(
2108 "measurement_request",
2109 Dot11RadioMeasurementBeaconRequest(),
2110 Dot11RadioMeasurementBeaconRequest
2111 ),
2112 lambda p: p.measurement_request_type == 5
2113 ),
2114 ConditionalField(
2115 StrLenField(
2116 "measurement_request_data",
2117 b"",
2118 length_from=lambda p: max(p.len - 3, 0)
2119 ),
2120 lambda p: p.measurement_request_type != 5
2121 ),
2122 ConditionalField(
2123 PacketListField(
2124 "subelems",
2125 [],
2126 SubelemTLV,
2127 length_from=lambda p: max(p.len - 16, 0)
2128 ),
2129 lambda p: p.measurement_request_type == 5 and p.len > 16
2130 )
2131 ]
2134# 802.11-2020 9.4.2.21.7
2136class Dot11RadioMeasurementBeaconReport(Packet):
2137 name = "802.11 Radio Measurement Beacon Report"
2138 fields_desc = [
2139 ByteField("op_class", 0),
2140 ByteField("channel", 0),
2141 LELongField("measurement_start_time", 0),
2142 LEShortField("measurement_duration", 0),
2143 # Reported Frame Information
2144 BitField("reported_frame_type", 0, 1),
2145 BitField("condensed_phy", 0, 7),
2146 ByteField("RCPI", 0),
2147 ByteField("RSNI", 0),
2148 MACField("BSSID", ETHER_ANY),
2149 ByteField("antenna_id", 0),
2150 LEIntField("parent_tsf", 0),
2151 ]
2153 def extract_padding(self, s):
2154 return b"", s
2157# 802.11-2020 9.6.6.3, 9.4.2.21
2159class Dot11RadioMeasurementReport(Packet):
2160 name = "802.11 Radio Measurement Report"
2161 fields_desc = [
2162 ByteField("token", 0),
2163 ByteEnumField("ID", 39, _dot11_id_enum),
2164 ByteField("len", 29),
2165 ByteField("measurement_token", 0),
2166 # Measurement Report Mode
2167 BitField("reserved", 0, 5),
2168 BitField("refused", 0, 1),
2169 BitField("incapable", 0, 1),
2170 BitField("late", 0, 1),
2171 ByteEnumField("measurement_report_type", 5,
2172 _dot11_measurement_type),
2173 # Only successful Beacon reports are specialized for now. Other or
2174 # unsuccessful measurement report bodies stay raw.
2175 ConditionalField(
2176 PacketField(
2177 "measurement_report",
2178 Dot11RadioMeasurementBeaconReport(),
2179 Dot11RadioMeasurementBeaconReport
2180 ),
2181 lambda p: (
2182 p.measurement_report_type == 5 and
2183 p.late == 0 and
2184 p.incapable == 0 and
2185 p.refused == 0
2186 )
2187 ),
2188 ConditionalField(
2189 StrLenField(
2190 "measurement_report_data",
2191 b"",
2192 length_from=lambda p: max(p.len - 3, 0)
2193 ),
2194 lambda p: (
2195 not (
2196 p.measurement_report_type == 5 and
2197 p.late == 0 and
2198 p.incapable == 0 and
2199 p.refused == 0
2200 )
2201 )
2202 ),
2203 ConditionalField(
2204 PacketListField(
2205 "subelems",
2206 [],
2207 SubelemTLV,
2208 length_from=lambda p: max(p.len - 29, 0)
2209 ),
2210 lambda p: (
2211 p.measurement_report_type == 5 and
2212 p.late == 0 and
2213 p.incapable == 0 and
2214 p.refused == 0 and
2215 p.len > 29
2216 )
2217 )
2218 ]
2221class Dot11S1GBeacon(_Dot11EltUtils):
2222 name = "802.11 S1G Beacon"
2223 fields_desc = [LEIntField("timestamp", 0),
2224 ByteField("change_seq", 0)]
2227###################
2228# 802.11 Security #
2229###################
2231# 802.11-2016 12
2233class Dot11Encrypted(Packet):
2234 name = "802.11 Encrypted (unknown algorithm)"
2235 fields_desc = [StrField("data", None)]
2237 @classmethod
2238 def dispatch_hook(cls, _pkt=None, *args, **kargs):
2239 # Extracted from
2240 # https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-ieee80211.c # noqa: E501
2241 KEY_EXTIV = 0x20
2242 EXTIV_LEN = 8
2243 if _pkt and len(_pkt) >= 3:
2244 if (orb(_pkt[3]) & KEY_EXTIV) and (len(_pkt) >= EXTIV_LEN):
2245 if orb(_pkt[1]) == ((orb(_pkt[0]) | 0x20) & 0x7f): # IS_TKIP
2246 return Dot11TKIP
2247 elif orb(_pkt[2]) == 0: # IS_CCMP
2248 return Dot11CCMP
2249 else:
2250 # Unknown encryption algorithm
2251 return Dot11Encrypted
2252 else:
2253 return Dot11WEP
2254 return conf.raw_layer
2257# 802.11-2016 12.3.2
2259class Dot11WEP(Dot11Encrypted):
2260 name = "802.11 WEP packet"
2261 fields_desc = [StrFixedLenField("iv", b"\0\0\0", 3),
2262 ByteField("keyid", 0),
2263 StrField("wepdata", None, remain=4),
2264 IntField("icv", None)]
2266 def decrypt(self, key=None):
2267 if key is None:
2268 key = conf.wepkey
2269 if key and conf.crypto_valid:
2270 d = Cipher(
2271 decrepit_algorithms.ARC4(self.iv + key.encode("utf8")),
2272 None,
2273 default_backend(),
2274 ).decryptor()
2275 self.add_payload(LLC(d.update(self.wepdata) + d.finalize()))
2277 def post_dissect(self, s):
2278 self.decrypt()
2280 def build_payload(self):
2281 if self.wepdata is None:
2282 return Packet.build_payload(self)
2283 return b""
2285 @crypto_validator
2286 def encrypt(self, p, pay, key=None):
2287 if key is None:
2288 key = conf.wepkey
2289 if key:
2290 if self.icv is None:
2291 pay += struct.pack("<I", crc32(pay) & 0xffffffff)
2292 icv = b""
2293 else:
2294 icv = p[4:8]
2295 e = Cipher(
2296 decrepit_algorithms.ARC4(self.iv + key.encode("utf8")),
2297 None,
2298 default_backend(),
2299 ).encryptor()
2300 return p[:4] + e.update(pay) + e.finalize() + icv
2301 else:
2302 warning("No WEP key set (conf.wepkey).. strange results expected..") # noqa: E501
2303 return b""
2305 def post_build(self, p, pay):
2306 if self.wepdata is None:
2307 p = self.encrypt(p, raw(pay))
2308 return p
2310# we can't dissect ICV / MIC here: they are encrypted
2312# 802.11-2016 12.5.2.2
2315class Dot11TKIP(Dot11Encrypted):
2316 name = "802.11 TKIP packet"
2317 fields_desc = [
2318 # iv - 4 bytes
2319 ByteField("TSC1", 0),
2320 ByteField("WEPSeed", 0),
2321 ByteField("TSC0", 0),
2322 BitField("key_id", 0, 2), #
2323 BitField("ext_iv", 0, 1), # => LE = reversed order
2324 BitField("res", 0, 5), #
2325 # ext_iv - 4 bytes
2326 ConditionalField(ByteField("TSC2", 0), lambda pkt: pkt.ext_iv),
2327 ConditionalField(ByteField("TSC3", 0), lambda pkt: pkt.ext_iv),
2328 ConditionalField(ByteField("TSC4", 0), lambda pkt: pkt.ext_iv),
2329 ConditionalField(ByteField("TSC5", 0), lambda pkt: pkt.ext_iv),
2330 # data
2331 StrField("data", None),
2332 ]
2334# 802.11-2016 12.5.3.2
2337class Dot11CCMP(Dot11Encrypted):
2338 name = "802.11 CCMP packet"
2339 fields_desc = [
2340 # iv - 8 bytes
2341 ByteField("PN0", 0),
2342 ByteField("PN1", 0),
2343 ByteField("res0", 0),
2344 BitField("key_id", 0, 2), #
2345 BitField("ext_iv", 0, 1), # => LE = reversed order
2346 BitField("res1", 0, 5), #
2347 ByteField("PN2", 0),
2348 ByteField("PN3", 0),
2349 ByteField("PN4", 0),
2350 ByteField("PN5", 0),
2351 # data
2352 StrField("data", None),
2353 ]
2356############
2357# Bindings #
2358############
2361bind_top_down(RadioTap, Dot11FCS, present=2, Flags=16)
2362bind_top_down(Dot11, Dot11QoS, type=2, subtype=0xc)
2364bind_layers(PrismHeader, Dot11,)
2365bind_layers(Dot11, LLC, type=2)
2366bind_layers(Dot11QoS, LLC,)
2368# 802.11-2016 9.2.4.1.3 Type and Subtype subfields
2369bind_layers(Dot11, Dot11AssoReq, subtype=0, type=0)
2370bind_layers(Dot11, Dot11AssoResp, subtype=1, type=0)
2371bind_layers(Dot11, Dot11ReassoReq, subtype=2, type=0)
2372bind_layers(Dot11, Dot11ReassoResp, subtype=3, type=0)
2373bind_layers(Dot11, Dot11ProbeReq, subtype=4, type=0)
2374bind_layers(Dot11, Dot11ProbeResp, subtype=5, type=0)
2375bind_layers(Dot11, Dot11Beacon, subtype=8, type=0)
2376bind_layers(Dot11, Dot11S1GBeacon, subtype=1, type=3)
2377bind_layers(Dot11, Dot11ATIM, subtype=9, type=0)
2378bind_layers(Dot11, Dot11Disas, subtype=10, type=0)
2379bind_layers(Dot11, Dot11Auth, subtype=11, type=0)
2380bind_layers(Dot11, Dot11Deauth, subtype=12, type=0)
2381bind_layers(Dot11, Dot11Action, subtype=13, type=0)
2382bind_layers(Dot11, Dot11Ack, subtype=13, type=1)
2383bind_layers(Dot11Beacon, Dot11Elt,)
2384bind_layers(Dot11S1GBeacon, Dot11Elt,)
2385bind_layers(Dot11AssoReq, Dot11Elt,)
2386bind_layers(Dot11AssoResp, Dot11Elt,)
2387bind_layers(Dot11ReassoReq, Dot11Elt,)
2388bind_layers(Dot11ReassoResp, Dot11Elt,)
2389bind_layers(Dot11ProbeReq, Dot11Elt,)
2390bind_layers(Dot11ProbeResp, Dot11Elt,)
2391bind_layers(Dot11Auth, Dot11Elt,)
2392bind_layers(Dot11Elt, Dot11Elt,)
2393bind_layers(Dot11TKIP, conf.raw_layer)
2394bind_layers(Dot11CCMP, conf.raw_layer)
2395bind_layers(Dot11Action, Dot11SpectrumManagement, category=0x00)
2396bind_layers(Dot11SpectrumManagement, Dot11CSA, action=4)
2397bind_layers(Dot11Action, Dot11WNM, category=0x0A)
2398bind_layers(Dot11WNM, Dot11BSSTMRequest, action=7)
2399bind_layers(Dot11WNM, Dot11BSSTMResponse, action=8)
2400bind_layers(Dot11Action, Dot11RadioMeasurement, category=0x05)
2401bind_layers(Dot11RadioMeasurement, Dot11RadioMeasurementRequest, action=0)
2402bind_layers(Dot11RadioMeasurement, Dot11RadioMeasurementReport, action=1)
2405conf.l2types.register(DLT_IEEE802_11, Dot11)
2406conf.l2types.register_num2layer(801, Dot11)
2407conf.l2types.register(DLT_PRISM_HEADER, PrismHeader)
2408conf.l2types.register_num2layer(802, PrismHeader)
2409conf.l2types.register(DLT_IEEE802_11_RADIO, RadioTap)
2410conf.l2types.register_num2layer(803, RadioTap)
2412####################
2413# Other WiFi utils #
2414####################
2417class WiFi_am(AnsweringMachine):
2418 """Before using this, initialize "iffrom" and "ifto" interfaces:
2419iwconfig iffrom mode monitor
2420iwpriv orig_ifto hostapd 1
2421ifconfig ifto up
2422note: if ifto=wlan0ap then orig_ifto=wlan0
2423note: ifto and iffrom must be set on the same channel
2424ex:
2425ifconfig eth1 up
2426iwconfig eth1 mode monitor
2427iwconfig eth1 channel 11
2428iwpriv wlan0 hostapd 1
2429ifconfig wlan0ap up
2430iwconfig wlan0 channel 11
2431iwconfig wlan0 essid dontexist
2432iwconfig wlan0 mode managed
2433"""
2434 function_name = "airpwn"
2435 filter = None
2437 def parse_options(self, iffrom=conf.iface, ifto=conf.iface, replace="",
2438 pattern="", ignorepattern=""):
2439 self.iffrom = iffrom
2440 self.ifto = ifto
2441 self.ptrn = re.compile(pattern.encode())
2442 self.iptrn = re.compile(ignorepattern.encode())
2443 self.replace = replace
2445 def is_request(self, pkt):
2446 if not isinstance(pkt, Dot11):
2447 return 0
2448 if not pkt.FCfield & 1:
2449 return 0
2450 if not pkt.haslayer(TCP):
2451 return 0
2452 tcp = pkt.getlayer(TCP)
2453 pay = raw(tcp.payload)
2454 if not self.ptrn.match(pay):
2455 return 0
2456 if self.iptrn.match(pay) is True:
2457 return 0
2458 return True
2460 def make_reply(self, p):
2461 ip = p.getlayer(IP)
2462 tcp = p.getlayer(TCP)
2463 pay = raw(tcp.payload)
2464 p[IP].underlayer.remove_payload()
2465 p.FCfield = "from_DS"
2466 p.addr1, p.addr2 = p.addr2, p.addr1
2467 p /= IP(src=ip.dst, dst=ip.src)
2468 p /= TCP(sport=tcp.dport, dport=tcp.sport,
2469 seq=tcp.ack, ack=tcp.seq + len(pay),
2470 flags="PA")
2471 q = p.copy()
2472 p /= self.replace
2473 q.ID += 1
2474 q.getlayer(TCP).flags = "RA"
2475 q.getlayer(TCP).seq += len(self.replace)
2476 return [p, q]
2478 def print_reply(self, query, *reply):
2479 p = reply[0][0]
2480 print(p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%"))
2482 def send_reply(self, reply):
2483 sendp(reply, iface=self.ifto, **self.optsend)
2485 def sniff(self):
2486 sniff(iface=self.iffrom, **self.optsniff)
2489conf.stats_dot11_protocols += [Dot11WEP, Dot11Beacon, ]
2492class Dot11PacketList(PacketList):
2493 def __init__(self, res=None, name="Dot11List", stats=None):
2494 if stats is None:
2495 stats = conf.stats_dot11_protocols
2497 PacketList.__init__(self, res, name, stats)
2499 def toEthernet(self):
2500 data = [x[Dot11] for x in self.res if Dot11 in x and x.type == 2]
2501 r2 = []
2502 for p in data:
2503 q = p.copy()
2504 q.unwep()
2505 r2.append(Ether() / q.payload.payload.payload) # Dot11/LLC/SNAP/IP
2506 return PacketList(r2, name="Ether from %s" % self.listname)