aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-ieee802154.c
blob: 95ffd3d0f90360327d4d633104e6ac47315001d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
/* packet-ieee802154.c
 *
 * $Id$
 *
 * IEEE 802.15.4 Dissectors for Wireshark
 * By Owen Kirby <osk@exegin.com>
 * Copyright 2007 Exegin Technologies Limited
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *------------------------------------------------------------
 *
 *  In IEEE 802.15.4 packets, all fields are little endian. And
 *  Each byte is transmitted least significan bit first (reflected
 *  bit ordering).
 *------------------------------------------------------------
 *
 *  IEEE 802.15.4 Packets have the following format:
 *  |  FCF  |Seq No|  Addressing |         Data          |  FCS  |
 *  |2 bytes|1 byte|0 to 20 bytes|Length-(Overhead) bytes|2 Bytes|
 *------------------------------------------------------------
 *
 *  CRC16 is calculated using the x^16 + x^12 + x^5 + 1 polynomial
 *  as specified by ITU-T, and is calculated over the IEEE 802.15.4
 *  packet (excluding the FCS) as transmitted over the air. Note,
 *  that because the least significan bits are transmitted first, this
 *  will require reversing the bit-order in each byte. Also, unlike
 *  most CRC algorithms, IEEE 802.15.4 uses an initial and final value
 *  of 0x0000, instead of 0xffff (which is used by the CCITT).
 *------------------------------------------------------------
 *
 *  This dissector supports both link-layer IEEE 802.15.4 captures
 *  and IEEE 802.15.4 packets encapsulated within other layers.
 *  Additionally, support has been provided for various formats
 *  of the frame check sequence:
 *      - IEEE 802.15.4 compliant FCS.
 *      - ChipCon/Texas Instruments CC24xx style FCS.
 *      - No FCS at all.
 *------------------------------------------------------------
 *
 *  No support has been provided for decryption. Maybe a TODO
 *  item, but this is unlikely as the decryption process requires
 *  the extended source address (to build the nonce/initial value)
 *  which will be absent most of the time.
 *------------------------------------------------------------
 */

/*  Include files */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVEHCONFIG_H */

#include <string.h>
#include <stdlib.h>
#include <gmodule.h>
#include <glib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <epan/emem.h>
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/crc16.h>
#include <epan/expert.h>
#include <epan/addr_resolv.h>

#include "packet-ieee802154.h"
#include "packet-frame.h"   /* For Exception Handling */

/* Dissection Options for dissect_ieee802154_common */
#define DISSECT_IEEE802154_OPTION_CC24xx    0x00000001  /* FCS field contains a TI CC24xx style FCS. */
#define DISSECT_IEEE802154_OPTION_LINUX     0x00000002  /* Addressing fields are padded DLT_IEEE802_15_4_LINUX, not implemented. */

/*  Function declarations */
/* Register Functions. Loads the dissector into Wireshark. */
void proto_reg_handoff_ieee802154   (void);
void proto_register_ieee802154      (void);

/* Dissection Routines. */
static void dissect_ieee802154              (tvbuff_t *, packet_info *, proto_tree *);
static void dissect_ieee802154_nofcs        (tvbuff_t *, packet_info *, proto_tree *);
static void dissect_ieee802154_cc24xx       (tvbuff_t *, packet_info *, proto_tree *);
/*static void dissect_ieee802154_linux        (tvbuff_t *, packet_info *, proto_tree *);  TODO: Implement Me. */
static void dissect_ieee802154_common       (tvbuff_t *, packet_info *, proto_tree *, guint);
static void dissect_ieee802154_beacon       (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *);
static void dissect_ieee802154_cmd          (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *);
/* Sub-dissector helpers. */
static void dissect_ieee802154_fcf          (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *, guint *);
static void dissect_ieee802154_cmd_asreq    (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *, guint *);
static void dissect_ieee802154_cmd_asrsp    (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *, guint *);
static void dissect_ieee802154_cmd_disas    (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *, guint *);
static void dissect_ieee802154_cmd_realign  (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *, guint *);
static void dissect_ieee802154_cmd_gtsrq    (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *, guint *);

/*  Initialize Protocol and Registered fields */
static int proto_ieee802154 = -1;
static int hf_ieee802154_frame_type = -1;
static int hf_ieee802154_security = -1;
static int hf_ieee802154_pending = -1;
static int hf_ieee802154_ack_request = -1;
static int hf_ieee802154_intra_pan = -1;
static int hf_ieee802154_seqno = -1;
static int hf_ieee802154_src_addr_mode = -1;
static int hf_ieee802154_dst_addr_mode = -1;
static int hf_ieee802154_version = -1;
static int hf_ieee802154_dst_pan = -1;
static int hf_ieee802154_dst_addr16 = -1;
static int hf_ieee802154_dst_addr64 = -1;
static int hf_ieee802154_src_panID = -1;
static int hf_ieee802154_src_addr16 = -1;
static int hf_ieee802154_src_addr64 = -1;
static int hf_ieee802154_fcs = -1;
static int hf_ieee802154_rssi = -1;
static int hf_ieee802154_fcs_ok = -1;
static int hf_ieee802154_correlation;

/*  Registered fields for Command Packets */
static int hf_ieee802154_cmd_id = -1;
static int hf_ieee802154_cmd_cinfo_alt_coord = -1;
static int hf_ieee802154_cmd_cinfo_device_type = -1;
static int hf_ieee802154_cmd_cinfo_power_src = -1;
static int hf_ieee802154_cmd_cinfo_idle_rx = -1;
static int hf_ieee802154_cmd_cinfo_sec_capable = -1;
static int hf_ieee802154_cmd_cinfo_alloc_addr = -1;
static int hf_ieee802154_cmd_asrsp_addr = -1;
static int hf_ieee802154_cmd_asrsp_status = -1;
static int hf_ieee802154_cmd_disas_reason = -1;
static int hf_ieee802154_cmd_coord_pan = -1;
static int hf_ieee802154_cmd_coord_caddr = -1;
static int hf_ieee802154_cmd_coord_channel = -1;
static int hf_ieee802154_cmd_coord_addr = -1;
static int hf_ieee802154_cmd_coord_channel_page = -1;
static int hf_ieee802154_cmd_gts_req_len = -1;
static int hf_ieee802154_cmd_gts_req_dir = -1;
static int hf_ieee802154_cmd_gts_req_type = -1;

/*  Registered fields for Beacon Packets */
static int hf_ieee802154_bcn_beacon_order = -1;
static int hf_ieee802154_bcn_superframe_order = -1;
static int hf_ieee802154_bcn_cap = -1;
static int hf_ieee802154_bcn_battery_ext = -1;
static int hf_ieee802154_bcn_coord = -1;
static int hf_ieee802154_bcn_assoc_permit = -1;
static int hf_ieee802154_bcn_gts_count = -1;
static int hf_ieee802154_bcn_gts_permit = -1;
static int hf_ieee802154_bcn_gts_direction = -1;
static int hf_ieee802154_bcn_pending16 = -1;
static int hf_ieee802154_bcn_pending64 = -1;

/*  Initialize Subtree Pointers */
static gint ett_ieee802154 = -1;
static gint ett_ieee802154_fcf = -1;
static gint ett_ieee802154_fcs = -1;
static gint ett_ieee802154_cmd = -1;
static gint ett_ieee802154_cmd_cinfo = -1;
static gint ett_ieee802154_bcn = -1;
static gint ett_ieee802154_bcn_superframe_spec = -1;
static gint ett_ieee802154_bcn_gts_spec = -1;
static gint ett_ieee802154_bcn_gts_direction = -1;
static gint ett_ieee802154_bcn_gts_descriptors = -1;
static gint ett_ieee802154_bcn_pending = -1;

/*  Dissector handles */
static dissector_handle_t       data_handle;
static heur_dissector_list_t    ieee802154_heur_subdissector_list;

/* Name Strings */
static const value_string ieee802154_frame_types[] = {
    { IEEE802154_FCF_BEACON,    "Beacon" },
    { IEEE802154_FCF_DATA,      "Data" },
    { IEEE802154_FCF_ACK,       "Ack" },
    { IEEE802154_FCF_CMD,       "Command" },
    { 0, NULL }
};

static const value_string ieee802154_addr_modes[] = {
    { IEEE802154_FCF_ADDR_NONE,     "None" },
    { IEEE802154_FCF_ADDR_SHORT,    "Short/16-bit" },
    { IEEE802154_FCF_ADDR_EXT,      "Long/64-bit" },
    { 0, NULL }
};

static const value_string ieee802154_cmd_names[] = {
    { IEEE802154_CMD_ASRQ,      "Association Request" },
    { IEEE802154_CMD_ASRSP,     "Association Response" },
    { IEEE802154_CMD_DISAS,     "Disassociation Notification" },
    { IEEE802154_CMD_DATA_RQ,   "Data Request" },
    { IEEE802154_CMD_PANID_ERR, "PAN ID Conflict" },
    { IEEE802154_CMD_ORPH_NOTIF,"Orphan Notification" },
    { IEEE802154_CMD_BCN_RQ,    "Beacon Request" },
    { IEEE802154_CMD_COORD_REAL,"Coordinator Realignment" },
    { IEEE802154_CMD_GTS_REQ,   "GTS Request" },
    { 0, NULL }
};

/* CRC definitions. IEEE 802.15.4 CRCs vary from CCITT by using an initial value of
 * 0x0000, and no XOR out. IEEE802154_CRC_XOR is defined as 0xFFFF in order to un-XOR
 * the output from the CCITT CRC routines in Wireshark.
 */
#define IEEE802154_CRC_SEED     0x0000
#define IEEE802154_CRC_XOROUT   0xFFFF
#define ieee802154_crc_tvb(tvb, offset)   (crc16_ccitt_tvb_seed(tvb, offset, IEEE802154_CRC_SEED) ^ IEEE802154_CRC_XOROUT)

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      get_by_mask
 *  DESCRIPTION
 *      Extracts an integer sub-field from an int with a given mask
 *      if the mask is 0, this will return 0, if the mask is non-
 *      continuos the output is undefined.
 *  PARAMETERS
 *      guint       input
 *      guint       mask
 *  RETURNS
 *      guint
 *---------------------------------------------------------------
 */
guint
get_by_mask(guint input, guint mask)
{
    /* Sanity Check, don't want infinite loops. */
    if (mask == 0) return 0;
    /* Shift input and mask together. */
    while (!(mask & 0x1)) {
        input >>= 1;
        mask >>=1;
    } /* while */
    return (input & mask);
} /* get_by_mask */

#define EUI64_STRLEN    (3*(sizeof(guint64)+1))
/*FUNCTION:------------------------------------------------------
 *  NAME
 *      print_eui64
 *  DESCRIPTION
 *      Prints an EUI-64 address in a string. Does not attempt to
 *      resolve the OUI value.
 *
 *  PARAMETERS
 *      guint64 addr
 *  RETURNS
 *      gchar*
 *---------------------------------------------------------------
 */
gchar *
print_eui64(guint64 addr)
{
    address         eui64addr;

    /* Endian-swap the address to put it into network order. */
    addr = pntoh64(&addr);
    /* Fill in the address struct. */
    eui64addr.type = AT_EUI64;
    eui64addr.len = sizeof(guint64);
    eui64addr.data = &addr;
    /* Print the address. */
    return address_to_str(&eui64addr);
} /* print_eui64 */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      print_eui64_oui
 *  DESCRIPTION
 *      Prints an EUI-64 address in a string. Attempts to lookup
 *      the vendor name from the OUI,
 *
 *  PARAMETERS
 *      guint64 addr
 *  RETURNS
 *      gchar*
 *---------------------------------------------------------------
 */
gchar *
print_eui64_oui(guint64 addr)
{
    const gchar     *manuf_name;
    address         eui64addr;

    /* Endian-swap the address to put it into network order. */
    addr = pntoh64(&addr);
    /* Fill in the address struct. */
    eui64addr.type = AT_EUI64;
    eui64addr.len = sizeof(guint64);
    eui64addr.data = &addr;
    /* Attempt an OUI lookup. */
    manuf_name = get_manuf_name_if_known(eui64addr.data);
    if (manuf_name == NULL) {
        /* Could not find an OUI. */
        return address_to_str(&eui64addr);
    }
    else {
        /* Found an address string. */
        gchar       *output_str = ep_alloc(64);
        g_snprintf(output_str, 64, "%s_%02x:%02x:%02x:%02x:%02x", manuf_name,
            ((guint8 *)(eui64addr.data))[3], ((guint8 *)(eui64addr.data))[4],
            ((guint8 *)(eui64addr.data))[5], ((guint8 *)(eui64addr.data))[6],
            ((guint8 *)(eui64addr.data))[7]);
        return output_str;
    }
} /* print_eui64_oui */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_ieee802154_fcf
 *  DESCRIPTION
 *      Dissector helper, parses and displays the frame control
 *      field.
 *
 *  PARAMETERS
 *      ieee802154_packet   *packet - Packet info structure.
 *      tvbuff_t    *tvb    - pointer to buffer containing raw packet.
 *      packet_info *pinfo  - pointer to packet information fields
 *      proto_tree  *tree   - pointer to data tree ethereal uses to display packet.
 *      ieee802154_packet *packet   - IEEE 802.15.4 packet information.
 *      guint       offset  - offset into the tvb to find the FCF.
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
dissect_ieee802154_fcf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ieee802154_packet *packet, guint *offset)
{
    guint16         fcf;
    proto_tree      *field_tree;
    proto_item      *ti;

    /* Get the FCF field. */
    fcf = tvb_get_letohs(tvb, *offset);

     /* Parse FCF Flags. */
    packet->frame_type      = get_by_mask(fcf, IEEE802154_FCF_TYPE_MASK);
    packet->security_enable = get_by_mask(fcf, IEEE802154_FCF_SEC_EN);
    packet->frame_pending   = get_by_mask(fcf, IEEE802154_FCF_FRAME_PND);
    packet->ack_request     = get_by_mask(fcf, IEEE802154_FCF_ACK_REQ);
    packet->intra_pan       = get_by_mask(fcf, IEEE802154_FCF_INTRA_PAN);
    packet->version         = get_by_mask(fcf, IEEE802154_FCF_VERSION);
    packet->dst_addr_mode   = get_by_mask(fcf, IEEE802154_FCF_DADDR_MASK);
    packet->src_addr_mode   = get_by_mask(fcf, IEEE802154_FCF_SADDR_MASK);

    /* Display the frame type. */
    if (tree) proto_item_append_text(tree, " %s", val_to_str(packet->frame_type, ieee802154_frame_types, "Reserved"));
    if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, val_to_str(packet->frame_type, ieee802154_frame_types, "Reserved"));

    /* Add the FCF to the protocol tree. */
    if (tree) {
        /*  Create the FCF subtree. */
        ti = proto_tree_add_text(tree, tvb, *offset, sizeof(guint16), "Frame Control Field: %s (0x%04x)",
                val_to_str(packet->frame_type, ieee802154_frame_types, "Unknown"), fcf);
        field_tree = proto_item_add_subtree(ti, ett_ieee802154_fcf);

        /* FCF Fields. */
        proto_tree_add_uint(field_tree, hf_ieee802154_frame_type, tvb, *offset, sizeof(guint8), fcf & IEEE802154_FCF_TYPE_MASK);
        proto_tree_add_boolean(field_tree, hf_ieee802154_security, tvb, *offset, sizeof(guint8), fcf & IEEE802154_FCF_SEC_EN);
        proto_tree_add_boolean(field_tree, hf_ieee802154_pending, tvb, *offset, sizeof(guint8), fcf & IEEE802154_FCF_FRAME_PND);
        proto_tree_add_boolean(field_tree, hf_ieee802154_ack_request, tvb, *offset, sizeof(guint8), fcf & IEEE802154_FCF_ACK_REQ);
        proto_tree_add_boolean(field_tree, hf_ieee802154_intra_pan, tvb, *offset, sizeof(guint8), fcf & IEEE802154_FCF_INTRA_PAN);
        proto_tree_add_uint(field_tree, hf_ieee802154_dst_addr_mode, tvb, (*offset)+sizeof(guint8), sizeof(guint8), fcf & IEEE802154_FCF_DADDR_MASK);
        proto_tree_add_uint(field_tree, hf_ieee802154_version, tvb, (*offset)+sizeof(guint8), sizeof(guint8), fcf & IEEE802154_FCF_VERSION);
        proto_tree_add_uint(field_tree, hf_ieee802154_src_addr_mode, tvb, (*offset)+sizeof(guint8), sizeof(guint8), fcf & IEEE802154_FCF_SADDR_MASK);
    }

    *offset += sizeof(guint16);
} /* dissect_ieee802154_fcf */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_ieee802154
 *  DESCRIPTION
 *      Dissector for IEEE 802.15.4 packet with an FCS containing
 *      a 16-bit CRC value.
 *
 *  PARAMETERS
 *      tvbuff_t *tvb       - pointer to buffer containing raw packet.
 *      packet_info *pinfo  - pointer to packet information fields
 *      proto_tree *tree    - pointer to data tree ethereal uses to display packet.
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
dissect_ieee802154(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    /* Call the common dissector. */
    dissect_ieee802154_common(tvb, pinfo, tree, 0);
} /* dissect_ieee802154 */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_ieee802154_nofcs
 *  DESCRIPTION
 *      Dissector for IEEE 802.15.4 packet with no FCS present.
 *
 *  PARAMETERS
 *      tvbuff_t *tvb       - pointer to buffer containing raw packet.
 *      packet_info *pinfo  - pointer to packet information fields
 *      proto_tree *tree    - pointer to data tree ethereal uses to display packet.
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
dissect_ieee802154_nofcs(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    tvbuff_t    *new_tvb;
    /* If there is no FCS present in the reported packet, then the length of
     * the true IEEE 802.15.4 packet is actually 2 bytes longer. Re-create
     * the buffer with an extended reported length so that the packet will
     * be handled as though the FCS were truncated.
     *
     * Note, we can't just call tvb_set_reported_length(), because it includes
     * checks to ensure that the new reported length is not longer than the old
     * reported length (why?), and will throw an exception.
     */
    new_tvb = tvb_new_subset(tvb, 0, -1, tvb_reported_length(tvb)+IEEE802154_FCS_LEN);
    /* Call the common dissector. */
    dissect_ieee802154_common(new_tvb, pinfo, tree, 0);
} /* dissect_ieee802154_nofcs */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_ieee802154_cc24xx
 *  DESCRIPTION
 *      Dissector for IEEE 802.15.4 packet with a ChipCon/Texas
 *      Instruments compatible FCS. This is typically called by
 *      layers encapsulating an IEEE 802.15.4 packet.
 *
 *  PARAMETERS
 *      tvbuff_t *tvb       - pointer to buffer containing raw packet.
 *      packet_info *pinfo  - pointer to packet information fields
 *      proto_tree *tree    - pointer to data tree ethereal uses to display packet.
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
dissect_ieee802154_cc24xx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    /* Call the common dissector. */
    dissect_ieee802154_common(tvb, pinfo, tree, DISSECT_IEEE802154_OPTION_CC24xx);
} /* dissect_ieee802154_cc24xx */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_ieee802154_common
 *  DESCRIPTION
 *      IEEE 802.15.4 packet dissection routine for Ethereal.
 *      This function extracts all the information first before displaying.
 *      If payload exists, that portion will be passed into another dissector
 *      for further processing.
 *
 *      This is called after the individual dissect_ieee802154* functions
 *      have been called to determine what sort of FCS is present.
 *      The dissect_ieee802154* functions will set the parameters
 *      in the ieee802154_packet structure, and pass it to this one
 *      through the pinfo->private_data pointer.
 *
 *  PARAMETERS
 *      tvbuff_t *tvb       - pointer to buffer containing raw packet.
 *      packet_info *pinfo  - pointer to packet information fields
 *      proto_tree *tree    - pointer to data tree ethereal uses to display packet.
 *      guint options       - bitwise or of dissector options (see DISSECT_IEEE802154_OPTION_xxx).
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
dissect_ieee802154_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint options)
{
    tvbuff_t            *payload_tvb;
    proto_tree          *volatile ieee802154_tree = NULL;
    proto_item          *volatile proto_root = NULL;
    proto_item          *ti;

    guint               offset = 0;
    gboolean            fcs_ok = TRUE;
    const char          *saved_proto;
    ieee802154_packet   *packet = ep_alloc(sizeof(ieee802154_packet));

    /* Link our packet info structure into the private data field for the
     * Network-Layer heuristic subdissectors. */
    pinfo->private_data = packet;

    /* Create the protocol tree. */
    if (tree) {
        proto_root = proto_tree_add_protocol_format(tree, proto_ieee802154, tvb, 0, tvb_length(tvb), "IEEE 802.15.4");
        ieee802154_tree = proto_item_add_subtree(proto_root, ett_ieee802154);
    }
    /* Add the protocol name. */
    if(check_col(pinfo->cinfo, COL_PROTOCOL)){
        col_set_str(pinfo->cinfo, COL_PROTOCOL, "IEEE 802.15.4");
    }
    /* Add the packet length. */
    if(check_col(pinfo->cinfo, COL_PACKET_LENGTH)){
        col_clear(pinfo->cinfo, COL_PACKET_LENGTH);
        col_add_fstr(pinfo->cinfo, COL_PACKET_LENGTH, "%i", tvb_length(tvb));
    }

    /*=====================================================
     * FRAME CONTROL FIELD
     *=====================================================
     */
    dissect_ieee802154_fcf(tvb, pinfo, ieee802154_tree, packet, &offset);

    /*=====================================================
     * SEQUENCE NUMBER
     *=====================================================
     */
    packet->seqno = tvb_get_guint8(tvb, offset);
    if (tree) {
        proto_tree_add_uint(ieee802154_tree, hf_ieee802154_seqno, tvb, offset, 1, packet->seqno);
        /* For Ack packets display this in the root. */
        if (packet->frame_type == IEEE802154_FCF_ACK) {
            proto_item_append_text(proto_root, ", Sequence Number: %u", packet->seqno);
        }
    }
    offset += sizeof(guint8);

    /*=====================================================
     * ADDRESSING FIELDS
     *=====================================================
     */
    /* Clear out the addressing strings. */
    SET_ADDRESS(&pinfo->dst, AT_NONE, 0, NULL);
    SET_ADDRESS(&pinfo->src, AT_NONE, 0, NULL);
    SET_ADDRESS(&pinfo->dl_dst, AT_NONE, 0, NULL);
    SET_ADDRESS(&pinfo->dl_src, AT_NONE, 0, NULL);
    SET_ADDRESS(&pinfo->net_dst, AT_NONE, 0, NULL);
    SET_ADDRESS(&pinfo->net_src, AT_NONE, 0, NULL);

    /* Get and display the destination PAN, if present. */
    if ( (packet->dst_addr_mode == IEEE802154_FCF_ADDR_SHORT) ||
         (packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT) ) {
        packet->dst_pan = tvb_get_letohs(tvb, offset);
        if (tree) {
            proto_tree_add_uint(ieee802154_tree, hf_ieee802154_dst_pan, tvb, offset, sizeof(guint16), packet->dst_pan);
        }
        offset += sizeof(guint16);
    }

    /* Get destination address. */
    if (packet->dst_addr_mode == IEEE802154_FCF_ADDR_SHORT) {
        /* Dynamic (not stack) memory required for address column. */
        gchar   *dst_addr = ep_alloc(32);

        /* Get the address. */
        packet->dst_addr16 = tvb_get_letohs(tvb, offset);

        /* Display the destination address. */
        if(packet->dst_addr16==IEEE802154_BCAST_ADDR) g_snprintf(dst_addr, 32, "Broadcast");
        else g_snprintf(dst_addr, 32, "0x%04x", packet->dst_addr16);
        SET_ADDRESS(&pinfo->dl_dst, AT_STRINGZ, strlen(dst_addr)+1, dst_addr);
        SET_ADDRESS(&pinfo->dst, AT_STRINGZ, strlen(dst_addr)+1, dst_addr);
        if (tree) {
            proto_tree_add_uint(ieee802154_tree, hf_ieee802154_dst_addr16, tvb, offset, sizeof(guint16), packet->dst_addr16);
            proto_item_append_text(proto_root, ", Dst: %s", dst_addr);
        }
        if (check_col(pinfo->cinfo, COL_INFO)) {
            col_append_fstr(pinfo->cinfo, COL_INFO, ", Dst: %s", dst_addr);
        }
        offset += sizeof(guint16);
    }
    else if (packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT) {
        /* Dynamic (not stack) memory required for address column. */
        gchar    *addr = ep_alloc(sizeof(guint64));
        gchar    *dst, *dst_oui;

        /* Get the address */
        packet->dst_addr64 = tvb_get_letoh64(tvb, offset);

        /* print the address strings. */
        dst = print_eui64(packet->dst_addr64);
        dst_oui = print_eui64_oui(packet->dst_addr64);

        /* Copy and convert the address to network byte order. */
        *(guint64 *)(addr) = pntoh64(&(packet->dst_addr64));

        /* Display the destination address. */
        /* NOTE: OUI resolution doesn't happen when displaying EUI64 addresses
         *          might want to switch to AT_STRINZ type to display the OUI in
         *          the address columns.
         */
        SET_ADDRESS(&pinfo->dl_dst, AT_EUI64, sizeof(guint64), addr);
        SET_ADDRESS(&pinfo->dst, AT_EUI64, sizeof(guint64), addr);
        if (tree) {
            proto_tree_add_uint64_format_value(ieee802154_tree, hf_ieee802154_dst_addr64, tvb, offset, sizeof(guint64), packet->dst_addr64, "%s (%s)", dst_oui, dst);
            proto_item_append_text(proto_root, ", Dst: %s", dst_oui);
        }
        if (check_col(pinfo->cinfo, COL_INFO)) {
            col_append_fstr(pinfo->cinfo, COL_INFO, ", Dst: %s", dst_oui);
        }
        offset += sizeof(guint64);
    }
    else if (packet->dst_addr_mode != IEEE802154_FCF_ADDR_NONE) {
        /* Invalid Destination Address Mode. Abort Dissection. */
        expert_add_info_format(pinfo, proto_root, PI_MALFORMED, PI_ERROR, "Invalid Destination Address Mode");
        return;
    }

    /* Get the source PAN if it exists. The source address will be present if:
     *  - The Source addressing exists and
     *  - The Destination addressing doesn't exist, or the Intra-PAN bit is unset.
     */
    if ( ((packet->src_addr_mode == IEEE802154_FCF_ADDR_SHORT) || (packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT)) &&
         ((packet->dst_addr_mode == IEEE802154_FCF_ADDR_NONE) || (!packet->intra_pan)) ) {
        /* Source PAN is present, extract it and add it to the tree. */
        packet->src_pan = tvb_get_letohs(tvb, offset);
        if (tree) {
            proto_tree_add_uint(ieee802154_tree, hf_ieee802154_src_panID, tvb, offset, sizeof(guint16), packet->src_pan);
        }
        offset += sizeof(guint16);
    }
    else {
        /* Set the panID field in case the intra-pan condition was met. */
        packet->src_pan = packet->dst_pan;
    }

    /* Get source address if present. */
    if (packet->src_addr_mode == IEEE802154_FCF_ADDR_SHORT) {
        /* Dynamic (not stack) memory required for address column. */
        gchar   *src_addr = ep_alloc(32);

        /* Get the address. */
        packet->src_addr16 = tvb_get_letohs(tvb, offset);

        /* Update the Address fields. */
        if(packet->src_addr16==IEEE802154_BCAST_ADDR) g_snprintf(src_addr, 32, "Broadcast");
        else g_snprintf(src_addr, 32, "0x%04x", packet->src_addr16);
        SET_ADDRESS(&pinfo->dl_src, AT_STRINGZ, strlen(src_addr)+1, src_addr);
        SET_ADDRESS(&pinfo->src, AT_STRINGZ, strlen(src_addr)+1, src_addr);

        /* Add the addressing info to the tree. */
        if (tree) {
            proto_tree_add_uint(ieee802154_tree, hf_ieee802154_src_addr16, tvb, offset, sizeof(guint16), packet->src_addr16);
            proto_item_append_text(proto_root, ", Src: %s", src_addr);
        }
        if (check_col(pinfo->cinfo, COL_INFO)) {
            col_append_fstr(pinfo->cinfo, COL_INFO, ", Src: %s", src_addr);
        }
        offset += sizeof(guint16);
    }
    else if (packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) {
        /* Dynamic (not stack) memory required for address column. */
        gchar   *addr = ep_alloc(sizeof(guint64));
        gchar   *src, *src_oui;

        /* Get the address. */
        packet->src_addr64 = tvb_get_letoh64(tvb, offset);

        /* Print the address strings. */
        src = print_eui64(packet->src_addr64);
        src_oui = print_eui64_oui(packet->src_addr64);

        /* Copy and convert the address to network byte order. */
        *(guint64 *)(addr) = pntoh64(&(packet->src_addr64));

        /* Display the source address. */
        /* NOTE: OUI resolution doesn't happen when displaying EUI64 addresses
         *          might want to switch to AT_STRINZ type to display the OUI in
         *          the address columns.
         */
        SET_ADDRESS(&pinfo->dl_src, AT_EUI64, sizeof(guint64), addr);
        SET_ADDRESS(&pinfo->src, AT_EUI64, sizeof(guint64), addr);
        if (tree) {
            proto_tree_add_uint64_format_value(ieee802154_tree, hf_ieee802154_src_addr64, tvb, offset, sizeof(guint64), packet->src_addr64, "%s (%s)", src_oui, src);
            proto_item_append_text(proto_root, ", Src: %s", src_oui);
        }
        if (check_col(pinfo->cinfo, COL_INFO)) {
            col_append_fstr(pinfo->cinfo, COL_INFO, ", Src: %s", src_oui);
        }
        offset += sizeof(guint64);
    }
    else if (packet->src_addr_mode != IEEE802154_FCF_ADDR_NONE) {
        /* Invalid Destination Address Mode. Abort Dissection. */
        expert_add_info_format(pinfo, proto_root, PI_MALFORMED, PI_ERROR, "Invalid Source Address Mode");
        return;
    }

    /*=====================================================
     * FRAME CHECK SEQUENCE VERIFICATION
     *=====================================================
     */
    /* Check, but don't display the FCS yet, otherwise the payload dissection
     * may be out of place in the tree. But we want to know if the FCS is OK in
     * case the CRC is bad (don't want to continue dissection to the NWK layer).
     */
    if (tvb_bytes_exist(tvb, tvb_reported_length(tvb)-IEEE802154_FCS_LEN, IEEE802154_FCS_LEN)) {
        /* The FCS is in the last two bytes of the packet. */
        guint16     fcs = tvb_get_letohs(tvb, tvb_reported_length(tvb)-IEEE802154_FCS_LEN);
        gboolean    fcs_ok;
        /* Check if we are expecting a CC2420-style FCS*/
        if (options & DISSECT_IEEE802154_OPTION_CC24xx) {
            fcs_ok = (fcs & IEEE802154_CC24xx_CRC_OK);
        }
        else {
            fcs_ok = (fcs == ieee802154_crc_tvb(tvb, tvb_reported_length(tvb)-IEEE802154_FCS_LEN));
        }
    }

    /*=====================================================
     * PAYLOAD DISSECTION
     *=====================================================
     */
    /* Create the payload buffer. */
    payload_tvb = tvb_new_subset(tvb, offset, -1, tvb_reported_length(tvb)-offset-IEEE802154_FCS_LEN);
    /* Just being safe to ensure that real_length <= reported_length. The tvbuff
     * code should ensure this condition when creating the subset, but I don't
     * think it does. */
    tvb_set_reported_length(payload_tvb, tvb_reported_length(tvb)-offset-IEEE802154_FCS_LEN);
    /* We can't handle encryption, so if the packet is encrypted, give up. */
    if (packet->security_enable) {
        /* Payload is encrypted. We can't handle this. Maybe a future feature? */
        expert_add_info_format(pinfo, proto_root, PI_UNDECODED, PI_WARN, "Encrypted Payload");
        call_dissector(data_handle, payload_tvb, pinfo, tree);
        goto dissect_ieee802154_fcs;
    }
    /*
     * Wrap the sub-dissection in a try/catch block in case the payload is
     * broken. First we store the current protocol so we can fix it if an
     * exception is thrown by the subdissectors.
     */
    saved_proto = pinfo->current_proto;
    /* Try to dissect the payload. */
    TRY {
        switch (packet->frame_type) {
            case IEEE802154_FCF_BEACON:
                dissect_ieee802154_beacon(payload_tvb, pinfo, ieee802154_tree, packet);
                break;
            case IEEE802154_FCF_CMD:
                dissect_ieee802154_cmd(payload_tvb, pinfo, ieee802154_tree, packet);
                break;
            case IEEE802154_FCF_DATA:
                if (fcs_ok && (tvb_reported_length(payload_tvb)>0)) {
                    /* Attempt heuristic subdissection. */
                    if (dissector_try_heuristic(ieee802154_heur_subdissector_list, payload_tvb, pinfo, tree)) {
                        /* found a sub-dissector! */
                        break;
                    }
                }
                /* If no sub-dissector was called, call the data dissector. */
                call_dissector(data_handle, payload_tvb, pinfo, tree);
                break;
            case IEEE802154_FCF_ACK:
                /* Ack should not contain a payload. */
                if (tvb_reported_length(payload_tvb) > 0) {
                    expert_add_info_format(pinfo, proto_root, PI_MALFORMED, PI_WARN, "Unexpected Payload in Acknowledgement");
                }
                call_dissector(data_handle, payload_tvb, pinfo, tree);
                break;
            default:
                /* Unknown frame type! */
                call_dissector(data_handle, payload_tvb, pinfo, tree);
                break;
        } /* switch */
    }
    CATCH_ALL {
        /*
         * Someone encountered an error while dissecting the payload. But
         * we haven't yet finished processing all of our layer. Catch and
         * display the exception, then fall-through to finish displaying
         * the FCS (which we display last so the frame is ordered correctly
         * in the tree).
         */
        show_exception(payload_tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
        pinfo->current_proto = saved_proto;
    }
    ENDTRY;

    /*=====================================================
     * FRAME CHECK SEQUENCE
     *=====================================================
     */
dissect_ieee802154_fcs:
    /* The FCS should be the last bytes of the reported packet. */
    offset = tvb_reported_length(tvb)-IEEE802154_FCS_LEN;
    /* Dissect the FCS only if it exists (captures which don't or can't get the
     * FCS will simply truncate the packet to omit it, but should still set the
     * reported length to cover the original packet length), so if the snapshot
     * is too short for an FCS don't make a fuss.
     */
    if (tvb_bytes_exist(tvb, offset, IEEE802154_FCS_LEN) && (tree)) {
        proto_tree  *field_tree;
        guint16     fcs = tvb_get_letohs(tvb, offset);

        /* Display the FCS depending on expected FCS format */
        if ((options & DISSECT_IEEE802154_OPTION_CC24xx)) {
            /* Create a subtree for the FCS. */
            ti = proto_tree_add_text(ieee802154_tree, tvb, offset, sizeof(guint16), "Frame Check Sequence: FCS %s", (fcs_ok) ? "OK" : "Bad");
            field_tree = proto_item_add_subtree(ti, ett_ieee802154_fcs);
            /* Display FCS contents.  */
            ti = proto_tree_add_int(field_tree, hf_ieee802154_rssi, tvb, offset, sizeof(guint16), get_by_mask(fcs, IEEE802154_CC24xx_RSSI));
            proto_item_append_text(ti, " dBm"); /*  Displaying Units */
            proto_tree_add_boolean(field_tree, hf_ieee802154_fcs_ok, tvb, offset, sizeof(guint16), get_by_mask(fcs, IEEE802154_CC24xx_CRC_OK));
            proto_tree_add_uint(field_tree, hf_ieee802154_correlation, tvb, offset, sizeof(guint16), get_by_mask(fcs, IEEE802154_CC24xx_CORRELATION));
        }
        else {
            ti = proto_tree_add_uint(ieee802154_tree, hf_ieee802154_fcs, tvb, offset, sizeof(guint16), fcs);
            if (fcs_ok) {
                proto_item_append_text(ti, " (Correct)");
            }
            else {
                proto_item_append_text(ti, " (Incorrect, expected FCS=0x%04x", ieee802154_crc_tvb(tvb, offset));
            }
            /* To Help with filtering, add the fcs_ok field to the tree.  */
            ti = proto_tree_add_boolean(ieee802154_tree, hf_ieee802154_fcs_ok, tvb, offset, sizeof(guint16), fcs_ok);
            PROTO_ITEM_SET_HIDDEN(ti);
        }
    }
    else if (tree) {
        /* Even if the FCS isn't present, add the fcs_ok field to the tree to
         * help with filter. Be sure not to make it visible though.
         */
        ti = proto_tree_add_boolean(ieee802154_tree, hf_ieee802154_fcs_ok, tvb, offset, sizeof(guint16), fcs_ok);
        PROTO_ITEM_SET_HIDDEN(ti);
    }

    /* If the CRC is invalid, make a note of it in the info column. */
    if (!fcs_ok) {
        if (check_col(pinfo->cinfo, COL_INFO)) col_append_fstr(pinfo->cinfo, COL_INFO, ", Bad FCS");
        if (tree) proto_item_append_text(proto_root, ", Bad FCS");

        /* Flag packet as having a bad crc. */
        expert_add_info_format(pinfo, proto_root, PI_CHECKSUM, PI_WARN, "Bad FCS");
    }
} /* dissect_ieee802154_common */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_ieee802154_beacon
 *  DESCRIPTION
 *      ZigBee packet dissection routine for beacon packets.Please refer
 *      to section 7.2.2.1 in the IEEE 802.15.4 document on Beacon frame format
 *  PARAMETERS
 *      tvbuff_t *tvb               - pointer to buffer containing raw packet.
 *      packet_info *pinfo          - pointer to packet information fields
 *      proto_tree *tree            - pointer to data tree ethereal uses to display packet.
 *      ieee802154_packet *packet   - IEEE 802.15.4 packet information.
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
dissect_ieee802154_beacon(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ieee802154_packet *packet)
{
    proto_tree          *field_tree = NULL;
    proto_tree          *bcn_tree = NULL;
    proto_item          *ti;

    guint8      superframe_spec_hi;
    guint8      superframe_spec_lo;
    guint8      gts_spec;
    guint8      gts_desc_count;
    guint8      paddr_spec;
    guint8      paddr_num16;
    guint8      paddr_num64;
    guint8      bcn_payload_len;

    gint        i;
    gint        offset = 0;

    /* Parse the superframe spec. */
    superframe_spec_hi = tvb_get_guint8(tvb, offset);
    superframe_spec_lo = tvb_get_guint8(tvb, offset+1);
    if(tree){
        guint8  bo = superframe_spec_hi & IEEE802154_BCN_BO_MASK;
        guint8  sfo = (superframe_spec_hi & IEEE802154_BCN_SFO_MASK)>>IEEE802154_BCN_SFO_SHIFT;

        /*  Add Subtree for beacon frame */
        ti = proto_tree_add_text(tree, tvb, 0, tvb_length(tvb), "Beacon Frame");
        bcn_tree = proto_item_add_subtree(ti, ett_ieee802154_bcn);

        /* 'Light' Assert to check for valid addressing. */
        if (packet->src_addr_mode == IEEE802154_FCF_ADDR_NONE) {
            expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_WARN, "Missing Source Address in Beacon" );
        }

        /*  Add Subtree for superframe specification */
        ti = proto_tree_add_text(bcn_tree, tvb, offset, 2, "Superframe Specification");
        field_tree = proto_item_add_subtree(ti, ett_ieee802154_bcn_superframe_spec);

        /*  Add Beacon Order to the superframe spec. */
        ti = proto_tree_add_uint_format(field_tree, hf_ieee802154_bcn_beacon_order, tvb, offset, 1, bo, "Beacon Order: ");
        if(bo == 0xf) proto_item_append_text(ti, "Beacons Disabled");
        else proto_item_append_text(ti, "%i", IEEE802154_BCN_SFRM_DURATION*(1<<bo));

        /* Add superframe order to superframe spec. */
        ti = proto_tree_add_uint_format(field_tree, hf_ieee802154_bcn_superframe_order, tvb, offset, 1, sfo, "Superframe Order: ");
        if(bo == 0xf) proto_item_append_text(ti, "Inactive");
        else proto_item_append_text(ti, "%i", IEEE802154_BCN_SFRM_DURATION*(1<<sfo));

        /* Add the CAP and Flags. */
        proto_tree_add_uint(field_tree, hf_ieee802154_bcn_cap, tvb, offset+1, 1, superframe_spec_lo & IEEE802154_BCN_CAP_MASK);
        proto_tree_add_boolean(field_tree, hf_ieee802154_bcn_battery_ext, tvb, offset+1, 1, superframe_spec_lo & IEEE802154_BCN_BATT_EXTN_MASK);
        proto_tree_add_boolean(field_tree, hf_ieee802154_bcn_coord, tvb, offset+1, 1, superframe_spec_lo & IEEE802154_BCN_COORD_MASK);
        proto_tree_add_boolean(field_tree, hf_ieee802154_bcn_assoc_permit, tvb, offset+1, 1, superframe_spec_lo & IEEE802154_BCN_ASSOC_PERM_MASK);
    }
    offset += sizeof(guint16);

    /*  Get and display the GTS specification field */
    gts_spec = tvb_get_guint8(tvb, offset);
    gts_desc_count = gts_spec & IEEE802154_BCN_GTS_COUNT_MASK;
    if(tree){
        proto_tree_add_uint(bcn_tree, hf_ieee802154_bcn_gts_count, tvb, offset, 1, gts_desc_count);
        proto_tree_add_boolean(bcn_tree, hf_ieee802154_bcn_gts_permit, tvb, offset, 1, gts_spec & IEEE802154_BCN_GTS_PERMIT_MASK);
    }
    offset += sizeof(guint8);

    /* If the GTS descriptor count is nonzero, then the GTS directions mask and descriptor list are present. */
    if(gts_desc_count){
        guint8  gts_directions = tvb_get_guint8(tvb, offset + 1);
        guint   gts_numRx = 0;

        /* Display the directions mask. */
        if (tree) {
            /* Create a subtree. */
            ti = proto_tree_add_text(bcn_tree, tvb, offset, sizeof(guint8), "GTS Directions");
            field_tree = proto_item_add_subtree(ti, ett_ieee802154_bcn_gts_direction);

            /* Add the directions to the subtree. */
            for (i=0; i<gts_desc_count; i++) {
                gboolean    dir = gts_directions & IEEE802154_BCN_GTS_DIRECTION_SLOT(i);

                proto_tree_add_boolean_format(field_tree, hf_ieee802154_bcn_gts_direction, tvb, offset, sizeof(guint8), dir, "GTS Slot %i: %s", i+1, dir?"Receive Only":"Transmit Only");
                if (dir) gts_numRx++;
            } /* for */
            proto_item_append_text(ti, ": %i Receive & %i Transmit", gts_numRx, gts_desc_count-gts_numRx);
        }
        offset += sizeof(guint8);

        /* Create a subtree for the GTS descriptors. */
        if (tree) {
            ti = proto_tree_add_text(bcn_tree, tvb, offset, (sizeof(guint16)+sizeof(guint8))*gts_desc_count, "GTS Descriptors");
            field_tree = proto_item_add_subtree(ti, ett_ieee802154_bcn_gts_descriptors);
        }

        /* Get and display the GTS descriptors. */
        for (i=0; i<gts_desc_count; i++) {
            guint16 gts_addr        = tvb_get_letohs(tvb, offset);
            guint8  gts_slot        = tvb_get_guint8(tvb, offset+2);
            guint8  gts_slot_len    = (gts_slot & IEEE802154_BCN_GTS_LENGTH_MASK) >> IEEE802154_BCN_GTS_LENGTH_SHIFT;

            if (tree) {
                /* Add address, slot, and time length fields. */
                ti = proto_tree_add_text(field_tree, tvb, offset, 2, "{Address: 0x%04x", gts_addr);
                proto_item_append_text(ti, ", Slot: %i", gts_slot);
                proto_item_append_text(ti, ", Length: %i}", gts_slot_len);
            }
            offset += sizeof(guint16)+sizeof(guint8);
        } /* for */
    }

    /*  Get the Pending Addresses specification fields */
    paddr_spec = tvb_get_guint8(tvb, offset);
    paddr_num16 = paddr_spec & IEEE802154_BCN_PADDR_SHORT_MASK;
    paddr_num64 = (paddr_spec & IEEE802154_BCN_PADDR_LONG_MASK) >> IEEE802154_BCN_PADDR_LONG_SHIFT;
    if(tree){
        /*  Add Subtree for the addresses */
        ti = proto_tree_add_text(bcn_tree, tvb, offset, 1 + 2*paddr_num16 + 8*paddr_num64, "Pending Addresses: %i Short and %i Long", paddr_num16, paddr_num64);
        field_tree = proto_item_add_subtree(ti, ett_ieee802154_bcn_pending);
    }
    offset += sizeof(guint8);

    for (i=0; i<paddr_num16; i++) {
        guint16 addr = tvb_get_letohs(tvb, offset);
        if (tree) {
            proto_tree_add_uint(field_tree, hf_ieee802154_bcn_pending16, tvb, offset, sizeof(guint16), addr);
        }
        offset += sizeof(guint16);
    }
    for (i=0; i<paddr_num64; i++) {
        guint64 addr = tvb_get_letoh64(tvb, offset);
        if (tree) {
            proto_tree_add_uint64_format_value(field_tree, hf_ieee802154_bcn_pending64, tvb, offset, sizeof(guint64), addr, "%s (%s)", print_eui64_oui(addr), print_eui64(addr));
        }
        offset += sizeof(guint64);
    }

    /* Get the beacon payload (if it exists) */
    bcn_payload_len = tvb_length(tvb) - offset;
    if(bcn_payload_len){
        proto_tree  *root_tree      = proto_tree_get_root(tree);
        tvbuff_t    *payload_tvb    = tvb_new_subset(tvb, offset, bcn_payload_len, bcn_payload_len);
        /* Attempt subdissection. */
        if(!dissector_try_heuristic(ieee802154_heur_subdissector_list, payload_tvb, pinfo, root_tree)) {
            /* heuristic subdissector was not called. use data subdissector instead. */
            call_dissector(data_handle, payload_tvb, pinfo, root_tree);
        }
    }
} /* dissect_ieee802154_beacon */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_ieee802154_cmd
 *  DESCRIPTION
 *      IEEE 802.15.4 packet dissection routine for command packets
 *  PARAMETERS
 *      tvbuff_t *tvb               - pointer to buffer containing raw packet.
 *      packet_info *pinfo          - pointer to packet information fields
 *      proto_tree *tree            - pointer to data tree Ethereal uses to display packet.
 *      ieee802154_packet *packet   - IEEE 802.15.4 packet information.
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 *  MAC Command Frames have a MAC Payload organized as follows:
 *  |Command Frame Identifier|   Command Payload    |
 *  |       1 Byte           |dependant upon command|
 *---------------------------------------------------------------
 */
static void
dissect_ieee802154_cmd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ieee802154_packet   *packet)
{
    guint8              cmd_id;
    guint               offset = 0;
    proto_tree          *cmd_tree = NULL;
    proto_item          *ti;
    proto_item          *cmd_root = NULL;

#define CMD_ADDR_CHECK(x)    if (!(x)) expert_add_info_format(pinfo, cmd_root, PI_MALFORMED, PI_WARN, "Invalid Addressing for %s", val_to_str(cmd_id, ieee802154_cmd_names, "Unknown Command"))

    /* Get and display the command frame identifier. */
    cmd_id = tvb_get_guint8(tvb, offset);
    if(check_col(pinfo->cinfo, COL_INFO)) {
        col_set_str(pinfo->cinfo, COL_INFO, val_to_str(cmd_id, ieee802154_cmd_names, "Unknown Command"));
    }
    if (tree) {
        /* Create a subtree for this command frame. */
        cmd_root = proto_tree_add_text(tree, tvb, 0, tvb_length(tvb), "Command Frame, %s", val_to_str(cmd_id, ieee802154_cmd_names, "Unknown Command"));
        cmd_tree = proto_item_add_subtree(cmd_root, ett_ieee802154_cmd);

        /* Add the command ID to the subtree. */
        ti = proto_tree_add_uint(cmd_tree, hf_ieee802154_cmd_id, tvb, offset, sizeof(guint8), cmd_id);
    }

    /* Increment the offset field. */
    offset += sizeof(guint8);

    /* Parse the Command Payloads. */
    switch(cmd_id){
        case IEEE802154_CMD_ASRQ:
            /* Check that the addressing is correct for this command type. */
            CMD_ADDR_CHECK((packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT)
                        && (packet->dst_addr_mode != IEEE802154_FCF_ADDR_NONE));

            dissect_ieee802154_cmd_asreq(tvb, pinfo, cmd_tree, packet, &offset);
            break;

        case IEEE802154_CMD_ASRSP:
            /* Check that the addressing is correct for this command type. */
            CMD_ADDR_CHECK((packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT)
                        && (packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT));

            dissect_ieee802154_cmd_asrsp(tvb, pinfo, cmd_tree, packet, &offset);
            break;

        case IEEE802154_CMD_DISAS:
            /* Check that the addressing is correct for this command type. */
            CMD_ADDR_CHECK((packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT)
                        && (packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT));

            dissect_ieee802154_cmd_disas(tvb, pinfo, cmd_tree, packet, &offset);
            break;

        case IEEE802154_CMD_DATA_RQ:
            /* Check that the addressing is correct for this command type. */
            CMD_ADDR_CHECK(packet->src_addr_mode != IEEE802154_FCF_ADDR_NONE);

            /* Data Req contains no payload. */
            break;

        case IEEE802154_CMD_PANID_ERR:
            /* Check that the addressing is correct for this command type. */
            CMD_ADDR_CHECK((packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT)
                        && (packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT));

            /* PANID Err contains no payload. */
            break;

        case IEEE802154_CMD_ORPH_NOTIF:
            /* Check that the addressing is correct for this command type. */
            CMD_ADDR_CHECK((packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT)
                        && (packet->dst_addr_mode == IEEE802154_FCF_ADDR_SHORT)
                        && (packet->dst_addr16 == IEEE802154_BCAST_ADDR)
                        && (packet->src_pan == IEEE802154_BCAST_PAN)
                        && (packet->dst_pan == IEEE802154_BCAST_PAN));

            /* Orphan Notification contains no payload. */
            break;

        case IEEE802154_CMD_BCN_RQ:
            /* Check that the addressing is correct for this command type. */
            CMD_ADDR_CHECK((packet->dst_addr_mode == IEEE802154_FCF_ADDR_SHORT)
                        && (packet->src_addr_mode == IEEE802154_FCF_ADDR_NONE)
                        && (packet->dst_addr16 == IEEE802154_BCAST_ADDR)
                        && (packet->dst_pan == IEEE802154_BCAST_PAN));

            /* Beacon Request contains no payload. */
            break;

        case IEEE802154_CMD_COORD_REAL:
            /* Check that the addressing is correct for this command type. */
            CMD_ADDR_CHECK((packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT)
                        && (packet->dst_pan == IEEE802154_BCAST_PAN)
                        && (packet->dst_addr_mode != IEEE802154_FCF_ADDR_NONE));

            if (packet->dst_addr_mode == IEEE802154_FCF_ADDR_SHORT) {
                /* If directed to a 16-bit address, check that it is being broadcast. */
                CMD_ADDR_CHECK(packet->dst_addr16 == IEEE802154_BCAST_ADDR);
            }

            dissect_ieee802154_cmd_realign(tvb, pinfo, cmd_tree, packet, &offset);
            break;

        case IEEE802154_CMD_GTS_REQ:
            /* Check that the addressing is correct for this command type. */
            CMD_ADDR_CHECK((packet->src_addr_mode == IEEE802154_FCF_ADDR_SHORT)
                        && (packet->dst_addr_mode == IEEE802154_FCF_ADDR_NONE)
                        && (packet->src_addr16 != IEEE802154_BCAST_ADDR)
                        && (packet->src_addr16 != IEEE802154_NO_ADDR16));

            dissect_ieee802154_cmd_gtsrq(tvb, pinfo, cmd_tree, packet, &offset);
            break;

        default:
            break;
    } /* switch */

#undef CMD_ADDR_CHECK

    /* If there are bytes leftover, call the data dissector to handle them. */
    if (offset < tvb_length(tvb)) {
        guint       leftover_len    = tvb_length(tvb) - offset;
        proto_tree  *root           = proto_tree_get_root(tree);
        tvbuff_t    *leftover_tvb   = tvb_new_subset(tvb, offset, leftover_len, leftover_len);

        /* Call the data dissector. */
        if (leftover_tvb) call_dissector(data_handle, leftover_tvb, pinfo, root);
    }
} /* dissect_ieee802154_cmd */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_ieee802154_cmd_asreq
 *  DESCRIPTION
 *      Command subdissector routine for the Association request
 *      command.
 *
 *      Assumes that COL_INFO will be set to the command name,
 *      command name will already be appended to the command subtree
 *      and protocol root. In addition, assumes that the command ID
 *      has already been parsed.
 *  PARAMETERS
 *      tvbuff_t    *tvb            - pointer to buffer containing raw packet.
 *      packet_info *pinfo          - pointer to packet information fields (unused).
 *      proto_tree  *tree           - pointer to command subtree.
 *      ieee802154_packet *packet   - IEEE 802.15.4 packet information (unused).
 *      guint       *offset         - offset into the tvbuff to begin dissection.
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
dissect_ieee802154_cmd_asreq(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, ieee802154_packet *packet _U_, guint *offset)
{
    proto_item          *ti;
    proto_tree          *field_tree;
    guint8              capability_info;

    /* Get the capability info. */
    capability_info = tvb_get_guint8(tvb, *offset);

    /* Display capability info. */
    if (tree) {
        ti = proto_tree_add_text(tree, tvb, *offset, sizeof(guint8), "Capability Information");
        field_tree = proto_item_add_subtree(ti, ett_ieee802154_cmd_cinfo);

        /* Enter the capability bits. */
        proto_tree_add_boolean(field_tree, hf_ieee802154_cmd_cinfo_alt_coord, tvb, *offset, sizeof(guint8), capability_info & IEEE802154_CMD_CINFO_ALT_PAN_COORD);
        ti = proto_tree_add_boolean(field_tree, hf_ieee802154_cmd_cinfo_device_type, tvb, *offset, sizeof(guint8), capability_info & IEEE802154_CMD_CINFO_DEVICE_TYPE);
        if (capability_info & IEEE802154_CMD_CINFO_DEVICE_TYPE) proto_item_append_text(ti, " (FFD)");
        else proto_item_append_text(ti, " (RFD)");
        ti = proto_tree_add_boolean(field_tree, hf_ieee802154_cmd_cinfo_power_src, tvb, *offset, sizeof(guint8), capability_info & IEEE802154_CMD_CINFO_POWER_SRC);
        if (capability_info & IEEE802154_CMD_CINFO_POWER_SRC) proto_item_append_text(ti, " (AC/Mains Power)");
        else proto_item_append_text(ti, " (Battery)");
        proto_tree_add_boolean(field_tree, hf_ieee802154_cmd_cinfo_idle_rx, tvb, *offset, sizeof(guint8), capability_info & IEEE802154_CMD_CINFO_IDLE_RX);
        proto_tree_add_boolean(field_tree, hf_ieee802154_cmd_cinfo_sec_capable, tvb, *offset, sizeof(guint8), capability_info & IEEE802154_CMD_CINFO_SEC_CAPABLE);
        proto_tree_add_boolean(field_tree, hf_ieee802154_cmd_cinfo_alloc_addr, tvb, *offset, sizeof(guint8), capability_info & IEEE802154_CMD_CINFO_ALLOC_ADDR);
    }

    /* Increase the offset. */
    (*offset) += sizeof(guint8);
} /* dissect_ieee802154_cmd_asreq */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_ieee802154_cmd_asrsp
 *  DESCRIPTION
 *      Command subdissector routine for the Association response
 *      command.
 *
 *      Assumes that COL_INFO will be set to the command name,
 *      command name will already be appended to the command subtree
 *      and protocol root. In addition, assumes that the command ID
 *      has already been parsed.
 *  PARAMETERS
 *      tvbuff_t    *tvb            - pointer to buffer containing raw packet.
 *      packet_info *pinfo          - pointer to packet information fields
 *      proto_tree  *tree           - pointer to command subtree.
 *      ieee802154_packet *packet   - IEEE 802.15.4 packet information (unused).
 *      guint       *offset         - offset into the tvbuff to begin dissection.
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
dissect_ieee802154_cmd_asrsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ieee802154_packet *packet _U_, guint *offset)
{
    proto_item          *ti;
    guint16             short_addr;
    guint8              status;

    /* Get and display the short address. */
    short_addr = tvb_get_letohs(tvb, *offset);
    if (tree) {
        proto_tree_add_uint(tree, hf_ieee802154_cmd_asrsp_addr, tvb, *offset, sizeof(guint16), short_addr);
    }
    (*offset) += sizeof(guint16);

    /* Get and display the status. */
    status = tvb_get_guint8(tvb, *offset);
    if (tree) {
        ti = proto_tree_add_uint(tree, hf_ieee802154_cmd_asrsp_status, tvb, *offset, sizeof(guint8), status);
        if (status == IEEE802154_CMD_ASRSP_AS_SUCCESS) proto_item_append_text(ti, " (Association Successful)");
        else if (status == IEEE802154_CMD_ASRSP_PAN_FULL) proto_item_append_text(ti, " (PAN Full)");
        else if (status == IEEE802154_CMD_ASRSP_PAN_DENIED) proto_item_append_text(ti, " (Association Denied)");
        else proto_item_append_text(ti, " (Reserved)");
    }
    (*offset) += sizeof(guint8);

    /* Update the info column. */
    if (check_col(pinfo->cinfo, COL_INFO)) {
        if (status == IEEE802154_CMD_ASRSP_AS_SUCCESS) {
            /* Association was successful. */
            if (packet->src_addr_mode != IEEE802154_FCF_ADDR_SHORT) {
                col_append_fstr(pinfo->cinfo, COL_INFO, ", PAN: 0x%04x", packet->dst_pan);
            }
            if (short_addr != IEEE802154_NO_ADDR16) {
                col_append_fstr(pinfo->cinfo, COL_INFO, " Addr: 0x%04x", short_addr);
            }
        }
        else {
            /* Association was unsuccessful. */
            col_append_fstr(pinfo->cinfo, COL_INFO, ", Unsuccessful");
        }
    }
} /* dissect_ieee802154_cmd_asrsp */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_ieee802154_cmd_disas
 *  DESCRIPTION
 *      Command subdissector routine for the Disassociate command.
 *
 *      Assumes that COL_INFO will be set to the command name,
 *      command name will already be appended to the command subtree
 *      and protocol root. In addition, assumes that the command ID
 *      has already been parsed.
 *  PARAMETERS
 *      tvbuff_t    *tvb            - pointer to buffer containing raw packet.
 *      packet_info *pinfo          - pointer to packet information fields (unused).
 *      proto_tree  *tree           - pointer to command subtree.
 *      ieee802154_packet *packet   - IEEE 802.15.4 packet information (unused).
 *      guint       *offset         - offset into the tvbuff to begin dissection.
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
dissect_ieee802154_cmd_disas(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, ieee802154_packet *packet _U_, guint *offset)
{
    proto_item          *ti;
    guint8              reason;

    /* Get and display the dissasociation reason. */
    reason = tvb_get_guint8(tvb, *offset);
    if (tree) {
        ti = proto_tree_add_uint(tree, hf_ieee802154_cmd_disas_reason, tvb, *offset, sizeof(guint8), reason);
        switch(reason) {
            case 0x01:
                proto_item_append_text(ti, " (Coordinator requests device to leave)");
                break;

            case 0x02:
                proto_item_append_text(ti, " (Device wishes to leave)");
                break;

            default:
                proto_item_append_text(ti, " (Reserved)");
                break;
        } /* switch */
    }

    /* Adjust offset */
    (*offset) += sizeof(guint8);
} /* dissect_ieee802154_cmd_disas */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_ieee802154_cmd_gtsrq
 *  DESCRIPTION
 *      Command subdissector routine for the Coordinator Realignment
 *      command.
 *
 *      Assumes that COL_INFO will be set to the command name,
 *      command name will already be appended to the command subtree
 *      and protocol root. In addition, assumes that the command ID
 *      has already been parsed.
 *  PARAMETERS
 *      tvbuff_t    *tvb            - pointer to buffer containing raw packet.
 *      packet_info *pinfo          - pointer to packet information fields
 *      proto_tree  *tree           - pointer to command subtree.
 *      ieee802154_packet *packet   - IEEE 802.15.4 packet information.
 *      guint       *offset         - offset into the tvbuff to begin dissection.
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
dissect_ieee802154_cmd_realign(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ieee802154_packet *packet, guint *offset)
{
    guint16 pan_id;
    guint16 coord_addr;
    guint8  channel;
    guint16 short_addr;

    /* Get and display the command PAN ID. */
    pan_id = tvb_get_letohs(tvb, *offset);
    if (tree) proto_tree_add_uint(tree, hf_ieee802154_cmd_coord_pan, tvb, *offset, sizeof(guint16), pan_id);
    if (check_col(pinfo->cinfo, COL_INFO)) col_append_fstr(pinfo->cinfo, COL_INFO, ", PAN: 0x%04x", pan_id);
    (*offset) += sizeof(guint16);

    /* Get and display the coordinator address. */
    coord_addr = tvb_get_letohs(tvb, *offset);
    if (tree) proto_tree_add_uint(tree, hf_ieee802154_cmd_coord_caddr, tvb, *offset, sizeof(guint16), coord_addr);
    if (check_col(pinfo->cinfo, COL_INFO)) col_append_fstr(pinfo->cinfo, COL_INFO, ", Coordinator: 0x%04x", coord_addr);
    (*offset) += sizeof(guint16);

    /* Get and display the channel. */
    channel = tvb_get_guint8(tvb, *offset);
    if (tree) proto_tree_add_uint(tree, hf_ieee802154_cmd_coord_channel, tvb, *offset, sizeof(guint8), channel);
    if (check_col(pinfo->cinfo, COL_INFO)) col_append_fstr(pinfo->cinfo, COL_INFO, ", Channel: %u", channel);
    (*offset) += sizeof(guint8);

    /* Get and display the short address. */
    short_addr = tvb_get_letohs(tvb, *offset);
    if (tree) proto_tree_add_uint(tree, hf_ieee802154_cmd_coord_addr, tvb, *offset, sizeof(guint16), short_addr);
    if (   (check_col(pinfo->cinfo, COL_INFO))
        && (packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT)
        && (short_addr != IEEE802154_NO_ADDR16)) {
        col_append_fstr(pinfo->cinfo, COL_INFO, ", Addr: 0x%04x", short_addr);
    }
    (*offset) += sizeof(guint16);

    /* Get and display the channel page, if it exists. Added in IEEE802.15.4-2006 */
    if (tvb_bytes_exist(tvb, *offset, sizeof(guint8))) {
        guint8  channel_page = tvb_get_guint8(tvb, *offset);

        if (tree) proto_tree_add_uint(tree, hf_ieee802154_cmd_coord_channel_page, tvb, *offset, sizeof(guint8), channel_page);
        (*offset) += sizeof(guint8);
    }
} /* dissect_ieee802154_cmd_realign */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_ieee802154_cmd_gtsrq
 *  DESCRIPTION
 *      Command subdissector routine for the GTS request command.
 *
 *      Assumes that COL_INFO will be set to the command name,
 *      command name will already be appended to the command subtree
 *      and protocol root. In addition, assumes that the command ID
 *      has already been parsed.
 *  PARAMETERS
 *      tvbuff_t    *tvb            - pointer to buffer containing raw packet.
 *      packet_info *pinfo          - pointer to packet information fields (unused).
 *      proto_tree  *tree           - pointer to command subtree.
 *      ieee802154_packet *packet   - IEEE 802.15.4 packet information (unused).
 *      guint       *offset         - offset into the tvbuff to begin dissection.
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
dissect_ieee802154_cmd_gtsrq(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, ieee802154_packet *packet _U_, guint *offset)
{
    proto_item  *ti;

    guint8  characteristics;
    guint8  length;
    guint8  direction;
    guint8  type;

    /* Get the characteristics field. */
    characteristics = tvb_get_guint8(tvb, *offset);
    length = characteristics & IEEE802154_CMD_GTS_REQ_LEN;
    direction = characteristics & IEEE802154_CMD_GTS_REQ_DIR;
    type = characteristics & IEEE802154_CMD_GTS_REQ_TYPE;

    /* Display the characteristics field. */
    if (tree) {
        proto_tree_add_uint(tree, hf_ieee802154_cmd_gts_req_len, tvb, *offset, sizeof(guint8), length);
        ti = proto_tree_add_boolean(tree, hf_ieee802154_cmd_gts_req_dir, tvb, *offset, sizeof(guint8), direction);
        if (direction) proto_item_append_text(ti, " (Receive)");
        else proto_item_append_text(ti, " (Transmit)");
        ti = proto_tree_add_boolean(tree, hf_ieee802154_cmd_gts_req_type, tvb, *offset, sizeof(guint8), type);
        if (type) proto_item_append_text(ti, " (Allocate GTS)");
        else proto_item_append_text(ti, " (Deallocate GTS)");
    }

    /* Adjust offset */
    (*offset) += sizeof(guint8);
} /* dissect_ieee802154_cmd_gtsrq */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      proto_register_ieee802154
 *  DESCRIPTION
 *      IEEE 802.15.4 protocol registration routine.
 *  PARAMETERS
 *      none
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
void proto_register_ieee802154(void)
{
    static hf_register_info hf[] = {
        { &hf_ieee802154_frame_type,
        { "Frame Type",                     "wpan.frame_type", FT_UINT16, BASE_HEX, VALS(ieee802154_frame_types), IEEE802154_FCF_TYPE_MASK,
            "", HFILL }},

        { &hf_ieee802154_security,
        { "Security Enabled",               "wpan.security", FT_BOOLEAN, 16, NULL, IEEE802154_FCF_SEC_EN,
            "Whether security operations are performed at the MAC layer or not.", HFILL }},

        { &hf_ieee802154_pending,
        { "Frame Pending",                  "wpan.pending", FT_BOOLEAN, 16, NULL, IEEE802154_FCF_FRAME_PND,
            "Indication of additional packets waiting to be transferred from the source device.", HFILL }},

        { &hf_ieee802154_ack_request,
        { "Acknowledge Request",            "wpan.ack_request", FT_BOOLEAN, 16, NULL, IEEE802154_FCF_ACK_REQ,
            "Whether the sender of this packet requests acknowledgement or not.", HFILL }},

        { &hf_ieee802154_intra_pan,
        { "Intra-PAN",                      "wpan.intra_pan", FT_BOOLEAN, 16, NULL, IEEE802154_FCF_INTRA_PAN,
            "Whether this packet originated and terminated within the same PAN or not.", HFILL }},

        { &hf_ieee802154_seqno,
        { "Sequence Number",                "wpan.seq_no", FT_UINT8, BASE_DEC, NULL, 0x0,
            "", HFILL }},

        { &hf_ieee802154_dst_addr_mode,
        { "Destination Addressing Mode",    "wpan.dst_addr_mode", FT_UINT16, BASE_HEX, VALS(ieee802154_addr_modes), IEEE802154_FCF_DADDR_MASK,
            "", HFILL }},

        { &hf_ieee802154_src_addr_mode,
        { "Source Addressing Mode",         "wpan.src_addr_mode", FT_UINT16, BASE_HEX, VALS(ieee802154_addr_modes), IEEE802154_FCF_SADDR_MASK,
            "", HFILL }},

        { &hf_ieee802154_version,
        { "Frame Version",                  "wpan.version", FT_UINT16, BASE_DEC, NULL, IEEE802154_FCF_VERSION,
            "", HFILL }},

        { &hf_ieee802154_dst_pan,
        { "Destination PAN",                "wpan.dst_pan", FT_UINT16, BASE_HEX, NULL, 0x0,
            "", HFILL }},

        { &hf_ieee802154_dst_addr16,
        { "Destination",                    "wpan.dst_addr16", FT_UINT16, BASE_HEX, NULL, 0x0,
            "", HFILL }},

        { &hf_ieee802154_dst_addr64,
        { "Destination",                    "wpan.dst_addr64", FT_UINT64, BASE_HEX, NULL, 0x0,
            "", HFILL }},

        { &hf_ieee802154_src_panID,
        { "Source PAN",                     "wpan.src_pan", FT_UINT16, BASE_HEX, NULL, 0x0,
            "", HFILL }},

        { &hf_ieee802154_src_addr16,
        { "Source",                         "wpan.src_addr16", FT_UINT16, BASE_HEX, NULL, 0x0,
            "", HFILL }},

        { &hf_ieee802154_src_addr64,
        { "Source",                         "wpan.src_addr64", FT_UINT64, BASE_HEX, NULL, 0x0,
            "", HFILL }},

        { &hf_ieee802154_fcs,
        { "FCS",                            "wpan.fcs", FT_UINT16, BASE_HEX, NULL, 0x0,
            "", HFILL }},

        { &hf_ieee802154_rssi,
        { "RSSI",                           "wpan.rssi", FT_INT8, BASE_DEC, NULL, 0x0,
            "Received Signal Strength", HFILL }},

        { &hf_ieee802154_fcs_ok,
        { "FCS Valid",                      "wpan.fcs_ok", FT_BOOLEAN, 8, NULL, 0x0,
            "", HFILL }},

        { &hf_ieee802154_correlation,
        { "LQI Correlation Value",          "wpan.correlation", FT_UINT8, BASE_DEC, NULL, 0x0,
            "", HFILL }},

            /*  Command Frame Specific Fields */
            /*--------------------------------*/

        { &hf_ieee802154_cmd_id,
        { "Command Identifier",         "wpan.cmd.id", FT_UINT8, BASE_HEX, VALS(ieee802154_cmd_names), 0x0,
            "", HFILL }},

            /*  Capability Information Fields */
        { &hf_ieee802154_cmd_cinfo_alt_coord,
        { "Alternate PAN Coordinator",  "wpan.cmd.cinfo.alt_coord", FT_BOOLEAN, 8, NULL, IEEE802154_CMD_CINFO_ALT_PAN_COORD,
            "Whether this device can act as a PAN coordinator or not.", HFILL }},

        { &hf_ieee802154_cmd_cinfo_device_type,
        { "Device Type",                "wpan.cmd.cinfo.device_type", FT_BOOLEAN, 8, NULL, IEEE802154_CMD_CINFO_DEVICE_TYPE,
            "Whether this device is RFD (reduced-function device) or FFD (full-function device).", HFILL }},

        { &hf_ieee802154_cmd_cinfo_power_src,
        { "Power Source",               "wpan.cmd.cinfo.power_src", FT_BOOLEAN, 8, NULL, IEEE802154_CMD_CINFO_POWER_SRC,
            "Whether this device is operating on AC/mains or battery power.", HFILL }},

        { &hf_ieee802154_cmd_cinfo_idle_rx,
        { "Receive On When Idle",       "wpan.cmd.cinfo.idle_rx", FT_BOOLEAN, 8, NULL, IEEE802154_CMD_CINFO_IDLE_RX,
            "Whether this device can receive packets while idle or not.", HFILL }},

        { &hf_ieee802154_cmd_cinfo_sec_capable,
        { "Security Capability",        "wpan.cmd.cinfo.sec_capable", FT_BOOLEAN, 8, NULL, IEEE802154_CMD_CINFO_SEC_CAPABLE,
            "Whether this device is capable of receiving encrypted packets.", HFILL }},

        { &hf_ieee802154_cmd_cinfo_alloc_addr,
        { "Allocate Address",           "wpan.cmd.cinfo.alloc_addr", FT_BOOLEAN, 8, NULL, IEEE802154_CMD_CINFO_ALLOC_ADDR,
            "Whether this device wishes to use a 16-bit short address instead of its IEEE 802.15.4 64-bit long address.", HFILL }},

            /*  Association response fields */
        { &hf_ieee802154_cmd_asrsp_addr,
        { "Short Address",              "wpan.cmd.asrsp.addr", FT_UINT16, BASE_HEX, NULL, 0x0,
            "The short address that the device should assume.\nAn address of 0xfffe indicates that the device should use its IEEE 64-bit long address.", HFILL }},

        { &hf_ieee802154_cmd_asrsp_status,
        { "Association Status",         "wpan.cmd.asrsp.status", FT_UINT8, BASE_HEX, NULL, 0x0,
            "", HFILL }},

        { &hf_ieee802154_cmd_disas_reason,
        { "Disassociation Reason",      "wpan.cmd.disas.reason", FT_UINT8, BASE_HEX, NULL, 0x0,
            "", HFILL }},

            /*  Coordinator Realignment fields */
        { &hf_ieee802154_cmd_coord_pan,
        { "PAN ID",                     "wpan.cmd.coord.pan", FT_UINT16, BASE_HEX, NULL, 0x0,
            "The PAN identifier the coordinator wishes to use for future communication.", HFILL }},

        { &hf_ieee802154_cmd_coord_caddr,
        { "Coordinator Short Address",  "wpan.cmd.coord.addr", FT_UINT16, BASE_HEX, NULL, 0x0,
            "The 16-bit address the coordinator wishes to use for future communication.", HFILL }},

        { &hf_ieee802154_cmd_coord_channel,
        { "Logical Channel",            "wpan.cmd.coord.channel", FT_UINT8, BASE_DEC, NULL, 0x0,
            "The logical channel the coordinator wishes to use for future communication.", HFILL }},

        { &hf_ieee802154_cmd_coord_addr,
        { "Short Address",              "wpan.cmd.coord.addr", FT_UINT16, BASE_HEX, NULL, 0x0,
            "A short-address that the orphaned device shall assume if applicable.", HFILL }},

        { &hf_ieee802154_cmd_coord_channel_page,
        { "Channel Page",               "wpan.cmd.coord.channel_page", FT_UINT8, BASE_DEC, NULL, 0x0,
            "The logical channel page the coordinator wishes to use for future communication.", HFILL }},

        { &hf_ieee802154_cmd_gts_req_len,
        { "GTS Length",                 "wpan.cmd.gts.length", FT_UINT8, BASE_DEC, NULL, IEEE802154_CMD_GTS_REQ_LEN,
            "Number of superframe slots the device is requesting.", HFILL }},

        { &hf_ieee802154_cmd_gts_req_dir,
        { "GTS Direction",              "wpan.cmd.gts.direction", FT_BOOLEAN, 8, NULL, IEEE802154_CMD_GTS_REQ_DIR,
            "The direction of traffic in the guaranteed timeslot.", HFILL }},

        { &hf_ieee802154_cmd_gts_req_type,
        { "Characteristic Type",        "wpan.cmd.gts.type", FT_BOOLEAN, 8, NULL, IEEE802154_CMD_GTS_REQ_TYPE,
            "Whether this request is to allocate or deallocate a timeslot.", HFILL }},

            /*  Beacon Frame Specific Fields */
            /*-------------------------------*/
        { &hf_ieee802154_bcn_beacon_order,
        { "Beacon Interval",            "wpan.bcn.beacon_order", FT_UINT8, BASE_DEC, NULL, 0x0,
            "Specifies the transmission interval of the beacons.", HFILL }},

        { &hf_ieee802154_bcn_superframe_order,
        { "Superframe Interval",        "wpan.bcn.superframe_order", FT_UINT8, BASE_DEC, NULL, 0x0,
            "Specifies the length of time the coordinator will interact with the PAN.", HFILL }},

        { &hf_ieee802154_bcn_cap,
        { "Final CAP Slot",             "wpan.bcn.cap", FT_UINT8, BASE_DEC, NULL, 0x0,
            "Specifies the final superframe slot used by the CAP.", HFILL }},

        { &hf_ieee802154_bcn_battery_ext,
        { "Battery Extension",          "wpan.bcn.battery_ext", FT_BOOLEAN, 8, NULL, IEEE802154_BCN_BATT_EXTN_MASK,
            "Whether transmissions may not extend past the length of the beacon frame.", HFILL }},

        { &hf_ieee802154_bcn_coord,
        { "PAN Coordinator",            "wpan.bcn.coord", FT_BOOLEAN, 8, NULL, IEEE802154_BCN_COORD_MASK,
            "Whether this beacon frame is being transmitted by the PAN coordinator or not.", HFILL }},

        { &hf_ieee802154_bcn_assoc_permit,
        { "Association Permit",         "wpan.bcn.assoc_permit", FT_BOOLEAN, 8, NULL, IEEE802154_BCN_ASSOC_PERM_MASK,
            "Whether this PAN is accepting association requests or not.", HFILL }},

        { &hf_ieee802154_bcn_gts_count,
        { "GTS Descriptor Count",       "wpan.bcn.gts.count", FT_UINT8, BASE_DEC, NULL, 0x0,
            "The number of GTS descriptors present in this beacon frame.", HFILL }},

        { &hf_ieee802154_bcn_gts_permit,
        { "GTS Permit",                 "wpan.bcn.gts.permit", FT_BOOLEAN, 8, NULL, 0x0,
            "Whether the PAN coordinator is accepting GTS requests or not.", HFILL }},

        { &hf_ieee802154_bcn_gts_direction,
        { "Direction",                  "wpan.bcn.gts.direction", FT_BOOLEAN, 8, NULL, 0x0,
            "A flag defining the direction of the GTS Slot.", HFILL }},

        { &hf_ieee802154_bcn_pending16,
        { "Address",                    "wpan.bcn.pending16", FT_UINT16, BASE_HEX, NULL, 0x0,
            "Device with pending data to receive.", HFILL }},

        { &hf_ieee802154_bcn_pending64,
        { "Address",                    "wpan.bcn.pending64", FT_UINT64, BASE_HEX, NULL, 0x0,
            "Device with pending data to receive.", HFILL }}
    };

    static gint *ett[] = {
        &ett_ieee802154,
        &ett_ieee802154_fcf,
        &ett_ieee802154_fcs,
        &ett_ieee802154_cmd,
        &ett_ieee802154_cmd_cinfo,
        &ett_ieee802154_bcn,
        &ett_ieee802154_bcn_superframe_spec,
        &ett_ieee802154_bcn_gts_spec,
        &ett_ieee802154_bcn_gts_direction,
        &ett_ieee802154_bcn_gts_descriptors,
        &ett_ieee802154_bcn_pending
    };

    /*  Register Protocol name and description. */
    proto_ieee802154 = proto_register_protocol("IEEE 802.15.4 Low-Rate Wireless PAN", "IEEE 802.15.4", "wpan");

    /*  Register header fields and subtrees. */
    proto_register_field_array(proto_ieee802154, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));

    /* Register the subdissector list */
    register_heur_dissector_list("wpan", &ieee802154_heur_subdissector_list);

    /*  Register dissectors with Ethereal. */
    register_dissector("wpan", dissect_ieee802154, proto_ieee802154);
    register_dissector("wpan_nofcs", dissect_ieee802154_nofcs, proto_ieee802154);
    register_dissector("wpan_cc24xx", dissect_ieee802154_cc24xx, proto_ieee802154);
} /* proto_register_ieee802154 */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      proto_reg_handoff_ieee802154
 *  DESCRIPTION
 *      Registers the zigbee dissector with Wireshark.
 *      Will be called every time 'apply' is pressed in the preferences menu.
 *  PARAMETERS
 *      none
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
void proto_reg_handoff_ieee802154(void)
{
    dissector_handle_t  ieee802154_handle;

    /* Get the dissector handles. */
    ieee802154_handle   = find_dissector("wpan");
    data_handle         = find_dissector("data");

    /* Register dissector handles. */
    dissector_add("wtap_encap", WTAP_ENCAP_IEEE802_15_4, ieee802154_handle);
} /* proto_reg_handoff_ieee802154 */