1# $Id: 80211.py 53 2008-12-18 01:22:57Z jon.oberheide $
2# -*- coding: utf-8 -*-
3"""IEEE 802.11."""
4from __future__ import print_function
5from __future__ import absolute_import
6
7import struct
8
9from . import dpkt
10from .compat import ntole
11
12# Frame Types
13MGMT_TYPE = 0
14CTL_TYPE = 1
15DATA_TYPE = 2
16
17# Frame Sub-Types
18M_ASSOC_REQ = 0
19M_ASSOC_RESP = 1
20M_REASSOC_REQ = 2
21M_REASSOC_RESP = 3
22M_PROBE_REQ = 4
23M_PROBE_RESP = 5
24M_BEACON = 8
25M_ATIM = 9
26M_DISASSOC = 10
27M_AUTH = 11
28M_DEAUTH = 12
29M_ACTION = 13
30C_BLOCK_ACK_REQ = 8
31C_BLOCK_ACK = 9
32C_PS_POLL = 10
33C_RTS = 11
34C_CTS = 12
35C_ACK = 13
36C_CF_END = 14
37C_CF_END_ACK = 15
38D_DATA = 0
39D_DATA_CF_ACK = 1
40D_DATA_CF_POLL = 2
41D_DATA_CF_ACK_POLL = 3
42D_NULL = 4
43D_CF_ACK = 5
44D_CF_POLL = 6
45D_CF_ACK_POLL = 7
46D_QOS_DATA = 8
47D_QOS_CF_ACK = 9
48D_QOS_CF_POLL = 10
49D_QOS_CF_ACK_POLL = 11
50D_QOS_NULL = 12
51D_QOS_CF_POLL_EMPTY = 14
52
53TO_DS_FLAG = 10
54FROM_DS_FLAG = 1
55INTER_DS_FLAG = 11
56
57# Bitshifts for Frame Control
58_VERSION_MASK = 0x0300
59_TYPE_MASK = 0x0c00
60_SUBTYPE_MASK = 0xf000
61_TO_DS_MASK = 0x0001
62_FROM_DS_MASK = 0x0002
63_MORE_FRAG_MASK = 0x0004
64_RETRY_MASK = 0x0008
65_PWR_MGT_MASK = 0x0010
66_MORE_DATA_MASK = 0x0020
67_WEP_MASK = 0x0040
68_ORDER_MASK = 0x0080
69_VERSION_SHIFT = 8
70_TYPE_SHIFT = 10
71_SUBTYPE_SHIFT = 12
72_TO_DS_SHIFT = 0
73_FROM_DS_SHIFT = 1
74_MORE_FRAG_SHIFT = 2
75_RETRY_SHIFT = 3
76_PWR_MGT_SHIFT = 4
77_MORE_DATA_SHIFT = 5
78_WEP_SHIFT = 6
79_ORDER_SHIFT = 7
80
81# IEs
82IE_SSID = 0
83IE_RATES = 1
84IE_FH = 2
85IE_DS = 3
86IE_CF = 4
87IE_TIM = 5
88IE_IBSS = 6
89IE_HT_CAPA = 45
90IE_ESR = 50
91IE_HT_INFO = 61
92
93FCS_LENGTH = 4
94
95FRAMES_WITH_CAPABILITY = [M_BEACON, M_ASSOC_RESP, M_ASSOC_REQ, M_REASSOC_REQ, ]
96
97# Block Ack control constants
98_ACK_POLICY_SHIFT = 0
99_MULTI_TID_SHIFT = 1
100_COMPRESSED_SHIFT = 2
101_TID_SHIFT = 12
102
103_ACK_POLICY_MASK = 0x0001
104_MULTI_TID_MASK = 0x0002
105_COMPRESSED_MASK = 0x0004
106_TID_MASK = 0xf000
107
108_COMPRESSED_BMP_LENGTH = 8
109_BMP_LENGTH = 128
110
111# Action frame categories
112BLOCK_ACK = 3
113
114# Block ack category action codes
115BLOCK_ACK_CODE_REQUEST = 0
116BLOCK_ACK_CODE_RESPONSE = 1
117BLOCK_ACK_CODE_DELBA = 2
118
119
120class IEEE80211(dpkt.Packet):
121    """IEEE 802.11.
122
123    TODO: Longer class information....
124
125    Attributes:
126        __hdr__: Header fields of IEEE802.11.
127        TODO.
128    """
129
130    __hdr__ = (
131        ('framectl', 'H', 0),
132        ('duration', 'H', 0)
133    )
134
135    # The standard really defines the entire MAC protocol as little-endian on the wire,
136    # however there is broken logic in the rest of the module preventing this from working right now
137    #  __byte_order__ = '<'
138
139    @property
140    def version(self):
141        return (self.framectl & _VERSION_MASK) >> _VERSION_SHIFT
142
143    @version.setter
144    def version(self, val):
145        self.framectl = (val << _VERSION_SHIFT) | (self.framectl & ~_VERSION_MASK)
146
147    @property
148    def type(self):
149        return (self.framectl & _TYPE_MASK) >> _TYPE_SHIFT
150
151    @type.setter
152    def type(self, val):
153        self.framectl = (val << _TYPE_SHIFT) | (self.framectl & ~_TYPE_MASK)
154
155    @property
156    def subtype(self):
157        return (self.framectl & _SUBTYPE_MASK) >> _SUBTYPE_SHIFT
158
159    @subtype.setter
160    def subtype(self, val):
161        self.framectl = (val << _SUBTYPE_SHIFT) | (self.framectl & ~_SUBTYPE_MASK)
162
163    @property
164    def to_ds(self):
165        return (self.framectl & _TO_DS_MASK) >> _TO_DS_SHIFT
166
167    @to_ds.setter
168    def to_ds(self, val):
169        self.framectl = (val << _TO_DS_SHIFT) | (self.framectl & ~_TO_DS_MASK)
170
171    @property
172    def from_ds(self):
173        return (self.framectl & _FROM_DS_MASK) >> _FROM_DS_SHIFT
174
175    @from_ds.setter
176    def from_ds(self, val):
177        self.framectl = (val << _FROM_DS_SHIFT) | (self.framectl & ~_FROM_DS_MASK)
178
179    @property
180    def more_frag(self):
181        return (self.framectl & _MORE_FRAG_MASK) >> _MORE_FRAG_SHIFT
182
183    @more_frag.setter
184    def more_frag(self, val):
185        self.framectl = (val << _MORE_FRAG_SHIFT) | (self.framectl & ~_MORE_FRAG_MASK)
186
187    @property
188    def retry(self):
189        return (self.framectl & _RETRY_MASK) >> _RETRY_SHIFT
190
191    @retry.setter
192    def retry(self, val):
193        self.framectl = (val << _RETRY_SHIFT) | (self.framectl & ~_RETRY_MASK)
194
195    @property
196    def pwr_mgt(self):
197        return (self.framectl & _PWR_MGT_MASK) >> _PWR_MGT_SHIFT
198
199    @pwr_mgt.setter
200    def pwr_mgt(self, val):
201        self.framectl = (val << _PWR_MGT_SHIFT) | (self.framectl & ~_PWR_MGT_MASK)
202
203    @property
204    def more_data(self):
205        return (self.framectl & _MORE_DATA_MASK) >> _MORE_DATA_SHIFT
206
207    @more_data.setter
208    def more_data(self, val):
209        self.framectl = (val << _MORE_DATA_SHIFT) | (self.framectl & ~_MORE_DATA_MASK)
210
211    @property
212    def wep(self):
213        return (self.framectl & _WEP_MASK) >> _WEP_SHIFT
214
215    @wep.setter
216    def wep(self, val):
217        self.framectl = (val << _WEP_SHIFT) | (self.framectl & ~_WEP_MASK)
218
219    @property
220    def order(self):
221        return (self.framectl & _ORDER_MASK) >> _ORDER_SHIFT
222
223    @order.setter
224    def order(self, val):
225        self.framectl = (val << _ORDER_SHIFT) | (self.framectl & ~_ORDER_MASK)
226
227    def unpack_ies(self, buf):
228        self.ies = []
229
230        ie_decoder = {
231            IE_SSID: ('ssid', self.IE),
232            IE_RATES: ('rate', self.IE),
233            IE_FH: ('fh', self.FH),
234            IE_DS: ('ds', self.DS),
235            IE_CF: ('cf', self.CF),
236            IE_TIM: ('tim', self.TIM),
237            IE_IBSS: ('ibss', self.IBSS),
238            IE_HT_CAPA: ('ht_capa', self.IE),
239            IE_ESR: ('esr', self.IE),
240            IE_HT_INFO: ('ht_info', self.IE)
241        }
242
243        # each IE starts with an ID and a length
244        while len(buf) > FCS_LENGTH:
245            ie_id = struct.unpack('B', buf[:1])[0]
246            try:
247                parser = ie_decoder[ie_id][1]
248                name = ie_decoder[ie_id][0]
249            except KeyError:
250                parser = self.IE
251                name = 'ie_' + str(ie_id)
252            ie = parser(buf)
253
254            ie.data = buf[2:2 + ie.len]
255            setattr(self, name, ie)
256            self.ies.append(ie)
257            buf = buf[2 + ie.len:]
258
259    class Capability(object):
260        def __init__(self, field):
261            self.ess = field & 1
262            self.ibss = (field >> 1) & 1
263            self.cf_poll = (field >> 2) & 1
264            self.cf_poll_req = (field >> 3) & 1
265            self.privacy = (field >> 4) & 1
266            self.short_preamble = (field >> 5) & 1
267            self.pbcc = (field >> 6) & 1
268            self.hopping = (field >> 7) & 1
269            self.spec_mgmt = (field >> 8) & 1
270            self.qos = (field >> 9) & 1
271            self.short_slot = (field >> 10) & 1
272            self.apsd = (field >> 11) & 1
273            self.dsss = (field >> 13) & 1
274            self.delayed_blk_ack = (field >> 14) & 1
275            self.imm_blk_ack = (field >> 15) & 1
276
277    def __init__(self, *args, **kwargs):
278        if kwargs and 'fcs' in kwargs:
279            self.fcs_present = kwargs.pop('fcs')
280        else:
281            self.fcs_present = False
282
283        super(IEEE80211, self).__init__(*args, **kwargs)
284
285    def unpack(self, buf):
286        dpkt.Packet.unpack(self, buf)
287        self.data = buf[self.__hdr_len__:]
288
289        m_decoder = {
290            M_BEACON: ('beacon', self.Beacon),
291            M_ASSOC_REQ: ('assoc_req', self.Assoc_Req),
292            M_ASSOC_RESP: ('assoc_resp', self.Assoc_Resp),
293            M_DISASSOC: ('diassoc', self.Disassoc),
294            M_REASSOC_REQ: ('reassoc_req', self.Reassoc_Req),
295            M_REASSOC_RESP: ('reassoc_resp', self.Assoc_Resp),
296            M_AUTH: ('auth', self.Auth),
297            M_PROBE_RESP: ('probe_resp', self.Beacon),
298            M_DEAUTH: ('deauth', self.Deauth),
299            M_ACTION: ('action', self.Action)
300        }
301
302        c_decoder = {
303            C_RTS: ('rts', self.RTS),
304            C_CTS: ('cts', self.CTS),
305            C_ACK: ('ack', self.ACK),
306            C_BLOCK_ACK_REQ: ('bar', self.BlockAckReq),
307            C_BLOCK_ACK: ('back', self.BlockAck),
308            C_CF_END: ('cf_end', self.CFEnd),
309        }
310
311        d_dsData = {
312            0: self.Data,
313            FROM_DS_FLAG: self.DataFromDS,
314            TO_DS_FLAG: self.DataToDS,
315            INTER_DS_FLAG: self.DataInterDS
316        }
317
318        # For now decode everything with DATA. Haven't checked about other QoS
319        # additions
320        d_decoder = {
321            # modified the decoder to consider the ToDS and FromDS flags
322            # Omitting the 11 case for now
323            D_DATA: ('data_frame', d_dsData),
324            D_NULL: ('data_frame', d_dsData),
325            D_QOS_DATA: ('data_frame', d_dsData),
326            D_QOS_NULL: ('data_frame', d_dsData)
327        }
328
329        decoder = {
330            MGMT_TYPE: m_decoder,
331            CTL_TYPE: c_decoder,
332            DATA_TYPE: d_decoder
333        }
334
335        # Strip off the FCS field
336        if self.fcs_present:
337            self.fcs = struct.unpack('<I', self.data[-1 * FCS_LENGTH:])[0]
338            self.data = self.data[0: -1 * FCS_LENGTH]
339
340        if self.type == MGMT_TYPE:
341            self.mgmt = self.MGMT_Frame(self.data)
342            self.data = self.mgmt.data
343            if self.subtype == M_PROBE_REQ:
344                self.unpack_ies(self.data)
345                return
346            if self.subtype == M_ATIM:
347                return
348
349        try:
350            parser = decoder[self.type][self.subtype][1]
351            name = decoder[self.type][self.subtype][0]
352        except KeyError:
353            raise dpkt.UnpackError("KeyError: type=%s subtype=%s" % (self.type, self.subtype))
354
355        if self.type == DATA_TYPE:
356            # need to grab the ToDS/FromDS info
357            parser = parser[self.to_ds * 10 + self.from_ds]
358
359        if self.type == MGMT_TYPE:
360            field = parser(self.mgmt.data)
361        else:
362            field = parser(self.data)
363            self.data = field
364
365        setattr(self, name, field)
366
367        if self.type == MGMT_TYPE:
368            self.unpack_ies(field.data)
369            if self.subtype in FRAMES_WITH_CAPABILITY:
370                self.capability = self.Capability(ntole(field.capability))
371
372        if self.type == DATA_TYPE and self.subtype == D_QOS_DATA:
373            self.qos_data = self.QoS_Data(field.data)
374            field.data = self.qos_data.data
375
376        self.data = field.data
377
378    class BlockAckReq(dpkt.Packet):
379        __hdr__ = (
380            ('dst', '6s', '\x00' * 6),
381            ('src', '6s', '\x00' * 6),
382            ('ctl', 'H', 0),
383            ('seq', 'H', 0),
384        )
385
386    class BlockAck(dpkt.Packet):
387        __hdr__ = (
388            ('dst', '6s', '\x00' * 6),
389            ('src', '6s', '\x00' * 6),
390            ('ctl', 'H', 0),
391            ('seq', 'H', 0),
392        )
393
394        @property
395        def compressed(self):
396            return (self.ctl & _COMPRESSED_MASK) >> _COMPRESSED_SHIFT
397
398        @compressed.setter
399        def compressed(self, val):
400            self.ctl = (val << _COMPRESSED_SHIFT) | (self.ctl & ~_COMPRESSED_MASK)
401
402        @property
403        def ack_policy(self):
404            return (self.ctl & _ACK_POLICY_MASK) >> _ACK_POLICY_SHIFT
405
406        @ack_policy.setter
407        def ack_policy(self, val):
408            self.ctl = (val << _ACK_POLICY_SHIFT) | (self.ctl & ~_ACK_POLICY_MASK)
409
410        @property
411        def multi_tid(self):
412            return (self.ctl & _MULTI_TID_MASK) >> _MULTI_TID_SHIFT
413
414        @multi_tid.setter
415        def multi_tid(self, val):
416            self.ctl = (val << _MULTI_TID_SHIFT) | (self.ctl & ~_MULTI_TID_MASK)
417
418        @property
419        def tid(self):
420            return (self.ctl & _TID_MASK) >> _TID_SHIFT
421
422        @tid.setter
423        def tid(self, val):
424            self.ctl = (val << _TID_SHIFT) | (self.ctl & ~_TID_MASK)
425
426        def unpack(self, buf):
427            dpkt.Packet.unpack(self, buf)
428            self.data = buf[self.__hdr_len__:]
429            self.ctl = ntole(self.ctl)
430
431            if self.compressed:
432                self.bmp = struct.unpack('8s', self.data[0:_COMPRESSED_BMP_LENGTH])[0]
433            else:
434                self.bmp = struct.unpack('128s', self.data[0:_BMP_LENGTH])[0]
435            self.data = self.data[len(self.__hdr__) + len(self.bmp):]
436
437    class RTS(dpkt.Packet):
438        __hdr__ = (
439            ('dst', '6s', '\x00' * 6),
440            ('src', '6s', '\x00' * 6)
441        )
442
443    class CTS(dpkt.Packet):
444        __hdr__ = (
445            ('dst', '6s', '\x00' * 6),
446        )
447
448    class ACK(dpkt.Packet):
449        __hdr__ = (
450            ('dst', '6s', '\x00' * 6),
451        )
452
453    class CFEnd(dpkt.Packet):
454        __hdr__ = (
455            ('dst', '6s', '\x00' * 6),
456            ('src', '6s', '\x00' * 6),
457        )
458
459    class MGMT_Frame(dpkt.Packet):
460        __hdr__ = (
461            ('dst', '6s', '\x00' * 6),
462            ('src', '6s', '\x00' * 6),
463            ('bssid', '6s', '\x00' * 6),
464            ('frag_seq', 'H', 0)
465        )
466
467    class Beacon(dpkt.Packet):
468        __hdr__ = (
469            ('timestamp', 'Q', 0),
470            ('interval', 'H', 0),
471            ('capability', 'H', 0)
472        )
473
474    class Disassoc(dpkt.Packet):
475        __hdr__ = (
476            ('reason', 'H', 0),
477        )
478
479    class Assoc_Req(dpkt.Packet):
480        __hdr__ = (
481            ('capability', 'H', 0),
482            ('interval', 'H', 0)
483        )
484
485    class Assoc_Resp(dpkt.Packet):
486        __hdr__ = (
487            ('capability', 'H', 0),
488            ('status', 'H', 0),
489            ('aid', 'H', 0)
490        )
491
492    class Reassoc_Req(dpkt.Packet):
493        __hdr__ = (
494            ('capability', 'H', 0),
495            ('interval', 'H', 0),
496            ('current_ap', '6s', '\x00' * 6)
497        )
498
499    # This obviously doesn't support any of AUTH frames that use encryption
500    class Auth(dpkt.Packet):
501        __hdr__ = (
502            ('algorithm', 'H', 0),
503            ('auth_seq', 'H', 0),
504        )
505
506    class Deauth(dpkt.Packet):
507        __hdr__ = (
508            ('reason', 'H', 0),
509        )
510
511    class Action(dpkt.Packet):
512        __hdr__ = (
513            ('category', 'B', 0),
514            ('code', 'B', 0),
515        )
516
517        def unpack(self, buf):
518            dpkt.Packet.unpack(self, buf)
519
520            action_parser = {
521                BLOCK_ACK: {
522                    BLOCK_ACK_CODE_REQUEST: ('block_ack_request', IEEE80211.BlockAckActionRequest),
523                    BLOCK_ACK_CODE_RESPONSE: ('block_ack_response', IEEE80211.BlockAckActionResponse),
524                    BLOCK_ACK_CODE_DELBA: ('block_ack_delba', IEEE80211.BlockAckActionDelba),
525                },
526            }
527
528            try:
529                decoder = action_parser[self.category][self.code][1]
530                field_name = action_parser[self.category][self.code][0]
531            except KeyError:
532                raise dpkt.UnpackError("KeyError: category=%s code=%s" % (self.category, self.code))
533
534            field = decoder(self.data)
535            setattr(self, field_name, field)
536            self.data = field.data
537
538    class BlockAckActionRequest(dpkt.Packet):
539        __hdr__ = (
540            ('dialog', 'B', 0),
541            ('parameters', 'H', 0),
542            ('timeout', 'H', 0),
543            ('starting_seq', 'H', 0),
544        )
545
546    class BlockAckActionResponse(dpkt.Packet):
547        __hdr__ = (
548            ('dialog', 'B', 0),
549            ('status_code', 'H', 0),
550            ('parameters', 'H', 0),
551            ('timeout', 'H', 0),
552        )
553
554    class BlockAckActionDelba(dpkt.Packet):
555        __byte_order__ = '<'
556        __hdr__ = (
557            ('delba_param_set', 'H', 0),
558            ('reason_code', 'H', 0),
559            # ('gcr_group_addr', '8s', '\x00' * 8), # Standard says it must be there, but it isn't?
560        )
561
562    class Data(dpkt.Packet):
563        __hdr__ = (
564            ('dst', '6s', '\x00' * 6),
565            ('src', '6s', '\x00' * 6),
566            ('bssid', '6s', '\x00' * 6),
567            ('frag_seq', 'H', 0)
568        )
569
570    class DataFromDS(dpkt.Packet):
571        __hdr__ = (
572            ('dst', '6s', '\x00' * 6),
573            ('bssid', '6s', '\x00' * 6),
574            ('src', '6s', '\x00' * 6),
575            ('frag_seq', 'H', 0)
576        )
577
578    class DataToDS(dpkt.Packet):
579        __hdr__ = (
580            ('bssid', '6s', '\x00' * 6),
581            ('src', '6s', '\x00' * 6),
582            ('dst', '6s', '\x00' * 6),
583            ('frag_seq', 'H', 0)
584        )
585
586    class DataInterDS(dpkt.Packet):
587        __hdr__ = (
588            ('dst', '6s', '\x00' * 6),
589            ('src', '6s', '\x00' * 6),
590            ('da', '6s', '\x00' * 6),
591            ('frag_seq', 'H', 0),
592            ('sa', '6s', '\x00' * 6)
593        )
594
595    class QoS_Data(dpkt.Packet):
596        __hdr__ = (
597            ('control', 'H', 0),
598        )
599
600    class IE(dpkt.Packet):
601        __hdr__ = (
602            ('id', 'B', 0),
603            ('len', 'B', 0)
604        )
605
606        def unpack(self, buf):
607            dpkt.Packet.unpack(self, buf)
608            self.info = buf[2:self.len + 2]
609
610    class FH(dpkt.Packet):
611        __hdr__ = (
612            ('id', 'B', 0),
613            ('len', 'B', 0),
614            ('tu', 'H', 0),
615            ('hopset', 'B', 0),
616            ('hoppattern', 'B', 0),
617            ('hopindex', 'B', 0)
618        )
619
620    class DS(dpkt.Packet):
621        __hdr__ = (
622            ('id', 'B', 0),
623            ('len', 'B', 0),
624            ('ch', 'B', 0)
625        )
626
627    class CF(dpkt.Packet):
628        __hdr__ = (
629            ('id', 'B', 0),
630            ('len', 'B', 0),
631            ('count', 'B', 0),
632            ('period', 'B', 0),
633            ('max', 'H', 0),
634            ('dur', 'H', 0)
635        )
636
637    class TIM(dpkt.Packet):
638        __hdr__ = (
639            ('id', 'B', 0),
640            ('len', 'B', 0),
641            ('count', 'B', 0),
642            ('period', 'B', 0),
643            ('ctrl', 'H', 0)
644        )
645
646        def unpack(self, buf):
647            dpkt.Packet.unpack(self, buf)
648            self.bitmap = buf[5:self.len + 2]
649
650    class IBSS(dpkt.Packet):
651        __hdr__ = (
652            ('id', 'B', 0),
653            ('len', 'B', 0),
654            ('atim', 'H', 0)
655        )
656
657
658def test_802211_ack():
659    s = b'\xd4\x00\x00\x00\x00\x12\xf0\xb6\x1c\xa4\xff\xff\xff\xff'
660    ieee = IEEE80211(s, fcs=True)
661    assert ieee.version == 0
662    assert ieee.type == CTL_TYPE
663    assert ieee.subtype == C_ACK
664    assert ieee.to_ds == 0
665    assert ieee.from_ds == 0
666    assert ieee.pwr_mgt == 0
667    assert ieee.more_data == 0
668    assert ieee.wep == 0
669    assert ieee.order == 0
670    assert ieee.ack.dst == b'\x00\x12\xf0\xb6\x1c\xa4'
671    fcs = struct.unpack('<I', s[-4:])[0]
672    assert ieee.fcs == fcs
673
674
675def test_80211_beacon():
676    s = (
677        b'\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x26\xcb\x18\x6a\x30\x00\x26\xcb\x18\x6a\x30'
678        b'\xa0\xd0\x77\x09\x32\x03\x8f\x00\x00\x00\x66\x00\x31\x04\x00\x04\x43\x41\x45\x4e\x01\x08'
679        b'\x82\x84\x8b\x0c\x12\x96\x18\x24\x03\x01\x01\x05\x04\x00\x01\x00\x00\x07\x06\x55\x53\x20'
680        b'\x01\x0b\x1a\x0b\x05\x00\x00\x6e\x00\x00\x2a\x01\x02\x2d\x1a\x6e\x18\x1b\xff\xff\x00\x00'
681        b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x14\x01'
682        b'\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x28\x00\x32\x04\x30'
683        b'\x48\x60\x6c\x36\x03\x51\x63\x03\x3d\x16\x01\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00'
684        b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\x1e\x05\x00\x8f\x00\x0f\x00\xff\x03\x59\x00'
685        b'\x63\x73\x65\x2d\x33\x39\x31\x32\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x36\x96\x06'
686        b'\x00\x40\x96\x00\x14\x00\xdd\x18\x00\x50\xf2\x02\x01\x01\x80\x00\x03\xa4\x00\x00\x27\xa4'
687        b'\x00\x00\x42\x43\x5e\x00\x62\x32\x2f\x00\xdd\x06\x00\x40\x96\x01\x01\x04\xdd\x05\x00\x40'
688        b'\x96\x03\x05\xdd\x05\x00\x40\x96\x0b\x09\xdd\x08\x00\x40\x96\x13\x01\x00\x34\x01\xdd\x05'
689        b'\x00\x40\x96\x14\x05'
690    )
691    ieee = IEEE80211(s, fcs=True)
692    assert ieee.version == 0
693    assert ieee.type == MGMT_TYPE
694    assert ieee.subtype == M_BEACON
695    assert ieee.to_ds == 0
696    assert ieee.from_ds == 0
697    assert ieee.pwr_mgt == 0
698    assert ieee.more_data == 0
699    assert ieee.wep == 0
700    assert ieee.order == 0
701    assert ieee.mgmt.dst == b'\xff\xff\xff\xff\xff\xff'
702    assert ieee.mgmt.src == b'\x00\x26\xcb\x18\x6a\x30'
703    assert ieee.beacon.capability == 0x3104
704    assert ieee.capability.privacy == 1
705    assert ieee.ssid.data == b'CAEN'
706    assert ieee.rate.data == b'\x82\x84\x8b\x0c\x12\x96\x18\x24'
707    assert ieee.ds.data == b'\x01'
708    assert ieee.tim.data == b'\x00\x01\x00\x00'
709    fcs = struct.unpack('<I', s[-4:])[0]
710    assert ieee.fcs == fcs
711
712
713def test_80211_data():
714    s = (
715        b'\x08\x09\x20\x00\x00\x26\xcb\x17\x3d\x91\x00\x16\x44\xb0\xae\xc6\x00\x02\xb3\xd6\x26\x3c'
716        b'\x80\x7e\xaa\xaa\x03\x00\x00\x00\x08\x00\x45\x00\x00\x28\x07\x27\x40\x00\x80\x06\x1d\x39'
717        b'\x8d\xd4\x37\x3d\x3f\xf5\xd1\x69\xc0\x5f\x01\xbb\xb2\xd6\xef\x23\x38\x2b\x4f\x08\x50\x10'
718        b'\x42\x04\xac\x17\x00\x00'
719    )
720    ieee = IEEE80211(s, fcs=True)
721    assert ieee.type == DATA_TYPE
722    assert ieee.subtype == D_DATA
723    assert ieee.data_frame.dst == b'\x00\x02\xb3\xd6\x26\x3c'
724    assert ieee.data_frame.src == b'\x00\x16\x44\xb0\xae\xc6'
725    assert ieee.data_frame.frag_seq == 0x807e
726    assert ieee.data == (b'\xaa\xaa\x03\x00\x00\x00\x08\x00\x45\x00\x00\x28\x07\x27\x40\x00\x80\x06'
727                         b'\x1d\x39\x8d\xd4\x37\x3d\x3f\xf5\xd1\x69\xc0\x5f\x01\xbb\xb2\xd6\xef\x23'
728                         b'\x38\x2b\x4f\x08\x50\x10\x42\x04')
729    assert ieee.fcs == struct.unpack('<I', b'\xac\x17\x00\x00')[0]
730
731    from . import llc
732    llc_pkt = llc.LLC(ieee.data_frame.data)
733    ip_pkt = llc_pkt.data
734    assert ip_pkt.dst == b'\x3f\xf5\xd1\x69'
735
736
737def test_80211_data_qos():
738    s = (
739        b'\x88\x01\x3a\x01\x00\x26\xcb\x17\x44\xf0\x00\x23\xdf\xc9\xc0\x93\x00\x26\xcb\x17\x44\xf0'
740        b'\x20\x7b\x00\x00\xaa\xaa\x03\x00\x00\x00\x88\x8e\x01\x00\x00\x74\x02\x02\x00\x74\x19\x80'
741        b'\x00\x00\x00\x6a\x16\x03\x01\x00\x65\x01\x00\x00\x61\x03\x01\x4b\x4c\xa7\x7e\x27\x61\x6f'
742        b'\x02\x7b\x3c\x72\x39\xe3\x7b\xd7\x43\x59\x91\x7f\xaa\x22\x47\x51\xb6\x88\x9f\x85\x90\x87'
743        b'\x5a\xd1\x13\x20\xe0\x07\x00\x00\x68\xbd\xa4\x13\xb0\xd5\x82\x7e\xc7\xfb\xe7\xcc\xab\x6e'
744        b'\x5d\x5a\x51\x50\xd4\x45\xc5\xa1\x65\x53\xad\xb5\x88\x5b\x00\x1a\x00\x2f\x00\x05\x00\x04'
745        b'\x00\x35\x00\x0a\x00\x09\x00\x03\x00\x08\x00\x33\x00\x39\x00\x16\x00\x15\x00\x14\x01\x00'
746        b'\xff\xff\xff\xff'
747    )
748    ieee = IEEE80211(s, fcs=True)
749    assert ieee.type == DATA_TYPE
750    assert ieee.subtype == D_QOS_DATA
751    assert ieee.data_frame.dst == b'\x00\x26\xcb\x17\x44\xf0'
752    assert ieee.data_frame.src == b'\x00\x23\xdf\xc9\xc0\x93'
753    assert ieee.data_frame.frag_seq == 0x207b
754    assert ieee.data == (b'\xaa\xaa\x03\x00\x00\x00\x88\x8e\x01\x00\x00\x74\x02\x02\x00\x74\x19\x80'
755                         b'\x00\x00\x00\x6a\x16\x03\x01\x00\x65\x01\x00\x00\x61\x03\x01\x4b\x4c\xa7'
756                         b'\x7e\x27\x61\x6f\x02\x7b\x3c\x72\x39\xe3\x7b\xd7\x43\x59\x91\x7f\xaa\x22'
757                         b'\x47\x51\xb6\x88\x9f\x85\x90\x87\x5a\xd1\x13\x20\xe0\x07\x00\x00\x68\xbd'
758                         b'\xa4\x13\xb0\xd5\x82\x7e\xc7\xfb\xe7\xcc\xab\x6e\x5d\x5a\x51\x50\xd4\x45'
759                         b'\xc5\xa1\x65\x53\xad\xb5\x88\x5b\x00\x1a\x00\x2f\x00\x05\x00\x04\x00\x35'
760                         b'\x00\x0a\x00\x09\x00\x03\x00\x08\x00\x33\x00\x39\x00\x16\x00\x15\x00\x14\x01\x00')
761    assert ieee.qos_data.control == 0x0
762    assert ieee.fcs == struct.unpack('<I', b'\xff\xff\xff\xff')[0]
763
764
765def test_bug():
766    s = (b'\x88\x41\x2c\x00\x00\x26\xcb\x17\x44\xf0\x00\x1e\x52\x97\x14\x11\x00\x1f\x6d\xe8\x18\x00'
767         b'\xd0\x07\x00\x00\x6f\x00\x00\x20\x00\x00\x00\x00')
768    ieee = IEEE80211(s)
769    assert ieee.wep == 1
770
771
772def test_data_ds():
773    # verifying the ToDS and FromDS fields and that we're getting the
774    # correct values
775
776    s = (b'\x08\x03\x00\x00\x01\x0b\x85\x00\x00\x00\x00\x26\xcb\x18\x73\x50\x01\x0b\x85\x00\x00\x00'
777         b'\x00\x89\x00\x26\xcb\x18\x73\x50')
778    ieee = IEEE80211(s)
779    assert ieee.type == DATA_TYPE
780    assert ieee.to_ds == 1
781    assert ieee.from_ds == 1
782    assert ieee.data_frame.sa == b'\x00\x26\xcb\x18\x73\x50'
783    assert ieee.data_frame.src == b'\x00\x26\xcb\x18\x73\x50'
784    assert ieee.data_frame.dst == b'\x01\x0b\x85\x00\x00\x00'
785    assert ieee.data_frame.da == b'\x01\x0b\x85\x00\x00\x00'
786
787    s = (b'\x88\x41\x50\x01\x00\x26\xcb\x17\x48\xc1\x00\x24\x2c\xe7\xfe\x8a\xff\xff\xff\xff\xff\xff'
788         b'\x80\xa0\x00\x00\x09\x1a\x00\x20\x00\x00\x00\x00')
789    ieee = IEEE80211(s)
790    assert ieee.type == DATA_TYPE
791    assert ieee.to_ds == 1
792    assert ieee.from_ds == 0
793    assert ieee.data_frame.bssid == b'\x00\x26\xcb\x17\x48\xc1'
794    assert ieee.data_frame.src == b'\x00\x24\x2c\xe7\xfe\x8a'
795    assert ieee.data_frame.dst == b'\xff\xff\xff\xff\xff\xff'
796
797    s = b'\x08\x02\x02\x01\x00\x02\x44\xac\x27\x70\x00\x1f\x33\x39\x75\x44\x00\x1f\x33\x39\x75\x44\x90\xa4'
798    ieee = IEEE80211(s)
799    assert ieee.type == DATA_TYPE
800    assert ieee.to_ds == 0
801    assert ieee.from_ds == 1
802    assert ieee.data_frame.bssid == b'\x00\x1f\x33\x39\x75\x44'
803    assert ieee.data_frame.src == b'\x00\x1f\x33\x39\x75\x44'
804    assert ieee.data_frame.dst == b'\x00\x02\x44\xac\x27\x70'
805
806
807def test_compressed_block_ack():
808    s = (b'\x94\x00\x00\x00\x34\xc0\x59\xd6\x3f\x62\xb4\x75\x0e\x46\x83\xc1\x05\x50\x80\xee\x03\x00'
809         b'\x00\x00\x00\x00\x00\x00\xa2\xe4\x98\x45')
810    ieee = IEEE80211(s, fcs=True)
811    assert ieee.type == CTL_TYPE
812    assert ieee.subtype == C_BLOCK_ACK
813    assert ieee.back.dst == b'\x34\xc0\x59\xd6\x3f\x62'
814    assert ieee.back.src == b'\xb4\x75\x0e\x46\x83\xc1'
815    assert ieee.back.compressed == 1
816    assert len(ieee.back.bmp) == 8
817    assert ieee.back.ack_policy == 1
818    assert ieee.back.tid == 5
819
820
821def test_action_block_ack_request():
822    s = (b'\xd0\x00\x3a\x01\x00\x23\x14\x36\x52\x30\xb4\x75\x0e\x46\x83\xc1\xb4\x75\x0e\x46\x83\xc1'
823         b'\x70\x14\x03\x00\x0d\x02\x10\x00\x00\x40\x29\x06\x50\x33\x9e')
824    ieee = IEEE80211(s, fcs=True)
825    assert ieee.type == MGMT_TYPE
826    assert ieee.subtype == M_ACTION
827    assert ieee.action.category == BLOCK_ACK
828    assert ieee.action.code == BLOCK_ACK_CODE_REQUEST
829    assert ieee.action.block_ack_request.timeout == 0
830    parameters = struct.unpack('<H', b'\x10\x02')[0]
831    assert ieee.action.block_ack_request.parameters == parameters
832
833
834def test_action_block_ack_response():
835    s = (b'\xd0\x00\x3c\x00\xb4\x75\x0e\x46\x83\xc1\x00\x23\x14\x36\x52\x30\xb4\x75\x0e\x46\x83\xc1'
836         b'\xd0\x68\x03\x01\x0d\x00\x00\x02\x10\x88\x13\x9f\xc0\x0b\x75')
837    ieee = IEEE80211(s, fcs=True)
838    assert ieee.type == MGMT_TYPE
839    assert ieee.subtype == M_ACTION
840    assert ieee.action.category == BLOCK_ACK
841    assert ieee.action.code == BLOCK_ACK_CODE_RESPONSE
842    timeout = struct.unpack('<H', b'\x13\x88')[0]
843    assert ieee.action.block_ack_response.timeout == timeout
844    parameters = struct.unpack('<H', b'\x10\x02')[0]
845    assert ieee.action.block_ack_response.parameters == parameters
846
847
848def test_action_block_ack_delete():
849    s = (b'\xd0\x00\x2c\x00\x00\xc1\x41\x06\x13\x0d\x6c\xb2\xae\xae\xde\x80\x6c\xb2\xae\xae\xde\x80'
850         b'\xa0\x52\x03\x02\x00\x08\x01\x00\x74\x5d\x0a\xc6')
851    ieee = IEEE80211(s, fcs=True)
852    assert ieee.type == MGMT_TYPE
853    assert ieee.subtype == M_ACTION
854    assert ieee.action.category == BLOCK_ACK
855    assert ieee.action.code == BLOCK_ACK_CODE_DELBA
856    assert ieee.action.block_ack_delba.delba_param_set == 0x0800
857    assert ieee.action.block_ack_delba.reason_code == 1
858
859
860def test_ieee80211_properties():
861    ieee80211 = IEEE80211()
862    assert ieee80211.version == 0
863    ieee80211.version = 1
864    assert ieee80211.version == 1
865
866    assert ieee80211.type == 0
867    ieee80211.type = 1
868    assert ieee80211.type == 1
869
870    assert ieee80211.subtype == 0
871    ieee80211.subtype = 1
872    assert ieee80211.subtype == 1
873
874    assert ieee80211.to_ds == 0
875    ieee80211.to_ds = 1
876    assert ieee80211.to_ds == 1
877
878    assert ieee80211.from_ds == 0
879    ieee80211.from_ds = 1
880    assert ieee80211.from_ds == 1
881
882    assert ieee80211.more_frag == 0
883    ieee80211.more_frag = 1
884    assert ieee80211.more_frag == 1
885
886    assert ieee80211.retry == 0
887    ieee80211.retry = 0
888    assert ieee80211.retry == 0
889
890    assert ieee80211.pwr_mgt == 0
891    ieee80211.pwr_mgt = 0
892    assert ieee80211.pwr_mgt == 0
893
894    assert ieee80211.more_data == 0
895    ieee80211.more_data = 0
896    assert ieee80211.more_data == 0
897
898    assert ieee80211.wep == 0
899    ieee80211.wep = 1
900    assert ieee80211.wep == 1
901
902    assert ieee80211.order == 0
903    ieee80211.order = 1
904    assert ieee80211.order == 1
905
906
907def test_blockack_properties():
908    blockack = IEEE80211.BlockAck()
909    assert blockack.compressed == 0
910    blockack.compressed = 1
911    assert blockack.compressed == 1
912
913    assert blockack.ack_policy == 0
914    blockack.ack_policy = 1
915    assert blockack.ack_policy == 1
916
917    assert blockack.multi_tid == 0
918    blockack.multi_tid = 1
919    assert blockack.multi_tid == 1
920
921    assert blockack.tid == 0
922    blockack.tid = 1
923    assert blockack.tid == 1
924
925
926def test_ieee80211_unpack():
927    import pytest
928    from binascii import unhexlify
929
930    buf = unhexlify(
931        '4000'  # subtype set to M_PROBE_REQ
932        '0000'
933
934        # MGMT_Frame
935        '000000000000'  # dst
936        '000000000000'  # src
937        '000000000000'  # bssid
938        '0000'          # frag_seq
939    )
940    ieee80211 = IEEE80211(buf)
941    assert ieee80211.ies == []
942
943    buf = unhexlify(
944        '9000'  # subtype set to M_ATIM
945        '0000'
946
947        # MGMT_Frame
948        '000000000000'  # dst
949        '000000000000'  # src
950        '000000000000'  # bssid
951        '0000'          # frag_seq
952    )
953    ieee80211 = IEEE80211(buf)
954    assert not hasattr(ieee80211, 'ies')
955
956    buf = unhexlify(
957        '0c00'  # type set to invalid value
958        '0000'
959    )
960    with pytest.raises(dpkt.UnpackError, match="KeyError: type=3 subtype=0"):
961        IEEE80211(buf)
962
963
964def test_blockack_unpack():
965    from binascii import unhexlify
966    # unpack a non-compressed BlockAck
967    buf = unhexlify(
968        '000000000000'
969        '000000000000'
970        '0000'   # compressed flag not set
971        '0000'
972    ) + b'\xff' * 128
973
974    blockack = IEEE80211.BlockAck(buf)
975    assert blockack.bmp == b'\xff' * 128
976    assert blockack.data == b''
977
978
979def test_action_unpack():
980    import pytest
981    from binascii import unhexlify
982    buf = unhexlify(
983        '01'  # category
984        '00'  # code (non-existent)
985    )
986    with pytest.raises(dpkt.UnpackError, match="KeyError: category=1 code=0"):
987        IEEE80211.Action(buf)
988