1"""IEEE 802.11"""
2import logging
3
4from pypacker import pypacker
5from pypacker import triggerlist
6from pypacker import utils
7from pypacker.structcbs import unpack_H, pack_Q, unpack_Q_le
8
9
10logger = logging.getLogger("pypacker")
11
12
13# Frame Types
14MGMT_TYPE		= 0
15CTL_TYPE		= 1
16DATA_TYPE		= 2
17
18# Frame Sub-Types
19# MGMT_TYPE
20M_ASSOC_REQ		= 0
21M_ASSOC_RESP		= 1
22M_REASSOC_REQ		= 2
23M_REASSOC_RESP		= 3
24M_PROBE_REQ		= 4
25M_PROBE_RESP		= 5
26M_DISASSOC		= 10
27M_AUTH			= 11
28M_DEAUTH		= 12
29M_ACTION		= 13
30M_BEACON		= 8
31M_ATIM			= 9
32
33# CTL_TYPE
34C_BLOCK_ACK_REQ		= 8
35C_BLOCK_ACK		= 9
36C_PS_POLL		= 10
37C_RTS			= 11
38C_CTS			= 12
39C_ACK			= 13
40C_CF_END		= 14
41C_CF_END_ACK		= 15
42
43# DATA_TYPE
44D_NORMAL		= 0
45D_DATA_CF_ACK		= 1
46D_DATA_CF_POLL		= 2
47D_DATA_CF_ACK_POLL	= 3
48D_NULL			= 4
49D_CF_ACK		= 5
50D_CF_POLL		= 6
51D_CF_ACK_POLL		= 7
52D_QOS_DATA		= 8
53D_QOS_CF_ACK		= 9
54D_QOS_CF_POLL		= 10
55D_QOS_CF_ACK_POLL	= 11
56D_QOS_NULL		= 12
57D_QOS_CF_POLL_EMPTY	= 14
58
59TO_DS_FLAG		= 1
60FROM_DS_FLAG		= 2
61INTER_DS_FLAG		= 3
62
63
64# name : (mask, offset)
65_FRAMECTRL_SUBHEADERDATA = {
66	"version": (0x0300, 8),
67	"type": (0x0C00, 10),
68	"subtype": (0xF000, 12),
69	"to_ds": (0x0001, 0),
70	"from_ds": (0x0002, 1),
71	"more_frag": (0x0004, 2),
72	"retry": (0x0008, 3),
73	"pwr_mgt": (0x0010, 4),
74	"more_data": (0x0020, 5),
75	"protected": (0x0040, 6),
76	"order": (0x0080, 7),
77	"from_to_ds": (0x0002 | 0x0001, 0),
78}
79
80# needed to distinguish subtypes via types
81TYPE_FACTORS		= [16, 32, 64]
82TYPE_FACTOR_PROTECTED	= 128
83
84_subheader_properties = []
85
86IEEE_FIELDS_SRC_DST_BSSID = ["src", "dst", "bssid"]
87
88# Set properties to access flags
89for subfield_name, mask_off in _FRAMECTRL_SUBHEADERDATA.items():
90	# logger.debug("setting prop: %r, %X, %X" % (subfield_name, mask_off[0], mask_off[1]))
91	subheader = [
92		subfield_name,
93		# lambda**2: avoid lexical closure, do not refer to value via reference
94		(lambda mask, off:
95			(lambda _obj: (_obj.framectl & mask) >> off))(mask_off[0], mask_off[1]),
96		(lambda mask, off:
97			(lambda _obj, _val: _obj.__setattr__("framectl",
98				(_obj.framectl & ~mask) | (_val << off))))(mask_off[0], mask_off[1]),
99	]
100	_subheader_properties.append(subheader)
101
102
103class IEEE80211(pypacker.Packet):
104	__hdr__ = (
105		# AAAABBCC | 00000000
106		# AAAA = subtype BB = type CC = version
107		("framectl", "H", 0),
108		("duration", "H", 0x3A01)  # 314 microseconds
109	)
110
111	__hdr_sub__ = _subheader_properties
112
113	def _dissect(self, buf):
114		#logger.debug("Starting to dissect")
115		# self.type/self.subtype use self.framectl, no unpack will happen in dissect so this has
116		# to be done manually
117		self.framectl = unpack_H(buf[0:2])[0]
118		#logger.debug("ieee80211 bytes=%X, type/subtype is=%X/%X, handler=%r" %
119		#	(self.framectl, self.type, self.subtype,
120		#	pypacker.Packet._id_handlerclass_dct[self.__class__][TYPE_FACTORS[self.type] + self.subtype]))
121		self._init_handler(TYPE_FACTORS[self.type] + self.subtype, buf[4:])
122		#logger.debug("Finished IEEE dissect")
123		return 4
124
125	def is_beacon(self):
126		"""return -- True if packet is a beacon. Avoids parsing upper layer."""
127		return self.type == MGMT_TYPE and self.subtype == M_BEACON
128
129	def extract_client_macs(self):
130		"""
131		Extracts client MACs from upper layer if this is a data packet.
132
133		return -- [mac_client1, ...] or [] if no client macs could be found
134		"""
135		macs_clients = []
136
137		# data: client -> AP or client <- AP
138		if self.type == DATA_TYPE:
139			if self.from_ds == 1 and self.to_ds == 0:
140				macs_clients.append(self.higher_layer.dst)
141			elif self.from_ds == 0 and self.to_ds == 1:
142				macs_clients.append(self.higher_layer.src)
143
144		return [addr for addr in macs_clients if not utils.is_special_mac(addr)]
145
146	#
147	# mgmt frames
148	#
149	class Beacon(pypacker.Packet):
150		__hdr__ = (
151			("dst", "6s", b"\x00" * 6),
152			("src", "6s", b"\x00" * 6),
153			("bssid", "6s", b"\x00" * 6),
154			# 12 Bits: 0->4095 | 4 Bits
155			# SF SS (LE)
156			("seq_frag", "H", 0),
157			# _ts (integer) is saved as LE
158			("_ts", "Q", 0),
159			("interval", "H", 0x6400),
160			("capa", "H", 0x0100),
161			("params", None, triggerlist.TriggerList)
162		)
163
164		def _get_seq(self):
165			return (self.seq_frag & 0xFF) << 4 | (self.seq_frag >> 12)
166
167		def _set_seq(self, val):
168			self.seq_frag = (val & 0xF) << 12 | (val & 0xFF0) >> 4 | (self.seq_frag & 0x0F00)
169
170		def _get_ts(self):
171			# LE->BE: dirty but simple
172			return unpack_Q_le(pack_Q(self._ts))[0]
173
174		def _set_ts(self, val):
175			self._ts = unpack_Q_le(pack_Q(val))[0]
176
177		def _get_essid(self):
178			return self.params.find_value(search_cb=lambda v: v.id == IEEE80211.IE_SSID).body_bytes
179
180		seq = property(_get_seq, _set_seq)
181		ts = property(_get_ts, _set_ts)
182		dst_s = pypacker.get_property_mac("dst")
183		bssid_s = pypacker.get_property_mac("bssid")
184		src_s = pypacker.get_property_mac("src")
185		essid = property(_get_essid)
186
187		def _dissect(self, buf):
188			self._init_triggerlist("params", buf[32:], IEEE80211._unpack_ies)
189			return len(buf)
190
191		def reverse_address(self):
192			self.dst, self.src = self.src, self.dst
193
194	class Action(pypacker.Packet):
195		__hdr__ = (
196			("dst", "6s", b"\x00" * 6),
197			("src", "6s", b"\x00" * 6),
198			("bssid", "6s", b"\x00" * 6),
199			("seq_frag", "H", 0),
200			("category", "B", 0),
201			("code", "B", 0)
202		)
203
204		def _get_seq(self):
205			return (self.seq_frag & 0xFF) << 4 | (self.seq_frag >> 12)
206
207		def _set_seq(self, val):
208			self.seq_frag = (val & 0xF) << 12 | (val & 0xFF0) >> 4 | (self.seq_frag & 0x0F00)
209
210		seq = property(_get_seq, _set_seq)
211
212		class BlockAckRequest(pypacker.Packet):
213			__hdr__ = (
214				("dialog", "B", 0),
215				("parameters", "H", 0),
216				("timeout", "H", 0),
217				("starting_seq", "H", 0),
218			)
219
220		class BlockAckResponse(pypacker.Packet):
221			__hdr__ = (
222				("dialog", "B", 0),
223				("status_code", "H", 0),
224				("parameters", "H", 0),
225				("timeout", "H", 0),
226			)
227
228		CATEGORY_BLOCK_ACK	= 3
229		CODE_BLOCK_ACK_REQUEST	= 0
230		CODE_BLOCK_ACK_RESPONSE	= 1
231
232		dst_s = pypacker.get_property_mac("dst")
233		src_s = pypacker.get_property_mac("src")
234		bssid_s = pypacker.get_property_mac("bssid")
235
236		def _dissect(self, buf):
237			# logger.debug(">>>>>>>> ACTION!!!")
238			# category: block ack, code: request or response
239			self._init_handler(buf[20] * 4 + buf[21], buf[22:])
240			return 22
241
242		def reverse_address(self):
243			self.dst, self.src = self.src, self.dst
244
245	class ProbeReq(pypacker.Packet):
246		__hdr__ = (
247			("dst", "6s", b"\x00" * 6),
248			("src", "6s", b"\x00" * 6),
249			("bssid", "6s", b"\x00" * 6),
250			("seq_frag", "H", 0),
251			("params", None, triggerlist.TriggerList)
252		)
253
254		dst_s = pypacker.get_property_mac("dst")
255		bssid_s = pypacker.get_property_mac("bssid")
256		src_s = pypacker.get_property_mac("src")
257
258		def _get_seq(self):
259			return (self.seq_frag & 0xFF) << 4 | (self.seq_frag >> 12)
260
261		def _set_seq(self, val):
262			self.seq_frag = (val & 0xF) << 12 | (val & 0xFF0) >> 4 | (self.seq_frag & 0x0F00)
263
264		seq = property(_get_seq, _set_seq)
265
266		def _dissect(self, buf):
267			self._init_triggerlist("params", buf[20:], IEEE80211._unpack_ies)
268			return len(buf)
269
270		def reverse_address(self):
271			self.dst, self.src = self.src, self.dst
272
273	class ProbeResp(Beacon):
274		pass
275
276	class AssocReq(pypacker.Packet):
277		__hdr__ = (
278			("dst", "6s", b"\x00" * 6),
279			("src", "6s", b"\x00" * 6),
280			("bssid", "6s", b"\x00" * 6),
281			("seq_frag", "H", 0),
282			("capa", "H", 0),
283			("interval", "H", 0),
284			("params", None, triggerlist.TriggerList)
285		)
286
287		dst_s = pypacker.get_property_mac("dst")
288		bssid_s = pypacker.get_property_mac("bssid")
289		src_s = pypacker.get_property_mac("src")
290
291		def _get_seq(self):
292			return (self.seq_frag & 0xFF) << 4 | (self.seq_frag >> 12)
293
294		def _set_seq(self, val):
295			self.seq_frag = (val & 0xF) << 12 | (val & 0xFF0) >> 4 | (self.seq_frag & 0x0F00)
296
297		seq = property(_get_seq, _set_seq)
298
299		def _dissect(self, buf):
300			self._init_triggerlist("params", buf[24:], IEEE80211._unpack_ies)
301			return len(buf)
302
303		def reverse_address(self):
304			self.dst, self.src = self.src, self.dst
305
306	class AssocResp(pypacker.Packet):
307		__hdr__ = (
308			("dst", "6s", b"\x00" * 6),
309			("src", "6s", b"\x00" * 6),
310			("bssid", "6s", b"\x00" * 6),
311			("seq_frag", "H", 0),
312			("capa", "H", 0),
313			("status", "H", 0),
314			("aid", "H", 0),
315			("params", None, triggerlist.TriggerList)
316		)
317
318		dst_s = pypacker.get_property_mac("dst")
319		bssid_s = pypacker.get_property_mac("bssid")
320		src_s = pypacker.get_property_mac("src")
321
322		def _get_seq(self):
323			return (self.seq_frag & 0xFF) << 4 | (self.seq_frag >> 12)
324
325		def _set_seq(self, val):
326			self.seq_frag = (val & 0xF) << 12 | (val & 0xFF0) >> 4 | (self.seq_frag & 0x0F00)
327
328		seq = property(_get_seq, _set_seq)
329
330		def _dissect(self, buf):
331			self._init_triggerlist("params", buf[26:], IEEE80211._unpack_ies)
332			return len(buf)
333
334		def reverse_address(self):
335			self.dst, self.src = self.src, self.dst
336
337	class Disassoc(pypacker.Packet):
338		__hdr__ = (
339			("dst", "6s", b"\x00" * 6),
340			("src", "6s", b"\x00" * 6),
341			("bssid", "6s", b"\x00" * 6),
342			("seq_frag", "H", 0),
343			("reason", "H", 0),
344		)
345
346		dst_s = pypacker.get_property_mac("dst")
347		bssid_s = pypacker.get_property_mac("bssid")
348		src_s = pypacker.get_property_mac("src")
349
350		def _get_seq(self):
351			return (self.seq_frag & 0xFF) << 4 | (self.seq_frag >> 12)
352
353		def _set_seq(self, val):
354			self.seq_frag = (val & 0xF) << 12 | (val & 0xFF0) >> 4 | (self.seq_frag & 0x0F00)
355
356		seq = property(_get_seq, _set_seq)
357
358		def reverse_address(self):
359			self.dst, self.src = self.src, self.dst
360
361	class ReassocReq(pypacker.Packet):
362		__hdr__ = (
363			("dst", "6s", b"\x00" * 6),
364			("src", "6s", b"\x00" * 6),
365			("bssid", "6s", b"\x00" * 6),
366			("seq_frag", "H", 0),
367			("capa", "H", 0),
368			("interval", "H", 0),
369			("current_ap", "6s", b"\x00" * 6)
370		)
371
372		dst_s = pypacker.get_property_mac("dst")
373		bssid_s = pypacker.get_property_mac("bssid")
374		src_s = pypacker.get_property_mac("src")
375
376		def _get_seq(self):
377			return (self.seq_frag & 0xFF) << 4 | (self.seq_frag >> 12)
378
379		def _set_seq(self, val):
380			self.seq_frag = (val & 0xF) << 12 | (val & 0xFF0) >> 4 | (self.seq_frag & 0x0F00)
381
382		seq = property(_get_seq, _set_seq)
383
384		def reverse_address(self):
385			self.dst, self.src = self.src, self.dst
386
387	class Auth(pypacker.Packet):
388		"""Authentication request."""
389		__hdr__ = (
390			("dst", "6s", b"\x00" * 6),
391			("src", "6s", b"\x00" * 6),
392			("bssid", "6s", b"\x00" * 6),
393			("seq_frag", "H", 0),
394			("algo", "H", 0),
395			("authseq", "H", 0x0100),
396			("status", "H", 0)
397		)
398
399		dst_s = pypacker.get_property_mac("dst")
400		bssid_s = pypacker.get_property_mac("bssid")
401		src_s = pypacker.get_property_mac("src")
402
403		def _get_seq(self):
404			return (self.seq_frag & 0xFF) << 4 | (self.seq_frag >> 12)
405
406		def _set_seq(self, val):
407			self.seq_frag = (val & 0xF) << 12 | (val & 0xFF0) >> 4 | (self.seq_frag & 0x0F00)
408
409		seq = property(_get_seq, _set_seq)
410
411		def reverse_address(self):
412			self.dst, self.src = self.src, self.dst
413
414	class Deauth(pypacker.Packet):
415		__hdr__ = (
416			("dst", "6s", b"\xff" * 6),
417			("src", "6s", b"\x00" * 6),
418			("bssid", "6s", b"\xff" * 6),
419			("seq_frag", "H", 0),
420			("reason", "H", 0x0700)  # class 3 frame received from non associated client
421		)
422
423		dst_s = pypacker.get_property_mac("dst")
424		bssid_s = pypacker.get_property_mac("bssid")
425		src_s = pypacker.get_property_mac("src")
426
427		def _get_seq(self):
428			return (self.seq_frag & 0xFF) << 4 | (self.seq_frag >> 12)
429
430		def _set_seq(self, val):
431			self.seq_frag = (val & 0xF) << 12 | (val & 0xFF0) >> 4 | (self.seq_frag & 0x0F00)
432
433		seq = property(_get_seq, _set_seq)
434
435		def reverse_address(self):
436			self.dst, self.src = self.src, self.dst
437
438	m_decoder = {
439		M_BEACON	: Beacon,
440		M_ACTION	: Action,
441		M_ASSOC_REQ	: AssocReq,
442		M_ASSOC_RESP	: AssocResp,
443		M_DISASSOC	: Disassoc,
444		M_REASSOC_REQ	: ReassocReq,
445		M_REASSOC_RESP	: AssocResp,
446		M_AUTH		: Auth,
447		M_PROBE_REQ	: ProbeReq,
448		M_PROBE_RESP	: ProbeResp,
449		M_DEAUTH	: Deauth
450	}
451
452	#
453	# Control frames: no need for extra layer: 802.11 Base data is enough
454	#
455
456	class RTS(pypacker.Packet):
457		__hdr__ = (
458			("dst", "6s", b"\x00" * 6),
459			("src", "6s", b"\x00" * 6)
460		)
461
462		dst_s = pypacker.get_property_mac("dst")
463		src_s = pypacker.get_property_mac("src")
464
465		def reverse_address(self):
466			self.dst, self.src = self.src, self.dst
467
468	class CTS(pypacker.Packet):
469		__hdr__ = (
470			("dst", "6s", b"\x00" * 6),
471		)
472
473		dst_s = pypacker.get_property_mac("dst")
474
475	class ACK(pypacker.Packet):
476		__hdr__ = (
477			("dst", "6s", b"\x00" * 6),
478		)
479
480		dst_s = pypacker.get_property_mac("dst")
481
482	class BlockAckReq(pypacker.Packet):
483		__hdr__ = (
484			("dst", "6s", b"\x00" * 6),
485			("src", "6s", b"\x00" * 6),
486			("reqctrl", "H", 0),
487			("seq", "H", 0)
488		)
489
490		dst_s = pypacker.get_property_mac("dst")
491		src_s = pypacker.get_property_mac("src")
492
493		def reverse_address(self):
494			self.dst, self.src = self.src, self.dst
495
496	class BlockAck(pypacker.Packet):
497		__hdr__ = (
498			("dst", "6s", b"\x00" * 6),
499			("src", "6s", b"\x00" * 6),
500			("reqctrl", "H", 0),
501			("seq", "H", 0),
502			("bitmap", "Q", 0)
503		)
504
505		dst_s = pypacker.get_property_mac("dst")
506		src_s = pypacker.get_property_mac("src")
507
508		def reverse_address(self):
509			self.dst, self.src = self.src, self.dst
510
511	class CFEnd(pypacker.Packet):
512		__hdr__ = (
513			("dst", "6s", b"\x00" * 6),
514			("src", "6s", b"\x00" * 6),
515		)
516
517		dst_s = pypacker.get_property_mac("dst")
518		src_s = pypacker.get_property_mac("src")
519
520		def reverse_address(self):
521			self.dst, self.src = self.src, self.dst
522
523	c_decoder = {
524		C_RTS		: RTS,
525		C_CTS		: CTS,
526		C_ACK		: ACK,
527		C_BLOCK_ACK_REQ	: BlockAckReq,
528		C_BLOCK_ACK	: BlockAck,
529		C_CF_END	: CFEnd
530	}
531
532	#
533	# Data frames
534	#
535	class Dataframe(pypacker.Packet):
536		__hdr__ = (
537			("addr1", "6s", b"\x00" * 6),
538			("addr2", "6s", b"\x00" * 6),
539			("addr3", "6s", b"\x00" * 6),
540			("seq_frag", "H", 0),
541			("addr4", "6s", None),		# to/from-DS = 1
542			("qos_ctrl", "H", 0),		# QoS
543			("sec_param", "Q", 0)		# protected
544		)
545
546		def _get_seq(self):
547			return (self.seq_frag & 0xFF) << 4 | (self.seq_frag >> 12)
548
549		def _set_seq(self, val):
550			self.seq_frag = (val & 0xF) << 12 | (val & 0xFF0) >> 4 | (self.seq_frag & 0x0F00)
551
552		seq = property(_get_seq, _set_seq)
553
554		def reverse_address(self):
555			if self.from_to_ds == 0:
556				self.addr1, self.addr2 = self.addr2, self.addr1
557			elif self.from_to_ds == 1:
558				self.addr2, self.addr3 = self.addr3, self.addr2
559			elif self.from_to_ds == 2:
560				self.addr1, self.addr3 = self.addr3, self.addr1
561
562		def _get_from_to_ds(self):
563			try:
564				return self._lower_layer.from_to_ds
565			except:
566				return self._from_to_ds
567
568		_from_to_ds = 0
569
570		def _set_from_to_ds(self, value):
571			try:
572				self._lower_layer.from_to_ds = value
573			except:
574				self._from_to_ds = value
575
576		# Same property structure as in IEEE80211 class
577		from_to_ds = property(_get_from_to_ds, _set_from_to_ds)
578
579		def __get_src(self):
580			return self.addr2 if self.from_to_ds in [0, 1] else self.addr3
581
582		def __set_src(self, src):
583			if self.from_to_ds in [0, 1]:
584				self.addr2 = src
585			else:
586				self.addr3 = src
587
588		def __get_dst(self):
589			return self.addr1 if self.from_to_ds in [0, 2] else self.addr3
590
591		def __set_dst(self, dst):
592			if self.from_to_ds in [0, 2]:
593				self.addr1 = dst
594			else:
595				self.addr3 = dst
596
597		def __get_bssid(self):
598			dstype = self.from_to_ds
599
600			if dstype == 0:
601				return self.addr3
602			elif dstype == 1:
603				return self.addr1
604			elif dstype == 2:
605				return self.addr2
606
607		def __set_bssid(self, bssid):
608			dstype = self.from_to_ds
609
610			if dstype == 0:
611				self.addr3 = bssid
612			elif dstype == 1:
613				self.addr1 = bssid
614			elif dstype == 2:
615				self.addr2 = bssid
616
617		src = property(__get_src, __set_src)
618		src_s = pypacker.get_property_mac("src")
619		dst = property(__get_dst, __set_dst)
620		dst_s = pypacker.get_property_mac("dst")
621		bssid = property(__get_bssid, __set_bssid)
622		bssid_s = pypacker.get_property_mac("bssid")
623
624		__QOS_SUBTYPES = {8, 9, 10, 11, 12, 14, 15}
625
626		def _dissect(self, buf):
627			# logger.debug("starting dissecting, buflen: %r" % str(buf))
628			header_len = 30
629
630			"""
631			DataFrames need special care: there are too many types of field combinations
632			to create classes for every one. Solution: initiate by taking from_to_ds of lower_layer
633			In order to use "src/dst/bssid" instead of addrX set from_to_ds
634			to one of the following values:
635
636			[Bit 0: from DS][Bit 1: to DS] = [order of fields]
637
638			00b = 0 = dst, src, bssid
639			01b = 1 = bssid, src, dst
640			10b = 2 = dst, bssid, src
641			11b = 3 = RA, TA, DA, SA
642			"""
643			if self._lower_layer.__class__ == IEEE80211:
644				is_qos = self._lower_layer.subtype in IEEE80211.Dataframe.__QOS_SUBTYPES
645				is_protected = self._lower_layer.protected == 1
646				is_bridge = self._lower_layer.from_ds == 1 and self._lower_layer.to_ds == 1
647			else:
648				# Default is fromds
649				is_qos = False
650				is_protected = False
651				is_bridge = False
652
653			# logger.debug("switching fields1")
654			if not is_qos:
655				self.qos_ctrl = None
656				header_len -= 2
657			# logger.debug("switching fields2")
658			if not is_protected:
659				self.sec_param = None
660				header_len -= 8
661			# logger.debug("switching fields3")
662			if is_bridge:
663				self.addr4 = b"\x00" * 6
664				header_len += 6
665			# logger.debug("format/length/len(bin): %s/%d/%d" % (self._hdr_fmtstr, self.hdr_len, len(self.bin())))
666			# logger.debug("%r" % self)
667			return header_len
668
669	d_decoder = {
670		D_NORMAL		: Dataframe,
671		D_DATA_CF_ACK		: Dataframe,
672		D_DATA_CF_POLL 		: Dataframe,
673		D_DATA_CF_ACK_POLL 	: Dataframe,
674		D_NULL			: Dataframe,
675		D_CF_ACK		: Dataframe,
676		D_CF_POLL		: Dataframe,
677		D_CF_ACK_POLL		: Dataframe,
678		D_QOS_DATA		: Dataframe,
679		D_QOS_CF_ACK		: Dataframe,
680		D_QOS_CF_POLL		: Dataframe,
681		D_QOS_CF_ACK_POLL	: Dataframe,
682		D_QOS_NULL		: Dataframe,
683		D_QOS_CF_POLL_EMPTY	: Dataframe
684	}
685
686	#
687	# IEs for Mgmt-Frames
688	#
689	@staticmethod
690	def _unpack_ies(buf):
691		"""Parse IEs and return them as Triggerlist."""
692		# each IE starts with an ID and a length
693		ies = []
694		off = 0
695		buflen = len(buf)
696		# logger.debug("lazy dissecting: %s" % buf)
697
698		while off + 2 < buflen:
699			ie_id = buf[off]
700			try:
701				parser = IEEE80211.ie_decoder[ie_id]
702			except KeyError:
703				# some unknown tag, use standard format
704				parser = IEEE80211.IE
705
706			dlen = buf[off + 1]
707			#logger.debug("IE parser is: %d = %s = %s" % (ie_id, parser, buf[off: off+2+dlen]))
708			try:
709				ie = parser(buf[off: off + 2 + dlen])
710				ies.append(ie)
711			except:
712				# Not enough bytes for handler, add raw bytes
713				ies.append(buf[off: off + 2 + dlen])
714			off += 2 + dlen
715		#logger.debug("Finished IE parsing")
716
717		return ies
718
719	class IE(pypacker.Packet):
720		__hdr__ = (
721			("id", "B", 0),
722			("len", "B", 0)
723		)
724
725	class FH(pypacker.Packet):
726		__hdr__ = (
727			("id", "B", 0),
728			("len", "B", 0),
729			("tu", "H", 0),
730			("hopset", "B", 0),
731			("hoppattern", "B", 0),
732			("hopindex", "B", 0)
733		)
734
735	class DS(pypacker.Packet):
736		__hdr__ = (
737			("id", "B", 0),
738			("len", "B", 0),
739			("ch", "B", 0)
740		)
741
742	class CF(pypacker.Packet):
743		__hdr__ = (
744			("id", "B", 0),
745			("len", "B", 0),
746			("count", "B", 0),
747			("period", "B", 0),
748			("max", "H", 0),
749			("dur", "H", 0)
750		)
751
752	class TIM(pypacker.Packet):
753		__hdr__ = (
754			("id", "B", 0),
755			("len", "B", 0),
756			("count", "B", 0),
757			("period", "B", 0),
758			("ctrl", "H", 0)
759		)
760
761	class IBSS(pypacker.Packet):
762		__hdr__ = (
763			("id", "B", 0),
764			("len", "B", 0),
765			("atim", "H", 0)
766		)
767
768	# IEs
769	IE_SSID			= 0
770	IE_RATES		= 1
771	IE_FH			= 2
772	IE_DS			= 3
773	IE_CF			= 4
774	IE_TIM			= 5
775	IE_IBSS			= 6
776	IE_HT_CAPA		= 45
777	IE_ESR			= 50
778	IE_HT_INFO		= 61
779
780	ie_decoder = {
781		IE_SSID		: IE,
782		IE_RATES	: IE,
783		IE_FH		: FH,
784		IE_DS		: DS,
785		IE_CF		: CF,
786		IE_TIM		: TIM,
787		IE_IBSS		: IBSS,
788		IE_HT_CAPA	: IE,
789		IE_ESR		: IE,
790		IE_HT_INFO	: IE
791	}
792
793# Handler for IEEE80211
794# position in list = type-ID
795dicts			= [IEEE80211.m_decoder, IEEE80211.c_decoder, IEEE80211.d_decoder]
796decoder_dict_complete	= {}
797
798for pos, decoder_dict in enumerate(dicts):
799	for key_decoder, val_decoder in decoder_dict.items():
800		# Same subtype-ID for different type-IDs, distinguish via "type_factor + subtype"
801		# Not doing so would lead to eg: type:0 + subtype:1 == type:1 + subtype:0
802		decoder_dict_complete[TYPE_FACTORS[pos] + key_decoder] = val_decoder
803
804pypacker.Packet.load_handler(IEEE80211, decoder_dict_complete)
805
806# handler for Action
807CATEGORY_BLOCK_ACK_FACTOR = IEEE80211.Action.CATEGORY_BLOCK_ACK * 4
808pypacker.Packet.load_handler(IEEE80211.Action,
809	{
810		CATEGORY_BLOCK_ACK_FACTOR + IEEE80211.Action.CODE_BLOCK_ACK_REQUEST: IEEE80211.Action.BlockAckRequest,
811		CATEGORY_BLOCK_ACK_FACTOR + IEEE80211.Action.CODE_BLOCK_ACK_RESPONSE: IEEE80211.Action.BlockAckResponse
812	}
813)
814