4
4
# Copyright (C) Ryan Speers <[email protected] > 2011-2012
5
5
# Copyright (C) Roger Meyer <[email protected] >: 2012-03-10 Added frames
6
6
# 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] >
8
8
# This program is published under a GPLv2 license
9
9
10
10
"""
36
36
XByteField ,
37
37
XLEIntField ,
38
38
XLEShortField ,
39
+ XStrField ,
39
40
)
40
41
41
42
# Fields #
@@ -233,9 +234,36 @@ class Dot15d4Data(Packet):
233
234
lambda pkt :pkt .underlayer .getfieldval ("fcf_srcaddrmode" ) != 0 ), # noqa: E501
234
235
# Security field present if fcf_security == True
235
236
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
+ ),
237
248
]
238
249
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
+
239
267
def guess_payload_class (self , payload ):
240
268
# TODO: See how it's done in wireshark:
241
269
# https://github.com/wireshark/wireshark/blob/93c60b3b7c801dddd11d8c7f2a0ea4b7d02d700a/epan/dissectors/packet-ieee802154.c#L2061 # noqa: E501
@@ -268,7 +296,7 @@ class Dot15d4Beacon(Packet):
268
296
dot15d4AddressField ("src_addr" , None , length_of = "fcf_srcaddrmode" ),
269
297
# Security field present if fcf_security == True
270
298
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
272
300
273
301
# Superframe spec field:
274
302
BitField ("sf_sforder" , 15 , 4 ), # not used by ZigBee
@@ -303,12 +331,137 @@ class Dot15d4Beacon(Packet):
303
331
FieldListField ("pa_long_addresses" , [],
304
332
dot15d4AddressField ("" , 0 , adjust = lambda pkt , x : 8 ),
305
333
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
+ ),
307
344
]
308
345
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
+
309
444
def mysummary (self ):
310
445
return self .sprintf ("802.15.4 Beacon ( %Dot15d4Beacon.src_panid%:%Dot15d4Beacon.src_addr% ) assocPermit(%Dot15d4Beacon.sf_assocpermit%) panCoord(%Dot15d4Beacon.sf_pancoord%)" ) # noqa: E501
311
446
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
+
312
465
313
466
class Dot15d4Cmd (Packet ):
314
467
name = "802.15.4 Command"
@@ -323,7 +476,7 @@ class Dot15d4Cmd(Packet):
323
476
lambda pkt :pkt .underlayer .getfieldval ("fcf_srcaddrmode" ) != 0 ), # noqa: E501
324
477
# Security field present if fcf_security == True
325
478
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
327
480
ByteEnumField ("cmd_id" , 0 , {
328
481
1 : "AssocReq" , # Association request
329
482
2 : "AssocResp" , # Association response
@@ -336,9 +489,41 @@ class Dot15d4Cmd(Packet):
336
489
9 : "GTSReq" # GTS request
337
490
# 0x0a - 0xff reserved
338
491
}),
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
+ ),
340
502
]
341
503
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
+
342
527
def mysummary (self ):
343
528
return self .sprintf ("802.15.4 Command %Dot15d4Cmd.cmd_id% ( %Dot15dCmd.src_panid%:%Dot15d4Cmd.src_addr% -> %Dot15d4Cmd.dest_panid%:%Dot15d4Cmd.dest_addr% )" ) # noqa: E501
344
529
0 commit comments