44# Copyright (C) Ryan Speers <[email protected] > 2011-2012 55# Copyright (C) Roger Meyer <[email protected] >: 2012-03-10 Added frames 66# Copyright (C) Gabriel Potter <[email protected] >: 2018 7- # Copyright (C) 2020 Dimitrios-Georgios Akestoridis <[email protected] > 7+ # Copyright (C) 2020, 2022 Dimitrios-Georgios Akestoridis <[email protected] > 88# This program is published under a GPLv2 license
99
1010"""
3636 XByteField ,
3737 XLEIntField ,
3838 XLEShortField ,
39+ XStrField ,
3940)
4041
4142# Fields #
@@ -233,9 +234,36 @@ class Dot15d4Data(Packet):
233234 lambda pkt :pkt .underlayer .getfieldval ("fcf_srcaddrmode" ) != 0 ), # noqa: E501
234235 # Security field present if fcf_security == True
235236 ConditionalField (PacketField ("aux_sec_header" , Dot15d4AuxSecurityHeader (), Dot15d4AuxSecurityHeader ), # noqa: E501
236- lambda pkt :pkt .underlayer .getfieldval ("fcf_security" ) is True ), # noqa: E501
237+ lambda pkt :pkt .underlayer .getfieldval ("fcf_security" )), # noqa: E501
238+ # Secured Payload (variable length)
239+ ConditionalField (
240+ XStrField ("sec_payload" , "" ),
241+ lambda pkt :pkt .underlayer .getfieldval ("fcf_security" ),
242+ ),
243+ # Message Integrity Code (variable length)
244+ ConditionalField (
245+ XStrField ("mic" , "" ),
246+ lambda pkt :pkt .underlayer .getfieldval ("fcf_security" ),
247+ ),
237248 ]
238249
250+ def post_dissect (self , s ):
251+ if self .underlayer .getfieldval ("fcf_security" ):
252+ if self .aux_sec_header .sec_sc_seclevel in {1 , 5 }:
253+ mic_length = 4
254+ elif self .aux_sec_header .sec_sc_seclevel in {2 , 6 }:
255+ mic_length = 8
256+ elif self .aux_sec_header .sec_sc_seclevel in {3 , 7 }:
257+ mic_length = 16
258+ else :
259+ mic_length = 0
260+ self .sec_payload = bytes (self .aux_sec_header .payload )
261+ self .aux_sec_header .remove_payload ()
262+ if mic_length > 0 and mic_length <= len (self .sec_payload ):
263+ self .mic = self .sec_payload [- mic_length :]
264+ self .sec_payload = self .sec_payload [:- mic_length ]
265+ return s
266+
239267 def guess_payload_class (self , payload ):
240268 # TODO: See how it's done in wireshark:
241269 # https://github.com/wireshark/wireshark/blob/93c60b3b7c801dddd11d8c7f2a0ea4b7d02d700a/epan/dissectors/packet-ieee802154.c#L2061 # noqa: E501
@@ -268,7 +296,7 @@ class Dot15d4Beacon(Packet):
268296 dot15d4AddressField ("src_addr" , None , length_of = "fcf_srcaddrmode" ),
269297 # Security field present if fcf_security == True
270298 ConditionalField (PacketField ("aux_sec_header" , Dot15d4AuxSecurityHeader (), Dot15d4AuxSecurityHeader ), # noqa: E501
271- lambda pkt :pkt .underlayer .getfieldval ("fcf_security" ) is True ), # noqa: E501
299+ lambda pkt :pkt .underlayer .getfieldval ("fcf_security" )), # noqa: E501
272300
273301 # Superframe spec field:
274302 BitField ("sf_sforder" , 15 , 4 ), # not used by ZigBee
@@ -303,12 +331,137 @@ class Dot15d4Beacon(Packet):
303331 FieldListField ("pa_long_addresses" , [],
304332 dot15d4AddressField ("" , 0 , adjust = lambda pkt , x : 8 ),
305333 count_from = lambda pkt : pkt .pa_num_long ),
306- # TODO beacon payload
334+ # Secured Payload (variable length)
335+ ConditionalField (
336+ XStrField ("sec_payload" , "" ),
337+ lambda pkt :pkt .underlayer .getfieldval ("fcf_security" ),
338+ ),
339+ # Message Integrity Code (variable length)
340+ ConditionalField (
341+ XStrField ("mic" , "" ),
342+ lambda pkt :pkt .underlayer .getfieldval ("fcf_security" ),
343+ ),
307344 ]
308345
346+ def post_dissect (self , s ):
347+ if self .underlayer .getfieldval ("fcf_security" ):
348+ if self .aux_sec_header .sec_sc_seclevel in {1 , 5 }:
349+ mic_length = 4
350+ elif self .aux_sec_header .sec_sc_seclevel in {2 , 6 }:
351+ mic_length = 8
352+ elif self .aux_sec_header .sec_sc_seclevel in {3 , 7 }:
353+ mic_length = 16
354+ else :
355+ mic_length = 0
356+ self .sec_payload = bytes (self .aux_sec_header .payload )
357+ self .aux_sec_header .remove_payload ()
358+ if mic_length > 0 and mic_length <= len (self .sec_payload ):
359+ self .mic = self .sec_payload [- mic_length :]
360+ self .sec_payload = self .sec_payload [:- mic_length ]
361+ i = 0
362+ if len (self .sec_payload ) > i :
363+ if isinstance (self .sec_payload [i ], str ):
364+ tmp_val = struct .unpack (">B" , self .sec_payload [i ])[0 ]
365+ else :
366+ tmp_val = self .sec_payload [i ]
367+ self .sf_sforder = (tmp_val >> 4 ) & 0b1111
368+ self .sf_beaconorder = tmp_val & 0b1111
369+ i += 1
370+ if len (self .sec_payload ) > i :
371+ if isinstance (self .sec_payload [i ], str ):
372+ tmp_val = struct .unpack (">B" , self .sec_payload [i ])[0 ]
373+ else :
374+ tmp_val = self .sec_payload [i ]
375+ self .sf_assocpermit = (tmp_val >> 7 ) & 0b1
376+ self .sf_pancoord = (tmp_val >> 6 ) & 0b1
377+ self .sf_reserved = (tmp_val >> 5 ) & 0b1
378+ self .sf_battlifeextend = (tmp_val >> 4 ) & 0b1
379+ self .sf_finalcapslot = tmp_val & 0b1111
380+ i += 1
381+ if len (self .sec_payload ) > i :
382+ if isinstance (self .sec_payload [i ], str ):
383+ tmp_val = struct .unpack (">B" , self .sec_payload [i ])[0 ]
384+ else :
385+ tmp_val = self .sec_payload [i ]
386+ self .gts_spec_permit = (tmp_val >> 7 ) & 0b1
387+ self .gts_spec_reserved = (tmp_val >> 3 ) & 0b1111
388+ self .gts_spec_desccount = tmp_val & 0b111
389+ i += 1
390+ if self .gts_spec_desccount != 0 and len (self .sec_payload ) > i :
391+ if isinstance (self .sec_payload [i ], str ):
392+ tmp_val = struct .unpack (">B" , self .sec_payload [i ])[0 ]
393+ else :
394+ tmp_val = self .sec_payload [i ]
395+ self .gts_dir_reserved = (tmp_val >> 7 ) & 0b1
396+ self .gts_dir_mask = tmp_val & 0b1111111
397+ i += 1
398+ # TODO: Update the GTS List field
399+ if len (self .sec_payload ) > i :
400+ if isinstance (self .sec_payload [i ], str ):
401+ tmp_val = struct .unpack (">B" , self .sec_payload [i ])[0 ]
402+ else :
403+ tmp_val = self .sec_payload [i ]
404+ self .pa_reserved_1 = (tmp_val >> 7 ) & 0b1
405+ self .pa_num_long = (tmp_val >> 4 ) & 0b111
406+ self .pa_reserved_2 = (tmp_val >> 3 ) & 0b1
407+ self .pa_num_short = tmp_val & 0b111
408+ i += 1
409+ for _ in range (self .pa_num_short ):
410+ if len (self .sec_payload ) > i + 1 :
411+ if isinstance (self .sec_payload [i ], str ):
412+ tmp_val = (
413+ struct .unpack ("<H" , self .sec_payload [i :i + 2 ])[0 ]
414+ )
415+ else :
416+ tmp_val = (
417+ self .sec_payload [i ] +
418+ (self .sec_payload [i + 1 ] << 8 )
419+ )
420+ self .pa_short_addresses .append (tmp_val )
421+ i += 2
422+ for _ in range (self .pa_num_long ):
423+ if len (self .sec_payload ) > i + 7 :
424+ if isinstance (self .sec_payload [i ], str ):
425+ tmp_val = (
426+ struct .unpack ("<Q" , self .sec_payload [i :i + 8 ])[0 ]
427+ )
428+ else :
429+ tmp_val = (
430+ self .sec_payload [i ] +
431+ (self .sec_payload [i + 1 ] << 8 ) +
432+ (self .sec_payload [i + 2 ] << 16 ) +
433+ (self .sec_payload [i + 3 ] << 24 ) +
434+ (self .sec_payload [i + 4 ] << 32 ) +
435+ (self .sec_payload [i + 5 ] << 40 ) +
436+ (self .sec_payload [i + 6 ] << 48 ) +
437+ (self .sec_payload [i + 7 ] << 56 )
438+ )
439+ self .pa_long_addresses .append (tmp_val )
440+ i += 8
441+ self .sec_payload = self .sec_payload [i :]
442+ return s
443+
309444 def mysummary (self ):
310445 return self .sprintf ("802.15.4 Beacon ( %Dot15d4Beacon.src_panid%:%Dot15d4Beacon.src_addr% ) assocPermit(%Dot15d4Beacon.sf_assocpermit%) panCoord(%Dot15d4Beacon.sf_pancoord%)" ) # noqa: E501
311446
447+ def guess_payload_class (self , payload ):
448+ from scapy .layers .sixlowpan import ThreadBeacon
449+ from scapy .layers .zigbee import ZigBeeBeacon
450+ if len (payload ) > 0 :
451+ if (
452+ (isinstance (payload [0 ], int ) and payload [0 ] == 0x03 ) or
453+ (isinstance (payload [0 ], bytes ) and payload [0 ] == b'\x03 ' )
454+ ):
455+ # https://gitlab.com/wireshark/wireshark/-/blob/5ecb57cb9026cebf0cfa4918c4a86942620c5ecf/epan/dissectors/packet-thread.c#L2223-2225 # noqa: E501
456+ return ThreadBeacon
457+ elif (
458+ (isinstance (payload [0 ], int ) and payload [0 ] == 0x00 ) or
459+ (isinstance (payload [0 ], bytes ) and payload [0 ] == b'\x00 ' )
460+ ):
461+ # https://gitlab.com/wireshark/wireshark/-/blob/5ecb57cb9026cebf0cfa4918c4a86942620c5ecf/epan/dissectors/packet-zbee-nwk.c#L1507-1509 # noqa: E501
462+ return ZigBeeBeacon
463+ return Packet .guess_payload_class (self , payload )
464+
312465
313466class Dot15d4Cmd (Packet ):
314467 name = "802.15.4 Command"
@@ -323,7 +476,7 @@ class Dot15d4Cmd(Packet):
323476 lambda pkt :pkt .underlayer .getfieldval ("fcf_srcaddrmode" ) != 0 ), # noqa: E501
324477 # Security field present if fcf_security == True
325478 ConditionalField (PacketField ("aux_sec_header" , Dot15d4AuxSecurityHeader (), Dot15d4AuxSecurityHeader ), # noqa: E501
326- lambda pkt :pkt .underlayer .getfieldval ("fcf_security" ) is True ), # noqa: E501
479+ lambda pkt :pkt .underlayer .getfieldval ("fcf_security" )), # noqa: E501
327480 ByteEnumField ("cmd_id" , 0 , {
328481 1 : "AssocReq" , # Association request
329482 2 : "AssocResp" , # Association response
@@ -336,9 +489,41 @@ class Dot15d4Cmd(Packet):
336489 9 : "GTSReq" # GTS request
337490 # 0x0a - 0xff reserved
338491 }),
339- # TODO command payload
492+ # Secured Payload (variable length)
493+ ConditionalField (
494+ XStrField ("sec_payload" , "" ),
495+ lambda pkt : pkt .underlayer .getfieldval ("fcf_security" ),
496+ ),
497+ # Message Integrity Code (variable length)
498+ ConditionalField (
499+ XStrField ("mic" , "" ),
500+ lambda pkt : pkt .underlayer .getfieldval ("fcf_security" ),
501+ ),
340502 ]
341503
504+ def post_dissect (self , s ):
505+ if self .underlayer .getfieldval ("fcf_security" ):
506+ if self .aux_sec_header .sec_sc_seclevel in {1 , 5 }:
507+ mic_length = 4
508+ elif self .aux_sec_header .sec_sc_seclevel in {2 , 6 }:
509+ mic_length = 8
510+ elif self .aux_sec_header .sec_sc_seclevel in {3 , 7 }:
511+ mic_length = 16
512+ else :
513+ mic_length = 0
514+ self .sec_payload = bytes (self .aux_sec_header .payload )
515+ self .aux_sec_header .remove_payload ()
516+ if mic_length > 0 and mic_length <= len (self .sec_payload ):
517+ self .mic = self .sec_payload [- mic_length :]
518+ self .sec_payload = self .sec_payload [:- mic_length ]
519+ if len (self .sec_payload ) > 0 :
520+ if isinstance (self .sec_payload [0 ], str ):
521+ self .cmd_id = struct .unpack (">B" , self .sec_payload [0 ])[0 ]
522+ else :
523+ self .cmd_id = self .sec_payload [0 ]
524+ self .sec_payload = self .sec_payload [1 :]
525+ return s
526+
342527 def mysummary (self ):
343528 return self .sprintf ("802.15.4 Command %Dot15d4Cmd.cmd_id% ( %Dot15dCmd.src_panid%:%Dot15d4Cmd.src_addr% -> %Dot15d4Cmd.dest_panid%:%Dot15d4Cmd.dest_addr% )" ) # noqa: E501
344529
0 commit comments