/* packet-zbee-nwk-gp.c
 * Dissector routines for the ZigBee Green Power profile (GP)
 * Copyright 2013 DSR Corporation, http://dsr-wireless.com/
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * Used Owen Kirby's packet-zbee-aps module as a template. Based
 * on ZigBee Cluster Library Specification document 075123r02ZB
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/* Include files. */
#include "config.h"

#include <epan/packet.h>
#include <epan/exceptions.h>
#include <epan/prefs.h>
#include <epan/uat.h>
#include "packet-zbee.h"
#include "packet-zbee-nwk.h"
#include "packet-zbee-security.h"
#include "packet-zbee-aps.h"
#include "packet-zbee-zcl.h"

void proto_register_zbee_nwk_gp(void);
void proto_reg_handoff_zbee_nwk_gp(void);

/**************************/
/* Defines. */
/**************************/

/* ZigBee NWK GP FCF frame types. */
#define ZBEE_NWK_GP_FCF_DATA        0x00
#define ZBEE_NWK_GP_FCF_MAINTENANCE 0x01

/* ZigBee NWK GP FCF fields. */
#define ZBEE_NWK_GP_FCF_AUTO_COMMISSIONING  0x40
#define ZBEE_NWK_GP_FCF_CONTROL_EXTENSION   0x80
#define ZBEE_NWK_GP_FCF_FRAME_TYPE          0x03
#define ZBEE_NWK_GP_FCF_VERSION             0x3C

/* Extended NWK Frame Control field. */
#define ZBEE_NWK_GP_FCF_EXT_APP_ID          0x07 /* 0 - 2 b. */
#define ZBEE_NWK_GP_FCF_EXT_SECURITY_LEVEL  0x18 /* 3 - 4 b. */
#define ZBEE_NWK_GP_FCF_EXT_SECURITY_KEY    0x20 /* 5 b. */
#define ZBEE_NWK_GP_FCF_EXT_RX_AFTER_TX     0x40 /* 6 b. */
#define ZBEE_NWK_GP_FCF_EXT_DIRECTION       0x80 /* 7 b. */

/* Definitions for application IDs. */
#define ZBEE_NWK_GP_APP_ID_DEFAULT  0x00
#define ZBEE_NWK_GP_APP_ID_LPED     0x01
#define ZBEE_NWK_GP_APP_ID_ZGP      0x02

/* Definitions for GP directions. */
#define ZBEE_NWK_GP_FC_EXT_DIRECTION_DEFAULT    0x00
#define ZBEE_NWK_GP_FC_EXT_DIRECTION_FROM_ZGPD  0x00
#define ZBEE_NWK_GP_FC_EXT_DIRECTION_FROM_ZGPP  0x01

/* Definitions for ZGPD Source IDs. */
#define ZBEE_NWK_GP_ZGPD_SRCID_ALL      0xFFFFFFFF
#define ZBEE_NWK_GP_ZGPD_SRCID_UNKNOWN  0x00000000

/* Security level values. */
#define ZBEE_NWK_GP_SECURITY_LEVEL_NO       0x00
#define ZBEE_NWK_GP_SECURITY_LEVEL_1LSB     0x01
#define ZBEE_NWK_GP_SECURITY_LEVEL_FULL     0x02
#define ZBEE_NWK_GP_SECURITY_LEVEL_FULLENCR 0x03

/* GP Security key types. */
#define ZBEE_NWK_GP_SECURITY_KEY_TYPE_NO_KEY                            0x00
#define ZBEE_NWK_GP_SECURITY_KEY_TYPE_ZB_NWK_KEY                        0x01
#define ZBEE_NWK_GP_SECURITY_KEY_TYPE_GPD_GROUP_KEY                     0x02
#define ZBEE_NWK_GP_SECURITY_KEY_TYPE_NWK_KEY_DERIVED_GPD_KEY_GROUP_KEY 0x03
#define ZBEE_NWK_GP_SECURITY_KEY_TYPE_PRECONFIGURED_INDIVIDUAL_GPD_KEY  0x04
#define ZBEE_NWK_GP_SECURITY_KEY_TYPE_DERIVED_INDIVIDUAL_GPD_KEY        0x07

typedef struct {
    /* FCF Data. */
    guint8 frame_type;
    gboolean nwk_frame_control_extension;

    /* Ext FCF Data. */
    guint8 application_id;
    guint8 security_level;
    guint8 direction;

    /* Src ID. */
    guint32 source_id;

    /* Security Frame Counter. */
    guint32 security_frame_counter;

    /* MIC. */
    guint8 mic_size;
    guint32 mic;

    /* Application Payload. */
    guint8 payload_len;
} zbee_nwk_green_power_packet;

/* Commissioning command payload. */
#define NWK_CMD_SECURITY_KEY_LEN 16

/* Definitions for GP Commissioning command opt field (bitmask). */
#define ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_MAC_SEQ           0x01
#define ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_RX_ON_CAP         0x02
#define ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_MANUFACTURER_INFO 0x04
#define ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_PAN_ID_REQ        0x10
#define ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_GP_SEC_KEY_REQ    0x20
#define ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_FIXED_LOCATION    0x40
#define ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_EXT_OPTIONS       0x80

/* Definitions for GP Commissioning command ext_opt field (bitmask). */
#define ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_SEC_LEVEL_CAP     0x03
#define ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_KEY_TYPE          0x1C
#define ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_GPD_KEY_PRESENT   0x20
#define ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_GPD_KEY_ENCR      0x40
#define ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_OUT_COUNTER       0x80

/* Definitions for GP Commissioning command MS Extensions field (bitmask). */
#define ZBEE_NWK_GP_CMD_COMMISSIONING_MS_EXT_MIP    0x01
#define ZBEE_NWK_GP_CMD_COMMISSIONING_MS_EXT_MMIP   0x02
#define ZBEE_NWK_GP_CMD_COMMISSIONING_MS_EXT_GCLP   0x04
#define ZBEE_NWK_GP_CMD_COMMISSIONING_MS_EXT_CRP    0x08

/* Definitions for GP Decommissioning command opt field (bitmask). */
#define ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_PAN_ID_PRESENT    0x01
#define ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_SEC_KEY_PRESENT   0x02
#define ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_KEY_ENCR          0x04
#define ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_SEC_LEVEL         0x18
#define ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_KEY_TYPE          0xE0

/* Definitions for GP Channel Request command. */
#define ZBEE_NWK_GP_CMD_CHANNEL_REQUEST_1ST 0x0F
#define ZBEE_NWK_GP_CMD_CHANNEL_REQUEST_2ND 0xF0

/* GP Channel Configuration command. */
#define ZBEE_NWK_GP_CMD_CHANNEL_CONFIGURATION_OPERATION_CH 0x0F

/* GP GENERIC IDS. */
#define GPD_DEVICE_ID_GENERIC_GP_SIMPLE_GENERIC_1STATE_SWITCH   0x00
#define GPD_DEVICE_ID_GENERIC_GP_SIMPLE_GENERIC_2STATE_SWITCH   0x01
#define GPD_DEVICE_ID_GENERIC_GP_ON_OFF_SWITCH                  0x02
#define GPD_DEVICE_ID_GENERIC_GP_LEVEL_CONTROL_SWITCH           0x03
#define GPD_DEVICE_ID_GENERIC_GP_SIMPLE_SENSOR                  0x04
#define GPD_DEVICE_ID_GENERIC_GP_ADVANCED_GENERIC_1STATE_SWITCH 0x05
#define GPD_DEVICE_ID_GENERIC_GP_ADVANCED_GENERIC_2STATE_SWITCH 0x06

/* GP LIGHTING IDS. */
#define GPD_DEVICE_ID_LIGHTING_GP_COLOR_DIMMER_SWITCH           0x10
#define GPD_DEVICE_ID_LIGHTING_GP_LIGHT_SENSOR                  0x11
#define GPD_DEVICE_ID_LIGHTING_GP_OCCUPANCY_SENSOR              0x12

/* GP CLOSURES IDS. */
#define GPD_DEVICE_ID_CLOSURES_GP_DOOR_LOCK_CONTROLLER          0x20

/* HVAC IDS. */
#define GPD_DEVICE_ID_HVAC_GP_TEMPERATURE_SENSOR                0x30
#define GPD_DEVICE_ID_HVAC_GP_PRESSURE_SENSOR                   0x31
#define GPD_DEVICE_ID_HVAC_GP_FLOW_SENSOR                       0x32
#define GPD_DEVICE_ID_HVAC_GP_INDOOR_ENVIRONMENT_SENSOR         0x33

/* Manufacturer specific device. */
#define GPD_DEVICE_ID_MANUFACTURER_SPECIFIC                     0xFE

/* GPD manufacturers. */
#define ZBEE_NWK_GP_MANUF_ID_GREENPEAK      0x10D0

/* GPD devices by GreenPeak. */
#define ZBEE_NWK_GP_MANUF_GREENPEAK_IZDS    0x0000
#define ZBEE_NWK_GP_MANUF_GREENPEAK_IZDWS   0x0001
#define ZBEE_NWK_GP_MANUF_GREENPEAK_IZLS    0x0002
#define ZBEE_NWK_GP_MANUF_GREENPEAK_IZRHS   0x0003

/*********************/
/* Global variables. */
/*********************/

/* GP proto handle. */
static int proto_zbee_nwk_gp = -1;

/* GP NWK FC. */
static int hf_zbee_nwk_gp_auto_commissioning = -1;
static int hf_zbee_nwk_gp_fc_ext = -1;
static int hf_zbee_nwk_gp_fcf = -1;
static int hf_zbee_nwk_gp_frame_type = -1;
static int hf_zbee_nwk_gp_proto_version = -1;

/* GP NWK FC extension. */
static int hf_zbee_nwk_gp_fc_ext_field = -1;
static int hf_zbee_nwk_gp_fc_ext_app_id = -1;
static int hf_zbee_nwk_gp_fc_ext_direction = -1;
static int hf_zbee_nwk_gp_fc_ext_rx_after_tx = -1;
static int hf_zbee_nwk_gp_fc_ext_sec_key = -1;
static int hf_zbee_nwk_gp_fc_ext_sec_level = -1;

/* ZGPD Src ID. */
static int hf_zbee_nwk_gp_zgpd_src_id = -1;

/* Security frame counter. */
static int hf_zbee_nwk_gp_security_frame_counter = -1;

/* Security MIC. */
static int hf_zbee_nwk_gp_security_mic_2b = -1;
static int hf_zbee_nwk_gp_security_mic_4b = -1;

/* Payload subframe. */
static int hf_zbee_nwk_gp_command_id = -1;

/* Commissioning. */
static int hf_zbee_nwk_gp_cmd_comm_device_id = -1;
static int hf_zbee_nwk_gp_cmd_comm_ext_opt = -1;
static int hf_zbee_nwk_gp_cmd_comm_ext_opt_gpd_key_encr = -1;
static int hf_zbee_nwk_gp_cmd_comm_ext_opt_gpd_key_present = -1;
static int hf_zbee_nwk_gp_cmd_comm_ext_opt_key_type = -1;
static int hf_zbee_nwk_gp_cmd_comm_ext_opt_outgoing_counter = -1;
static int hf_zbee_nwk_gp_cmd_comm_ext_opt_sec_level_cap = -1;
static int hf_zbee_nwk_gp_cmd_comm_security_key = -1;
static int hf_zbee_nwk_gp_cmd_comm_gpd_sec_key_mic = -1;
static int hf_zbee_nwk_gp_cmd_comm_opt_ext_opt = -1;
static int hf_zbee_nwk_gp_cmd_comm_opt = -1;
static int hf_zbee_nwk_gp_cmd_comm_opt_fixed_location = -1;
static int hf_zbee_nwk_gp_cmd_comm_opt_mac_sec_num_cap = -1;
static int hf_zbee_nwk_gp_cmd_comm_opt_ms_ext_present = -1;
static int hf_zbee_nwk_gp_cmd_comm_opt_panid_req = -1;
static int hf_zbee_nwk_gp_cmd_comm_opt_rx_on_cap = -1;
static int hf_zbee_nwk_gp_cmd_comm_opt_sec_key_req = -1;
static int hf_zbee_nwk_gp_cmd_comm_outgoing_counter = -1;
static int hf_zbee_nwk_gp_cmd_comm_manufacturer_greenpeak_dev_id = -1;
static int hf_zbee_nwk_gp_cmd_comm_manufacturer_dev_id = -1;
static int hf_zbee_nwk_gp_cmd_comm_manufacturer_id = -1;
static int hf_zbee_nwk_gp_cmd_comm_ms_ext = -1;
static int hf_zbee_nwk_gp_cmd_comm_ms_ext_crp = -1;
static int hf_zbee_nwk_gp_cmd_comm_ms_ext_gclp = -1;
static int hf_zbee_nwk_gp_cmd_comm_ms_ext_mip = -1;
static int hf_zbee_nwk_gp_cmd_comm_ms_ext_mmip = -1;

/* Commissioning reply. */
static int hf_zbee_nwk_gp_cmd_comm_rep_opt = -1;
static int hf_zbee_nwk_gp_cmd_comm_rep_opt_key_encr = -1;
static int hf_zbee_nwk_gp_cmd_comm_rep_opt_panid_present = -1;
static int hf_zbee_nwk_gp_cmd_comm_rep_opt_sec_key_present = -1;
static int hf_zbee_nwk_gp_cmd_comm_rep_opt_sec_level = -1;
static int hf_zbee_nwk_gp_cmd_comm_rep_opt_sec_type = -1;
static int hf_zbee_nwk_gp_cmd_comm_rep_pan_id = -1;

/* Attribute reporting. */
static int hf_zbee_nwk_gp_cmd_attr_report_cluster_id = -1;

/* Channel request. */
static int hf_zbee_nwk_gp_cmd_channel_request_toggling_behaviour = -1;
static int hf_zbee_nwk_gp_cmd_channel_request_toggling_behaviour_1st = -1;
static int hf_zbee_nwk_gp_cmd_channel_request_toggling_behaviour_2nd = -1;

/* Channel Configuration command. */
static int hf_zbee_nwk_gp_cmd_operational_channel = -1;
static int hf_zbee_nwk_gp_cmd_channel_configuration = -1;

/* Move Color command. */
static int hf_zbee_nwk_gp_cmd_move_color_ratex = -1;
static int hf_zbee_nwk_gp_cmd_move_color_ratey = -1;

/* Move Up/Down command. */
static int hf_zbee_nwk_gp_cmd_move_up_down_rate = -1;

/* Step Color command. */
static int hf_zbee_nwk_gp_cmd_step_color_stepx = -1;
static int hf_zbee_nwk_gp_cmd_step_color_stepy = -1;
static int hf_zbee_nwk_gp_cmd_step_color_transition_time = -1;

/* Step Up/Down command. */
static int hf_zbee_nwk_gp_cmd_step_up_down_step_size = -1;
static int hf_zbee_nwk_gp_cmd_step_up_down_transition_time = -1;

/* Proto tree elements. */
static gint ett_zbee_nwk = -1;
static gint ett_zbee_nwk_cmd = -1;
static gint ett_zbee_nwk_cmd_cinfo = -1;
static gint ett_zbee_nwk_cmd_ms_ext = -1;
static gint ett_zbee_nwk_cmd_options = -1;
static gint ett_zbee_nwk_fcf = -1;
static gint ett_zbee_nwk_fcf_ext = -1;

/* Common. */
static dissector_handle_t data_handle;
static GSList *zbee_gp_keyring = NULL;
static guint num_uat_key_records = 0;

typedef struct {
    gchar *string;
    guint8 byte_order;
    gchar *label;
    guint8 key[ZBEE_SEC_CONST_KEYSIZE];
} uat_key_record_t;

static uat_key_record_t *gp_uat_key_records = NULL;
static uat_t *zbee_gp_sec_key_table_uat;

/* UAT. */
UAT_CSTRING_CB_DEF(gp_uat_key_records, string, uat_key_record_t)
UAT_VS_DEF(gp_uat_key_records, byte_order, uat_key_record_t, guint8, 0, "Normal")
UAT_CSTRING_CB_DEF(gp_uat_key_records, label, uat_key_record_t)

/****************/
/* Field names. */
/****************/

/* Byte order. */
static const value_string byte_order_vals[] = {
    { 0, "Normal"},
    { 1, "Reverse"},

    { 0, NULL }
};

/* Application ID names. */
static const value_string zbee_nwk_gp_app_id_names[] = {
    { ZBEE_NWK_GP_APP_ID_LPED, "LPED" },
    { ZBEE_NWK_GP_APP_ID_ZGP, "ZGP" },

    { 0, NULL }
};

/* Green Power commands. */

/* Abbreviations:
 * GPDF commands sent:
 *   From GPD w/o payload: "F "
 *   From GPD w   payload: "FP"
 *   To GPD:               "T "
 */

#define zbee_nwk_gp_cmd_names_VALUE_STRING_LIST(XXX) \
    XXX( /*F */ ZB_GP_CMD_ID_IDENTIFY                                 , 0x00, "Identify" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE0                                   , 0x10, "Scene 0" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE1                                   , 0x11, "Scene 1" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE2                                   , 0x12, "Scene 2" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE3                                   , 0x13, "Scene 3" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE4                                   , 0x14, "Scene 4" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE5                                   , 0x15, "Scene 5" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE6                                   , 0x16, "Scene 6" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE7                                   , 0x17, "Scene 7" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE8                                   , 0x18, "Scene 8" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE9                                   , 0x19, "Scene 9" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE10                                  , 0x1A, "Scene 10" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE11                                  , 0x1B, "Scene 11" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE12                                  , 0x1C, "Scene 12" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE13                                  , 0x1D, "Scene 13" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE14                                  , 0x1E, "Scene 14" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SCENE15                                  , 0x1F, "Scene 15" ) \
    XXX( /*F */ ZB_GP_CMD_ID_OFF                                      , 0x20, "Off" ) \
    XXX( /*F */ ZB_GP_CMD_ID_ON                                       , 0x21, "On" ) \
    XXX( /*F */ ZB_GP_CMD_ID_TOGGLE                                   , 0x22, "Toggle" ) \
    XXX( /*F */ ZB_GP_CMD_ID_RELEASE                                  , 0x23, "Release" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_MOVE_UP                                  , 0x30, "Move Up" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_MOVE_DOWN                                , 0x31, "Move Down" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_STEP_UP                                  , 0x32, "Step Up" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_STEP_DOWN                                , 0x33, "Step Down" ) \
    XXX( /*F */ ZB_GP_CMD_ID_LEVEL_CONTROL_STOP                       , 0x34, "Level Control/Stop" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_MOVE_UP_WITH_ON_OFF                      , 0x35, "Move Up (with On/Off)" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_MOVE_DOWN_WITH_ON_OFF                    , 0x36, "Move Down (with On/Off)" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_STEP_UP_WITH_ON_OFF                      , 0x37, "Step Up (with On/Off)" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_STEP_DOWN_WITH_ON_OFF                    , 0x38, "Step Down (with On/Off)" ) \
    XXX( /*F */ ZB_GP_CMD_ID_MOVE_HUE_STOP                            , 0x40, "Move Hue Stop" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_MOVE_HUE_UP                              , 0x41, "Move Hue Up" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_MOVE_HUE_DOWN                            , 0x42, "Move Hue Down" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_STEP_HUE_UP                              , 0x43, "Step Hue Up" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_STEP_HUW_DOWN                            , 0x44, "Step Hue Down" ) \
    XXX( /*F */ ZB_GP_CMD_ID_MOVE_SATURATION_STOP                     , 0x45, "Move Saturation Stop" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_MOVE_SATUREATION_UP                      , 0x46, "Move Saturation Up" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_MOVE_SATUREATION_DOWN                    , 0x47, "Move Saturation Down" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_STEP_SATURATION_UP                       , 0x48, "Step Saturation Up" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_STEP_SATURATION_DOWN                     , 0x49, "Step Saturation Down" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_MOVE_COLOR                               , 0x4A, "Move Color" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_STEP_COLOR                               , 0x4B, "Step Color" ) \
    XXX( /*F */ ZB_GP_CMD_ID_LOCK_DOOR                                , 0x50, "Lock Door" ) \
    XXX( /*F */ ZB_GP_CMD_ID_UNLOCK_DOOR                              , 0x51, "Unlock Door" ) \
    XXX( /*F */ ZB_GP_CMD_ID_PRESS11                                  , 0x60, "Press 1 of 1" ) \
    XXX( /*F */ ZB_GP_CMD_ID_RELEASE11                                , 0x61, "Release 1 of 1" ) \
    XXX( /*F */ ZB_GP_CMD_ID_PRESS12                                  , 0x62, "Press 1 of 2" ) \
    XXX( /*F */ ZB_GP_CMD_ID_RELEASE12                                , 0x63, "Release 1 of 2" ) \
    XXX( /*F */ ZB_GP_CMD_ID_PRESS22                                  , 0x64, "Press 2 of 2" ) \
    XXX( /*F */ ZB_GP_CMD_ID_RELEASE22                                , 0x65, "Release 2 of 2" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SHORT_PRESS11                            , 0x66, "Short press 1 of 1" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SHORT_PRESS12                            , 0x67, "Short press 1 of 2" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SHORT_PRESS22                            , 0x68, "Short press 2 of 2" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_ATTRIBUTE_REPORTING                      , 0xA0, "Attribute reporting" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_MANUFACTURE_SPECIFIC_ATTR_REPORTING      , 0xA1, "Manufacturer-specific attribute reporting" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_MULTI_CLUSTER_REPORTING                  , 0xA2, "Multi-cluster reporting" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_MANUFACTURER_SPECIFIC_MCLUSTER_REPORTING , 0xA3, "Manufacturer-specific multi-cluster reporting" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_REQUEST_ATTRIBUTES                       , 0xA4, "Request Attributes" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_READ_ATTRIBUTES_RESPONSE                 , 0xA5, "Read Attributes Response" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_ANY_SENSOR_COMMAND_A0_A3                 , 0xAF, "Any GPD sensor command (0xA0 - 0xA3)" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_COMMISSIONING                            , 0xE0, "Commissioning" ) \
    XXX( /*F */ ZB_GP_CMD_ID_DECOMMISSIONING                          , 0xE1, "Decommissioning" ) \
    XXX( /*F */ ZB_GP_CMD_ID_SUCCESS                                  , 0xE2, "Success" ) \
    XXX( /*FP*/ ZB_GP_CMD_ID_CHANNEL_REQUEST                          , 0xE3, "Channel Request" ) \
    XXX( /*T */ ZB_GP_CMD_ID_COMMISSIONING_REPLY                      , 0xF0, "Commissioning Reply" ) \
    XXX( /*T */ ZB_GP_CMD_ID_WRITE_ATTRIBUTES                         , 0xF1, "Write Attributes" ) \
    XXX( /*T */ ZB_GP_CMD_ID_READ_ATTRIBUTES                          , 0xF2, "Read Attributes" ) \
    XXX( /*T */ ZB_GP_CMD_ID_CHANNEL_CONFIGURATION                    , 0xF3, "Channel Configuration" )

VALUE_STRING_ENUM(zbee_nwk_gp_cmd_names);

VALUE_STRING_ARRAY(zbee_nwk_gp_cmd_names);
static value_string_ext zbee_nwk_gp_cmd_names_ext = VALUE_STRING_EXT_INIT(zbee_nwk_gp_cmd_names);


/* Green Power devices. */
static const value_string zbee_nwk_gp_device_ids_names[] = {

    /* GP GENERIC */
    { GPD_DEVICE_ID_GENERIC_GP_SIMPLE_GENERIC_1STATE_SWITCH,   "Generic: GP Simple Generic 1-state Switch" },
    { GPD_DEVICE_ID_GENERIC_GP_SIMPLE_GENERIC_2STATE_SWITCH,   "Generic: GP Simple Generic 2-state Switch" },
    { GPD_DEVICE_ID_GENERIC_GP_ON_OFF_SWITCH,                  "Generic: GP On/Off Switch" },
    { GPD_DEVICE_ID_GENERIC_GP_LEVEL_CONTROL_SWITCH,           "Generic: GP Level Control Switch" },
    { GPD_DEVICE_ID_GENERIC_GP_SIMPLE_SENSOR,                  "Generic: GP Simple Sensor" },
    { GPD_DEVICE_ID_GENERIC_GP_ADVANCED_GENERIC_1STATE_SWITCH, "Generic: GP Advanced Generic 1-state Switch" },
    { GPD_DEVICE_ID_GENERIC_GP_ADVANCED_GENERIC_2STATE_SWITCH, "Generic: GP Advanced Generic 2-state Switch" },

    /* GP LIGHTING */
    { GPD_DEVICE_ID_LIGHTING_GP_COLOR_DIMMER_SWITCH,           "Lighting: GP Color Dimmer Switch" },
    { GPD_DEVICE_ID_LIGHTING_GP_LIGHT_SENSOR,                  "Lighting: GP Light Sensor" },
    { GPD_DEVICE_ID_LIGHTING_GP_OCCUPANCY_SENSOR,              "Lighting: GP Occupancy Sensor" },

    /* GP CLOSURES */
    { GPD_DEVICE_ID_CLOSURES_GP_DOOR_LOCK_CONTROLLER,          "Closures: GP Door Lock Controller" },

    /* HVAC */
    { GPD_DEVICE_ID_HVAC_GP_TEMPERATURE_SENSOR,                "HVAC: GP Temperature Sensor" },
    { GPD_DEVICE_ID_HVAC_GP_PRESSURE_SENSOR,                   "HVAC: GP Pressure Sensor" },
    { GPD_DEVICE_ID_HVAC_GP_FLOW_SENSOR,                       "HVAC: GP Flow Sensor" },
    { GPD_DEVICE_ID_HVAC_GP_INDOOR_ENVIRONMENT_SENSOR,         "HVAC: GP Indoor Environment Sensor" },

    /* CUSTOM */
    { GPD_DEVICE_ID_MANUFACTURER_SPECIFIC,                     "Manufacturer Specific" },

    { 0, NULL }
};
static value_string_ext zbee_nwk_gp_device_ids_names_ext = VALUE_STRING_EXT_INIT(zbee_nwk_gp_device_ids_names);

/* GP directions. */
static const value_string zbee_nwk_gp_directions[] = {
    { ZBEE_NWK_GP_FC_EXT_DIRECTION_FROM_ZGPD, "From ZGPD" },
    { ZBEE_NWK_GP_FC_EXT_DIRECTION_FROM_ZGPP, "From ZGPP" },

    { 0, NULL }
};

/* Frame types for Green Power profile. */
static const value_string zbee_nwk_gp_frame_types[] = {
    { ZBEE_NWK_GP_FCF_DATA,        "Data" },
    { ZBEE_NWK_GP_FCF_MAINTENANCE, "Maintenance" },

    { 0, NULL }
};

/* GreenPeak Green Power devices. */
static const value_string zbee_nwk_gp_manufacturer_greenpeak_dev_names[] = {
    { ZBEE_NWK_GP_MANUF_GREENPEAK_IZDS,  "IAS Zone Door Sensor" },
    { ZBEE_NWK_GP_MANUF_GREENPEAK_IZDWS, "IAS Zone Door/Window Sensor" },
    { ZBEE_NWK_GP_MANUF_GREENPEAK_IZLS,  "IAS Zone Leakage Sensor" },
    { ZBEE_NWK_GP_MANUF_GREENPEAK_IZRHS, "IAS Zone Relative Humidity Sensor" },

    { 0, NULL }
};

/* GP Src ID names. */
static const value_string zbee_nwk_gp_src_id_names[] = {
    { ZBEE_NWK_GP_ZGPD_SRCID_ALL,     "All" },
    { ZBEE_NWK_GP_ZGPD_SRCID_UNKNOWN, "Unspecified" },

    { 0, NULL }
};

/* GP security key type names. */
static const value_string zbee_nwk_gp_src_sec_keys_type_names[] = {
    { ZBEE_NWK_GP_SECURITY_KEY_TYPE_DERIVED_INDIVIDUAL_GPD_KEY,        "Derived individual GPD key" },
    { ZBEE_NWK_GP_SECURITY_KEY_TYPE_GPD_GROUP_KEY,                     "GPD group key" },
    { ZBEE_NWK_GP_SECURITY_KEY_TYPE_NO_KEY,                            "No key" },
    { ZBEE_NWK_GP_SECURITY_KEY_TYPE_NWK_KEY_DERIVED_GPD_KEY_GROUP_KEY, "NWK key derived GPD group key" },
    { ZBEE_NWK_GP_SECURITY_KEY_TYPE_PRECONFIGURED_INDIVIDUAL_GPD_KEY,  "Individual, out of the box GPD key" },
    { ZBEE_NWK_GP_SECURITY_KEY_TYPE_ZB_NWK_KEY,                        "ZigBee NWK key" },

    { 0, NULL }
};

/* GP security levels. */
static const value_string zbee_nwk_gp_src_sec_levels_names[] = {
    { ZBEE_NWK_GP_SECURITY_LEVEL_1LSB,     "1 LSB of frame counter and short MIC only" },
    { ZBEE_NWK_GP_SECURITY_LEVEL_FULL,     "Full frame counter and full MIC only" },
    { ZBEE_NWK_GP_SECURITY_LEVEL_FULLENCR, "Encryption with full frame counter and full MIC" },
    { ZBEE_NWK_GP_SECURITY_LEVEL_NO,       "No security" },

    { 0, NULL }
};

/*************************/
/* Function definitions. */
/*************************/

/* UAT record copy callback. */
static void *
uat_key_record_copy_cb(void *n, const void *o, size_t siz _U_)
{
    uat_key_record_t *new_key = (uat_key_record_t *)n;
    const uat_key_record_t *old_key = (const uat_key_record_t *)o;

    if (old_key->string) {
        new_key->string = g_strdup(old_key->string);
    } else {
        new_key->string = NULL;
    }
    if (old_key->label) {
        new_key->label = g_strdup(old_key->label);
    } else {
        new_key->label = NULL;
    }
    return new_key;
}

/* UAT record free callback. */
static void
uat_key_record_free_cb(void *r)
{
    uat_key_record_t *key = (uat_key_record_t *)r;
    if (key->string) {
        g_free(key->string);
    }
    if (key->label) {
        g_free(key->label);
    }
}

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      zbee_gp_security_parse_key
 *  DESCRIPTION
 *      Parses a key string from left to right into a buffer with increasing (normal byte order) or decreasing (reverse
 *      byte order) address.
 *  PARAMETERS
 *      const gchar    *key_str    - pointer to the string
 *      guint8         *key_buf    - destination buffer in memory
 *      gboolean        byte_order - byte order
 *  RETURNS
 *      gboolean
 *---------------------------------------------------------------
 */
static gboolean
zbee_gp_security_parse_key(const gchar *key_str, guint8 *key_buf, gboolean byte_order)
{
    gboolean string_mode = FALSE;
    gchar temp;
    int i, j;

    memset(key_buf, 0, ZBEE_SEC_CONST_KEYSIZE);
    if (key_str == NULL) {
        return FALSE;
    }
    if ((temp = *key_str++) == '"') {
        string_mode = TRUE;
        temp = *key_str++;
    }
    j = byte_order ? ZBEE_SEC_CONST_KEYSIZE - 1 : 0;
    for (i = ZBEE_SEC_CONST_KEYSIZE - 1; i >= 0; i--) {
        if (string_mode) {
            if (g_ascii_isprint(temp)) {
                key_buf[j] = temp;
                temp = *key_str++;
            } else {
                return FALSE;
            }
        } else {
            if ((temp == ':') || (temp == '-') || (temp == ' ')) {
                temp = *(key_str++);
            }
            if (g_ascii_isxdigit(temp)) {
                key_buf[j] = g_ascii_xdigit_value(temp) << 4;
            } else {
                return FALSE;
            }
            temp = *(key_str++);
            if (g_ascii_isxdigit(temp)) {
                key_buf[j] |= g_ascii_xdigit_value(temp);
            } else {
                return FALSE;
            }
            temp = *(key_str++);
        }
        if (byte_order) {
            j--;
        } else {
            j++;
        }
    }
    return TRUE;
}

/* UAT record update callback. */
static gboolean
uat_key_record_update_cb(void *r, char **err)
{
    uat_key_record_t *rec = (uat_key_record_t *)r;

    if (rec->string == NULL) {
         *err = g_strdup_printf("Key can't be blank.");
         return FALSE;
    } else {
        g_strstrip(rec->string);
        if (rec->string[0] != 0) {
            *err = NULL;
            if (!zbee_gp_security_parse_key(rec->string, rec->key, rec->byte_order)) {
                *err = g_strdup_printf("Expecting %d hexadecimal bytes or a %d character double-quoted string",
                    ZBEE_SEC_CONST_KEYSIZE, ZBEE_SEC_CONST_KEYSIZE);
                return FALSE;
            }
        } else {
            *err = g_strdup_printf("Key can't be blank.");
            return FALSE;
        }
    }
    return TRUE;
}

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_zbee_nwk_gp_cmd_commissioning
 *  DESCRIPTION
 *      Dissector for ZigBee Green Power commissioning.
 *  PARAMETERS
 *      tvbuff_t *tvb                       - pointer to buffer containing raw packet.
 *      packet_into *pinfo                  - pointer to packet information fields.
 *      proto_tree *tree                    - pointer to data tree Wireshark uses to display packet.
 *      zbee_nwk_green_power_packet *packet - packet data.
 *      guint offset                        - current payload offset.
 *  RETURNS
 *      guint                               - payload processed offset.
 *---------------------------------------------------------------
 */
static guint
dissect_zbee_nwk_gp_cmd_commissioning(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
    zbee_nwk_green_power_packet *packet _U_, guint offset)
{
    guint8 comm_options;
    guint8 comm_ext_options = 0;
    guint8 ms_ext_options = 0;
    guint16 manufacturer_id = 0;
    static const int * options[] = {
        &hf_zbee_nwk_gp_cmd_comm_opt_mac_sec_num_cap,
        &hf_zbee_nwk_gp_cmd_comm_opt_rx_on_cap,
        &hf_zbee_nwk_gp_cmd_comm_opt_ms_ext_present,
        &hf_zbee_nwk_gp_cmd_comm_opt_panid_req,
        &hf_zbee_nwk_gp_cmd_comm_opt_sec_key_req,
        &hf_zbee_nwk_gp_cmd_comm_opt_fixed_location,
        &hf_zbee_nwk_gp_cmd_comm_opt_ext_opt,
        NULL
    };
    static const int * ext_options[] = {
        &hf_zbee_nwk_gp_cmd_comm_ext_opt_sec_level_cap,
        &hf_zbee_nwk_gp_cmd_comm_ext_opt_key_type,
        &hf_zbee_nwk_gp_cmd_comm_ext_opt_gpd_key_present,
        &hf_zbee_nwk_gp_cmd_comm_ext_opt_gpd_key_encr,
        &hf_zbee_nwk_gp_cmd_comm_ext_opt_outgoing_counter,
        NULL
    };
    static const int * ms_ext[] = {
        &hf_zbee_nwk_gp_cmd_comm_ms_ext_mip,
        &hf_zbee_nwk_gp_cmd_comm_ms_ext_mmip,
        &hf_zbee_nwk_gp_cmd_comm_ms_ext_gclp,
        &hf_zbee_nwk_gp_cmd_comm_ms_ext_crp,
        NULL
    };

    /* Get Device ID and display it. */
    proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_comm_device_id, tvb, offset, 1, ENC_LITTLE_ENDIAN);
    offset += 1;
    /* Get Options Field, build subtree and display the results. */
    comm_options = tvb_get_guint8(tvb, offset);
    proto_tree_add_bitmask(tree, tvb, offset, hf_zbee_nwk_gp_cmd_comm_opt, ett_zbee_nwk_cmd_options, options, ENC_NA);
    offset += 1;

    if (comm_options & ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_EXT_OPTIONS) {
        /* Get extended Options Field, build subtree and display the results. */
        comm_ext_options = tvb_get_guint8(tvb, offset);
        proto_tree_add_bitmask(tree, tvb, offset, hf_zbee_nwk_gp_cmd_comm_ext_opt, ett_zbee_nwk_cmd_options, ext_options, ENC_NA);
        offset += 1;
        if (comm_ext_options & ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_GPD_KEY_PRESENT) {
            /* Get security key and display it. */
            proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_comm_security_key, tvb, offset, NWK_CMD_SECURITY_KEY_LEN, ENC_NA);
            offset += NWK_CMD_SECURITY_KEY_LEN;
        }
        if (comm_ext_options & ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_GPD_KEY_ENCR) {
            /* Get Security MIC and display it. */
            proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_comm_gpd_sec_key_mic, tvb, offset, 4, ENC_LITTLE_ENDIAN);
            offset += 4;
        }
        if (comm_ext_options & ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_OUT_COUNTER) {
            /* Get GPD Outgoing Frame Counter and display it. */
            proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_comm_outgoing_counter, tvb, offset, 4, ENC_LITTLE_ENDIAN);
            offset += 4;
        }
    }
    /* Display manufacturer specific data. */
    if (comm_options & ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_MANUFACTURER_INFO) {
        /* Display MS extensions. */
        ms_ext_options = tvb_get_guint8(tvb, offset);
        proto_tree_add_bitmask(tree, tvb, offset, hf_zbee_nwk_gp_cmd_comm_ms_ext, ett_zbee_nwk_cmd_ms_ext, ms_ext, ENC_NA);
        offset += 1;
        if (ms_ext_options & ZBEE_NWK_GP_CMD_COMMISSIONING_MS_EXT_MIP) {
            /* Get Manufacturer ID. */
            manufacturer_id = tvb_get_letohs(tvb, offset);
            proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_comm_manufacturer_id, tvb, offset, 2, ENC_LITTLE_ENDIAN);
            offset += 2;
        }
        if (ms_ext_options & ZBEE_NWK_GP_CMD_COMMISSIONING_MS_EXT_MMIP) {
            /* Get Manufacturer Device ID. */
            switch (manufacturer_id) {
                case ZBEE_NWK_GP_MANUF_ID_GREENPEAK:
                    proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_comm_manufacturer_greenpeak_dev_id, tvb, offset, 2,
                        ENC_LITTLE_ENDIAN);
                    offset += 2;
                    break;
                default:
                    proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_comm_manufacturer_dev_id, tvb, offset, 2, ENC_LITTLE_ENDIAN);
                    offset += 2;
                    break;
            }
        }
    }
    return offset;
} /* dissect_zbee_nwk_gp_cmd_commissioning */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_zbee_nwk_gp_cmd_channel_request
 *  DESCRIPTION
 *      Dissector for ZigBee Green Power channel request.
 *  PARAMETERS
 *      tvbuff_t *tvb                       - pointer to buffer containing raw packet.
 *      packet_into *pinfo                  - pointer to packet information fields.
 *      proto_tree *tree                    - pointer to data tree Wireshark uses to display packet.
 *      zbee_nwk_green_power_packet *packet - packet data.
 *      guint offset                        - current payload offset.
 *  RETURNS
 *      guint                               - payload processed offset.
 *---------------------------------------------------------------
 */
static guint
dissect_zbee_nwk_gp_cmd_channel_request(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
    zbee_nwk_green_power_packet *packet _U_, guint offset)
{
    static const int * channels[] = {
        &hf_zbee_nwk_gp_cmd_channel_request_toggling_behaviour_1st,
        &hf_zbee_nwk_gp_cmd_channel_request_toggling_behaviour_2nd,
        NULL
    };

    /* Get Command Options Field, build subtree and display the results. */
    proto_tree_add_bitmask(tree, tvb, offset, hf_zbee_nwk_gp_cmd_channel_request_toggling_behaviour, ett_zbee_nwk_cmd_options, channels, ENC_NA);
    offset += 1;
    return offset;
} /* dissect_zbee_nwk_gp_cmd_channel_request */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_zbee_nwk_gp_cmd_channel_configuration
 *  DESCRIPTION
 *      Dissector for ZigBee Green Power channel configuration.
 *  PARAMETERS
 *      tvbuff_t *tvb                       - pointer to buffer containing raw packet.
 *      packet_into *pinfo                  - pointer to packet information fields.
 *      proto_tree *tree                    - pointer to data tree Wireshark uses to display packet.
 *      zbee_nwk_green_power_packet *packet - packet data.
 *      guint offset                        - current payload offset.
 *  RETURNS
 *      guint                               - payload processed offset.
 *---------------------------------------------------------------
 */
static guint
dissect_zbee_nwk_gp_cmd_channel_configuration(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
    zbee_nwk_green_power_packet *packet _U_, guint offset)
{
    static const int * channels[] = {
        &hf_zbee_nwk_gp_cmd_channel_configuration,
        NULL
    };

    /* Get Command Options Field, build subtree and display the results. */
    proto_tree_add_bitmask(tree, tvb, offset, hf_zbee_nwk_gp_cmd_operational_channel, ett_zbee_nwk_cmd_options, channels, ENC_NA);

    offset += 1;
    return offset;
} /* dissect_zbee_nwk_gp_cmd_channel_configuration */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_zbee_nwk_gp_cmd_attr_reporting
 *  DESCRIPTION
 *      Dissector for ZigBee Green Power commands attrib reporting.
 *  PARAMETERS
 *      tvbuff_t *tvb                       - pointer to buffer containing raw packet.
 *      packet_into *pinfo                  - pointer to packet information fields.
 *      proto_tree *tree                    - pointer to data tree Wireshark uses to display packet.
 *      zbee_nwk_green_power_packet *packet - packet data.
 *      guint offset                        - current payload offset.
 *  RETURNS
 *      guint                               - payload processed offset.
 *---------------------------------------------------------------
 */
static guint
dissect_zbee_nwk_gp_cmd_attr_reporting(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
    zbee_nwk_green_power_packet *packet _U_, guint offset)
{
    guint16 cluster_id;
    proto_tree *field_tree;

    /* Get cluster ID and add it into the tree. */
    cluster_id = tvb_get_letohs(tvb, offset);
    proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_attr_report_cluster_id, tvb, offset, 2, ENC_LITTLE_ENDIAN);

    offset += 2;
    /* Create subtree and parse ZCL Write Attribute Payload. */
    field_tree = proto_tree_add_subtree_format(tree, tvb, offset, 2, ett_zbee_nwk_cmd_options, NULL,
                                "Attribute reporting command for cluster: 0x%02X", cluster_id);
    dissect_zcl_write_attr(tvb, pinfo, field_tree, &offset, cluster_id);

    return offset;
} /* dissect_zbee_nwk_gp_cmd_attr_reporting */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_zbee_nwk_gp_cmd_commissioning_reply
 *  DESCRIPTION
 *      Dissector for ZigBee Green Power comissioning reply.
 *  PARAMETERS
 *      tvbuff_t *tvb                       - pointer to buffer containing raw packet.
 *      packet_into *pinfo                  - pointer to packet information fields.
 *      proto_tree *tree                    - pointer to data tree Wireshark uses to display packet.
 *      zbee_nwk_green_power_packet *packet - packet data.
 *      guint offset                        - current payload offset.
 *  RETURNS
 *      guint                               - payload processed offset.
 *---------------------------------------------------------------
 */
static guint
dissect_zbee_nwk_gp_cmd_commissioning_reply(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
    zbee_nwk_green_power_packet *packet _U_, guint offset)
{
    guint8 cr_options;

    static const int * options[] = {
        &hf_zbee_nwk_gp_cmd_comm_rep_opt_panid_present,
        &hf_zbee_nwk_gp_cmd_comm_rep_opt_sec_key_present,
        &hf_zbee_nwk_gp_cmd_comm_rep_opt_key_encr,
        &hf_zbee_nwk_gp_cmd_comm_rep_opt_sec_level,
        &hf_zbee_nwk_gp_cmd_comm_rep_opt_sec_type,
        NULL
    };

    /* Get Options Field, build subtree and display the results. */
    cr_options = tvb_get_guint8(tvb, offset);
    proto_tree_add_bitmask(tree, tvb, offset, hf_zbee_nwk_gp_cmd_comm_rep_opt, ett_zbee_nwk_cmd_options, options, ENC_NA);
    offset += 1;

    /* Parse and display security Pan ID value. */
    if (cr_options & ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_PAN_ID_PRESENT) {
        proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_comm_rep_pan_id, tvb, offset, 2, ENC_LITTLE_ENDIAN);
        offset += 2;
    }
    /* Parse and display security key. */
    if (cr_options & ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_SEC_KEY_PRESENT) {
        proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_comm_security_key, tvb, offset, NWK_CMD_SECURITY_KEY_LEN, ENC_NA);
        offset += NWK_CMD_SECURITY_KEY_LEN;
    }
    /* Parse and display security MIC. */
    if ((cr_options & ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_KEY_ENCR) && (cr_options &
        ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_SEC_KEY_PRESENT)) {
        proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_comm_gpd_sec_key_mic, tvb, offset, 4, ENC_LITTLE_ENDIAN);
        offset += 4;
    }
    return offset;
} /* dissect_zbee_nwk_gp_cmd_commissioning_reply */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_zbee_nwk_gp_cmd_move_color
 *  DESCRIPTION
 *      Dissector for ZigBee Green Power Move Color.
 *  PARAMETERS
 *      tvbuff_t *tvb                       - pointer to buffer containing raw packet.
 *      packet_into *pinfo                  - pointer to packet information fields.
 *      proto_tree *tree                    - pointer to data tree Wireshark uses to display packet.
 *      zbee_nwk_green_power_packet *packet - packet data.
 *      guint offset                        - current payload offset.
 *  RETURNS
 *      guint                               - payload processed offset.
 *---------------------------------------------------------------
 */
static guint
dissect_zbee_nwk_gp_cmd_move_color(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
    zbee_nwk_green_power_packet *packet _U_, guint offset)
{
    proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_move_color_ratex, tvb, offset, 2, ENC_LITTLE_ENDIAN);
    offset += 2;
    proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_move_color_ratey, tvb, offset, 2, ENC_LITTLE_ENDIAN);
    offset += 2;
    return offset;
} /* dissect_zbee_nwk_gp_cmd_move_color */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_zbee_nwk_gp_cmd_move_up_down
 *  DESCRIPTION
 *      Dissector for ZigBee Green Power Move Up/Down.
 *  PARAMETERS
 *      tvbuff_t *tvb                       - pointer to buffer containing raw packet.
 *      packet_into *pinfo                  - pointer to packet information fields.
 *      proto_tree *tree                    - pointer to data tree Wireshark uses to display packet.
 *      zbee_nwk_green_power_packet *packet - packet data.
 *      guint offset                        - current payload offset.
 *  RETURNS
 *      guint                               - payload processed offset.
 *---------------------------------------------------------------
 */
static guint
dissect_zbee_nwk_gp_cmd_move_up_down(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
    zbee_nwk_green_power_packet *packet _U_, guint offset)
{
    proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_move_up_down_rate, tvb, offset, 1, ENC_LITTLE_ENDIAN);
    offset += 1;
    return offset;
} /* dissect_zbee_nwk_gp_cmd_move_up_down */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_zbee_nwk_gp_cmd_step_color
 *  DESCRIPTION
 *      Dissector for ZigBee Green Power Step Color.
 *  PARAMETERS
 *      tvbuff_t *tvb                       - pointer to buffer containing raw packet.
 *      packet_into *pinfo                  - pointer to packet information fields.
 *      proto_tree *tree                    - pointer to data tree Wireshark uses to display packet.
 *      zbee_nwk_green_power_packet *packet - packet data.
 *      guint offset                        - current payload offset.
 *  RETURNS
 *      guint                               - payload processed offset.
 *---------------------------------------------------------------
 */
static guint
dissect_zbee_nwk_gp_cmd_step_color(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
    zbee_nwk_green_power_packet *packet _U_, guint offset)
{
    proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_step_color_stepx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
    offset += 2;
    proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_step_color_stepy, tvb, offset, 2, ENC_LITTLE_ENDIAN);
    offset += 2;
    /* Optional time field. */
    if (tvb_reported_length(tvb) - offset >= 2) {
        proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_step_color_transition_time, tvb, offset, 2, ENC_LITTLE_ENDIAN);
        offset += 2;
    }
    return offset;
} /* dissect_zbee_nwk_gp_cmd_step_color */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_zbee_nwk_gp_cmd_step_up_down
 *  DESCRIPTION
 *      Dissector for ZigBee Green Power Step Up/Down.
 *  PARAMETERS
 *      tvbuff_t *tvb                       - pointer to buffer containing raw packet.
 *      packet_into *pinfo                  - pointer to packet information fields.
 *      proto_tree *tree                    - pointer to data tree Wireshark uses to display packet.
 *      zbee_nwk_green_power_packet *packet - packet data.
 *      guint offset                        - current payload offset.
 *  RETURNS
 *      guint                               - payload processed offset.
 *---------------------------------------------------------------
 */
static guint
dissect_zbee_nwk_gp_cmd_step_up_down(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
    zbee_nwk_green_power_packet *packet _U_, guint offset)
{
    proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_step_up_down_step_size, tvb, offset, 1, ENC_LITTLE_ENDIAN);
    offset += 1;
    proto_tree_add_item(tree, hf_zbee_nwk_gp_cmd_step_up_down_transition_time, tvb, offset, 2, ENC_LITTLE_ENDIAN);
    offset += 2;
    return offset;
} /* dissect_zbee_nwk_gp_cmd_step_up_down */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_zbee_nwk_gp_cmd
 *  DESCRIPTION
 *      Dissector for ZigBee Green Power commands.
 *  PARAMETERS
 *      tvbuff_t *tvb       - pointer to buffer containing raw packet.
 *      packet_into *pinfo  - pointer to packet information fields.
 *      proto_tree *tree    - pointer to data tree Wireshark uses to display packet.
 *      void *data          - raw packet private data.
 *  RETURNS
 *      guint               - payload processed offset
 *---------------------------------------------------------------
 */
static guint
dissect_zbee_nwk_gp_cmd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
    guint offset = 0;
    guint8 cmd_id = tvb_get_guint8(tvb, offset);
    proto_item *cmd_root;
    proto_tree *cmd_tree;
    zbee_nwk_green_power_packet *packet = (zbee_nwk_green_power_packet *)data;

    /* Create a subtree for the command. */
    cmd_tree = proto_tree_add_subtree_format(tree, tvb, offset, -1, ett_zbee_nwk_cmd, &cmd_root,
                                        "Command Frame: %s", val_to_str_ext_const(cmd_id,
                                                            &zbee_nwk_gp_cmd_names_ext,
                                                            "Unknown Command Frame"));
    /* Add the command ID. */
    proto_tree_add_uint(cmd_tree, hf_zbee_nwk_gp_command_id, tvb, offset, 1, cmd_id);

    offset += 1;
    /* Add the command name to the info column. */
    col_set_str(pinfo->cinfo, COL_INFO, val_to_str_ext_const(cmd_id, &zbee_nwk_gp_cmd_names_ext, "Unknown command"));
    /* Handle the command for one of the following devices:
     * - Door Lock Controller (IDs: 0x50 - 0x51);
     * - GP Flow Sensor (IDs: 0xE0, 0xA0 - 0xA3);
     * - GP Temperature Sensor (IDs: 0xE0, 0xA0 - 0xA3); */
    switch(cmd_id) {
        /* Payloadless GPDF commands sent by GPD. */
        case ZB_GP_CMD_ID_IDENTIFY:
        case ZB_GP_CMD_ID_SCENE0:
        case ZB_GP_CMD_ID_SCENE1:
        case ZB_GP_CMD_ID_SCENE2:
        case ZB_GP_CMD_ID_SCENE3:
        case ZB_GP_CMD_ID_SCENE4:
        case ZB_GP_CMD_ID_SCENE5:
        case ZB_GP_CMD_ID_SCENE6:
        case ZB_GP_CMD_ID_SCENE7:
        case ZB_GP_CMD_ID_SCENE8:
        case ZB_GP_CMD_ID_SCENE9:
        case ZB_GP_CMD_ID_SCENE10:
        case ZB_GP_CMD_ID_SCENE11:
        case ZB_GP_CMD_ID_SCENE12:
        case ZB_GP_CMD_ID_SCENE13:
        case ZB_GP_CMD_ID_SCENE14:
        case ZB_GP_CMD_ID_SCENE15:
        case ZB_GP_CMD_ID_OFF:
        case ZB_GP_CMD_ID_ON:
        case ZB_GP_CMD_ID_TOGGLE:
        case ZB_GP_CMD_ID_RELEASE:
        case ZB_GP_CMD_ID_LEVEL_CONTROL_STOP:
        case ZB_GP_CMD_ID_MOVE_HUE_STOP:
        case ZB_GP_CMD_ID_MOVE_SATURATION_STOP:
        case ZB_GP_CMD_ID_LOCK_DOOR:
        case ZB_GP_CMD_ID_UNLOCK_DOOR:
        case ZB_GP_CMD_ID_PRESS11:
        case ZB_GP_CMD_ID_RELEASE11:
        case ZB_GP_CMD_ID_PRESS12:
        case ZB_GP_CMD_ID_RELEASE12:
        case ZB_GP_CMD_ID_PRESS22:
        case ZB_GP_CMD_ID_RELEASE22:
        case ZB_GP_CMD_ID_SHORT_PRESS11:
        case ZB_GP_CMD_ID_SHORT_PRESS12:
        case ZB_GP_CMD_ID_SHORT_PRESS22:
        case ZB_GP_CMD_ID_DECOMMISSIONING:
        case ZB_GP_CMD_ID_SUCCESS:
            break;
        /* GPDF commands with payload sent by GPD. */
        case ZB_GP_CMD_ID_MOVE_UP:
        case ZB_GP_CMD_ID_MOVE_DOWN:
        case ZB_GP_CMD_ID_MOVE_UP_WITH_ON_OFF:
        case ZB_GP_CMD_ID_MOVE_DOWN_WITH_ON_OFF:
        case ZB_GP_CMD_ID_MOVE_HUE_UP:
        case ZB_GP_CMD_ID_MOVE_HUE_DOWN:
        case ZB_GP_CMD_ID_MOVE_SATUREATION_UP:
        case ZB_GP_CMD_ID_MOVE_SATUREATION_DOWN:
            offset = dissect_zbee_nwk_gp_cmd_move_up_down(tvb, pinfo, cmd_tree, packet, offset);
            break;
        case ZB_GP_CMD_ID_STEP_UP:
        case ZB_GP_CMD_ID_STEP_DOWN:
        case ZB_GP_CMD_ID_STEP_UP_WITH_ON_OFF:
        case ZB_GP_CMD_ID_STEP_DOWN_WITH_ON_OFF:
        case ZB_GP_CMD_ID_STEP_HUE_UP:
        case ZB_GP_CMD_ID_STEP_HUW_DOWN:
        case ZB_GP_CMD_ID_STEP_SATURATION_UP:
        case ZB_GP_CMD_ID_STEP_SATURATION_DOWN:
            offset = dissect_zbee_nwk_gp_cmd_step_up_down(tvb, pinfo, cmd_tree, packet, offset);
            break;
        case ZB_GP_CMD_ID_MOVE_COLOR:
            offset = dissect_zbee_nwk_gp_cmd_move_color(tvb, pinfo, cmd_tree, packet, offset);
            break;
        case ZB_GP_CMD_ID_STEP_COLOR:
            offset = dissect_zbee_nwk_gp_cmd_step_color(tvb, pinfo, cmd_tree, packet, offset);
            break;
        case ZB_GP_CMD_ID_ATTRIBUTE_REPORTING:
            offset = dissect_zbee_nwk_gp_cmd_attr_reporting(tvb, pinfo, cmd_tree, packet, offset);
            break;
        case ZB_GP_CMD_ID_MANUFACTURE_SPECIFIC_ATTR_REPORTING:
        case ZB_GP_CMD_ID_MULTI_CLUSTER_REPORTING:
        case ZB_GP_CMD_ID_MANUFACTURER_SPECIFIC_MCLUSTER_REPORTING:
        case ZB_GP_CMD_ID_REQUEST_ATTRIBUTES:
        case ZB_GP_CMD_ID_READ_ATTRIBUTES_RESPONSE:
        case ZB_GP_CMD_ID_ANY_SENSOR_COMMAND_A0_A3:
            /* TODO: implement it. */
            break;
        case ZB_GP_CMD_ID_COMMISSIONING:
            offset = dissect_zbee_nwk_gp_cmd_commissioning(tvb, pinfo, cmd_tree, packet, offset);
            break;
        case ZB_GP_CMD_ID_CHANNEL_REQUEST:
            offset = dissect_zbee_nwk_gp_cmd_channel_request(tvb, pinfo, cmd_tree, packet, offset);
            break;
        /* GPDF commands sent to GPD. */
        case ZB_GP_CMD_ID_COMMISSIONING_REPLY:
            offset = dissect_zbee_nwk_gp_cmd_commissioning_reply(tvb, pinfo, cmd_tree, packet, offset);
            break;
        case ZB_GP_CMD_ID_WRITE_ATTRIBUTES:
        case ZB_GP_CMD_ID_READ_ATTRIBUTES:
            /* TODO: implement it. */
            break;
        case ZB_GP_CMD_ID_CHANNEL_CONFIGURATION:
            offset = dissect_zbee_nwk_gp_cmd_channel_configuration(tvb, pinfo, cmd_tree, packet, offset);
            break;
    }
    if (offset < tvb_reported_length(tvb)) {
        /* There are leftover bytes! */
        proto_tree *root;
        tvbuff_t *leftover_tvb = tvb_new_subset_remaining(tvb, offset);

        /* Correct the length of the command tree. */
        root = proto_tree_get_root(tree);
        proto_item_set_len(cmd_root, offset);

        /* Dump the tail. */
        call_dissector(data_handle, leftover_tvb, pinfo, root);
    }
    return offset;
} /* dissect_zbee_nwk_gp_cmd */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      zbee_sec_make_nonce
 *  DESCRIPTION
 *      Fills in ZigBee GP security nonce from the provided packet structure.
 *  PARAMETERS
 *      zbee_nwk_green_power_packet *packet - ZigBee NWK packet.
 *      gchar                       *nonce  - nonce buffer.
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
zbee_gp_make_nonce(zbee_nwk_green_power_packet *packet, gchar *nonce)
{
    memset(nonce, 0, ZBEE_SEC_CONST_NONCE_LEN);
    if (packet->direction == ZBEE_NWK_GP_FC_EXT_DIRECTION_FROM_ZGPD) {
        nonce[0] = (guint8)((packet->source_id) & 0xff);
        nonce[1] = (guint8)((packet->source_id) >> 8 & 0xff);
        nonce[2] = (guint8)((packet->source_id) >> 16 & 0xff);
        nonce[3] = (guint8)((packet->source_id) >> 24 & 0xff);
    }
    nonce[4]  = (guint8)((packet->source_id) & 0xff);
    nonce[5]  = (guint8)((packet->source_id) >> 8 & 0xff);
    nonce[6]  = (guint8)((packet->source_id) >> 16 & 0xff);
    nonce[7]  = (guint8)((packet->source_id) >> 24 & 0xff);
    nonce[8]  = (guint8)((packet->security_frame_counter) & 0xff);
    nonce[9]  = (guint8)((packet->security_frame_counter) >> 8 & 0xff);
    nonce[10] = (guint8)((packet->security_frame_counter) >> 16 & 0xff);
    nonce[11] = (guint8)((packet->security_frame_counter) >> 24 & 0xff);
    if ((packet->application_id == ZBEE_NWK_GP_APP_ID_ZGP) && (packet->direction !=
        ZBEE_NWK_GP_FC_EXT_DIRECTION_FROM_ZGPD)) {
        nonce[12] = 0xa3;
    } else {
        nonce[12] = 0x05;
    }
    /* TODO: implement if application_id == ZB_ZGP_APP_ID_0000. */
    /* TODO: implement if application_id != ZB_ZGP_APP_ID_0000. */
}

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      zbee_sec_decrypt_payload
 *  DESCRIPTION
 *      Creates a nonce and decrypts secured ZigBee GP payload.
 *  PARAMETERS
 *      zbee_nwk_green_power_packet *packet     - ZigBee NWK packet.
 *      const gchar                 *enc_buffer - encoded payload buffer.
 *      const gchar                 offset      - payload offset.
 *      guint8                      *dec_buffer - decoded payload buffer.
 *      guint                       payload_len - payload length.
 *      guint                       mic_len     - MIC length.
 *      guint8                      *key        - key.
 *  RETURNS
 *      gboolean
 *---------------------------------------------------------------
 */
static gboolean
zbee_gp_decrypt_payload(zbee_nwk_green_power_packet *packet, const gchar *enc_buffer, const gchar offset, guint8
    *dec_buffer, guint payload_len, guint mic_len, guint8 *key)
{
    guint8 *key_buffer = key;
    guint8 nonce[ZBEE_SEC_CONST_NONCE_LEN];

    zbee_gp_make_nonce(packet, nonce);
    if (zbee_sec_ccm_decrypt(key_buffer, nonce, enc_buffer, enc_buffer + offset, dec_buffer, offset, payload_len,
        mic_len)) {
        return TRUE;
    }

    return FALSE;
}

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_zbee_nwk_gp
 *  DESCRIPTION
 *      ZigBee NWK packet dissection routine for Green Power profile.
 *  PARAMETERS
 *      tvbuff_t *tvb       - pointer to buffer containing raw packet.
 *      packet_into *pinfo  - pointer to packet information fields.
 *      proto_tree *tree    - pointer to data tree Wireshark uses to display packet.
 *      void *data          - raw packet private data.
 *  RETURNS
 *      int
 *---------------------------------------------------------------
 */
static int
dissect_zbee_nwk_gp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
    gboolean gp_decrypted;
    GSList *GSList_i;
    guint offset = 0;
    guint8 *dec_buffer;
    guint8 *enc_buffer;
    guint8 fcf;
    proto_tree *nwk_tree;
    proto_item *proto_root;
    proto_item *ti = NULL;
    tvbuff_t *payload_tvb;
    zbee_nwk_green_power_packet packet;
    static const int * fields[] = {
        &hf_zbee_nwk_gp_frame_type,
        &hf_zbee_nwk_gp_proto_version,
        &hf_zbee_nwk_gp_auto_commissioning,
        &hf_zbee_nwk_gp_fc_ext,
        NULL
    };
    static const int * ext_fields[] = {
        &hf_zbee_nwk_gp_fc_ext_app_id,
        &hf_zbee_nwk_gp_fc_ext_sec_level,
        &hf_zbee_nwk_gp_fc_ext_sec_key,
        &hf_zbee_nwk_gp_fc_ext_rx_after_tx,
        &hf_zbee_nwk_gp_fc_ext_direction,
        NULL
    };

    memset(&packet, 0, sizeof(packet));
    /* Add ourself to the protocol column, clear the info column and create the protocol tree. */
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "ZigBee Green Power");
    col_clear(pinfo->cinfo, COL_INFO);

    proto_root = proto_tree_add_protocol_format(tree, proto_zbee_nwk_gp, tvb, offset, tvb_captured_length(tvb),
            "ZGP stub NWK header");
    nwk_tree = proto_item_add_subtree(proto_root, ett_zbee_nwk);

    enc_buffer = (guint8 *)tvb_memdup(wmem_packet_scope(), tvb, 0, tvb_captured_length(tvb));
    /* Get and parse the FCF. */
    fcf = tvb_get_guint8(tvb, offset);
    packet.frame_type = zbee_get_bit_field(fcf, ZBEE_NWK_GP_FCF_FRAME_TYPE);
    packet.nwk_frame_control_extension = zbee_get_bit_field(fcf, ZBEE_NWK_GP_FCF_CONTROL_EXTENSION);

    /* Display the FCF. */
    ti = proto_tree_add_bitmask(nwk_tree, tvb, offset, hf_zbee_nwk_gp_fcf, ett_zbee_nwk_fcf, fields, ENC_NA);
    proto_item_append_text(ti, " %s", val_to_str(packet.frame_type, zbee_nwk_gp_frame_types, "Unknown Frame Type"));
    offset += 1;

    /* Add the frame type to the info column and protocol root. */
    proto_item_append_text(proto_root, " %s", val_to_str(packet.frame_type, zbee_nwk_gp_frame_types, "Unknown type"));
    col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(packet.frame_type, zbee_nwk_gp_frame_types, "Reserved frame type"));

    if (packet.nwk_frame_control_extension) {
        /* Display ext FCF. */
        fcf = tvb_get_guint8(tvb, offset);
        packet.application_id = zbee_get_bit_field(fcf, ZBEE_NWK_GP_FCF_EXT_APP_ID);
        packet.security_level = zbee_get_bit_field(fcf, ZBEE_NWK_GP_FCF_EXT_SECURITY_LEVEL);
        packet.direction = zbee_get_bit_field(fcf, ZBEE_NWK_GP_FCF_EXT_DIRECTION);

        /* Create a subtree for the extended FCF. */
        proto_tree_add_bitmask(nwk_tree, tvb, offset, hf_zbee_nwk_gp_fc_ext_field, ett_zbee_nwk_fcf_ext, ext_fields, ENC_NA);
        offset += 1;
    }
    if ((packet.frame_type == ZBEE_NWK_GP_FCF_DATA && !packet.nwk_frame_control_extension) || (packet.frame_type ==
        ZBEE_NWK_GP_FCF_DATA && packet.nwk_frame_control_extension && packet.application_id ==
        ZBEE_NWK_GP_APP_ID_DEFAULT) || (packet.frame_type == ZBEE_NWK_GP_FCF_MAINTENANCE &&
        packet.nwk_frame_control_extension && packet.application_id == ZBEE_NWK_GP_APP_ID_DEFAULT && tvb_get_guint8(tvb,
        offset) != ZB_GP_CMD_ID_CHANNEL_CONFIGURATION)) {
        /* Display GPD Src ID. */
        packet.source_id = tvb_get_letohl(tvb, offset);
        proto_tree_add_item(nwk_tree, hf_zbee_nwk_gp_zgpd_src_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
        proto_item_append_text(proto_root, ", GPD Src ID: 0x%04x", packet.source_id);

        col_append_fstr(pinfo->cinfo, COL_INFO, ", GPD Src ID: 0x%04x", packet.source_id);
        offset += 4;
    }
    /* Display Security Frame Counter. */
    packet.mic_size = 0;
    if (packet.nwk_frame_control_extension) {
        if (packet.application_id == ZBEE_NWK_GP_APP_ID_DEFAULT || packet.application_id == ZBEE_NWK_GP_APP_ID_ZGP) {
            if (packet.security_level == ZBEE_NWK_GP_SECURITY_LEVEL_1LSB) {
                packet.mic_size = 2;
            } else if (packet.security_level == ZBEE_NWK_GP_SECURITY_LEVEL_FULL || packet.security_level ==
                ZBEE_NWK_GP_SECURITY_LEVEL_FULLENCR) {
                /* Get Security Frame Counter and display it. */
                packet.mic_size = 4;
                packet.security_frame_counter = tvb_get_letohl(tvb, offset);
                    proto_tree_add_item(nwk_tree, hf_zbee_nwk_gp_security_frame_counter, tvb, offset, 4,
                    ENC_LITTLE_ENDIAN);
                offset += 4;
            }
        }
    }
    /* Parse application payload. */
    packet.payload_len = tvb_reported_length(tvb) - offset - packet.mic_size;
    /* Ensure that the payload exists. */
    if (packet.payload_len <= 0) {
        THROW(BoundsError);
    }
    /* OK, payload exists. Parse MIC field if needed. */
    if (packet.mic_size == 2) {
        packet.mic = tvb_get_letohs(tvb, offset + packet.payload_len);
    } else if (packet.mic_size == 4) {
        packet.mic = tvb_get_letohl(tvb, offset + packet.payload_len);
    }
    payload_tvb = tvb_new_subset_length(tvb, offset, packet.payload_len);
    if (packet.security_level != ZBEE_NWK_GP_SECURITY_LEVEL_FULLENCR) {
        dissect_zbee_nwk_gp_cmd(payload_tvb, pinfo, nwk_tree, data);
    }
    offset += packet.payload_len;
    /* Display MIC field. */
    if (packet.mic_size) {
        proto_tree_add_uint(nwk_tree, packet.mic_size == 4 ? hf_zbee_nwk_gp_security_mic_4b :
                hf_zbee_nwk_gp_security_mic_2b, tvb, offset, packet.mic_size, packet.mic);
        offset += packet.mic_size;
    }
    /* Save packet private data. */
    data = (void *)&packet;
    if ((offset < tvb_captured_length(tvb)) && (packet.security_level != ZBEE_NWK_GP_SECURITY_LEVEL_FULLENCR)) {
        THROW(BoundsError);
    }
    if (packet.security_level == ZBEE_NWK_GP_SECURITY_LEVEL_FULLENCR) {
        dec_buffer = (guint8 *)g_malloc(packet.payload_len);
        gp_decrypted = FALSE;
        if (packet.security_level == ZBEE_NWK_GP_SECURITY_LEVEL_FULLENCR) {
            GSList_i = zbee_gp_keyring;
            while (GSList_i && !gp_decrypted) {
                gp_decrypted = zbee_gp_decrypt_payload(&packet, enc_buffer, offset - packet.payload_len -
                    packet.mic_size, dec_buffer, packet.payload_len, packet.mic_size,
                    ((key_record_t *)(GSList_i->data))->key);
                if (!gp_decrypted) {
                    GSList_i = g_slist_next(GSList_i);
                }
            }
        }
        if (gp_decrypted) {
            payload_tvb = tvb_new_child_real_data(tvb, dec_buffer, packet.payload_len, packet.payload_len);
            add_new_data_source(pinfo, payload_tvb, "Decrypted GP Payload");
            dissect_zbee_nwk_gp_cmd(payload_tvb, pinfo, nwk_tree, data);
            g_free(dec_buffer);
        } else {
            g_free(dec_buffer);
            payload_tvb = tvb_new_subset(tvb, offset - packet.payload_len - packet.mic_size, packet.payload_len, -1);
            call_dissector(data_handle, payload_tvb, pinfo, tree);
        }
    }
    return tvb_captured_length(tvb);
} /* dissect_zbee_nwk_gp */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      dissect_zbee_nwk_heur_gp
 *  DESCRIPTION
 *      Heuristic interpreter for the ZigBee Green Power dissectors.
 *  PARAMETERS
 *      tvbuff_t *tvb       - pointer to buffer containing raw packet.
 *      packet_into *pinfo  - pointer to packet information fields.
 *      proto_tree *tree    - pointer to data tree Wireshark uses to display packet.
 *      void *data          - raw packet private data.
 *  RETURNS
 *      Boolean value, whether it handles the packet or not.
 *---------------------------------------------------------------
 */
static gboolean
dissect_zbee_nwk_heur_gp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
    ieee802154_packet   *packet = (ieee802154_packet *)data;
    guint8              fcf;

    /* We must have the IEEE 802.15.4 headers. */
    if (packet == NULL) return FALSE;
    /* ZigBee green power never uses 16-bit source addresses. */
    if (packet->src_addr_mode == IEEE802154_FCF_ADDR_SHORT) return FALSE;

    /* If the frame type and version are not sane, then it's probably not ZGP. */
    fcf = tvb_get_guint8(tvb, 0);
    if (zbee_get_bit_field(fcf, ZBEE_NWK_GP_FCF_VERSION) != ZBEE_VERSION_GREEN_POWER) return FALSE;
    if (!try_val_to_str(zbee_get_bit_field(fcf, ZBEE_NWK_FCF_FRAME_TYPE), zbee_nwk_gp_frame_types)) return FALSE;

    /* ZigBee greenpower frames are either sent to broadcast or the extended address. */
    if (packet->dst_pan == IEEE802154_BCAST_PAN && packet->dst_addr_mode == IEEE802154_FCF_ADDR_SHORT &&
        packet->dst16 == IEEE802154_BCAST_ADDR) {
        dissect_zbee_nwk_gp(tvb, pinfo, tree, data);
        return TRUE;
    }
    /* 64-bit destination addressing mode support. */
    if (packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT) {
        dissect_zbee_nwk_gp(tvb, pinfo, tree, data);
        return TRUE;
    }

    return FALSE;
} /* dissect_zbee_nwk_heur_gp */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      gp_init_zbee_security
 *  DESCRIPTION
 *      Init routine for the ZigBee GP profile security.
 *  PARAMETERS
 *      none
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
static void
gp_init_zbee_security(void)
{
    guint i;
    key_record_t key_record;

    for (i = 0; gp_uat_key_records && (i < num_uat_key_records); i++) {
        key_record.frame_num = 0;
        key_record.label = g_strdup(gp_uat_key_records[i].label);
        memcpy(&key_record.key, &gp_uat_key_records[i].key, ZBEE_SEC_CONST_KEYSIZE);
        zbee_gp_keyring = g_slist_prepend(zbee_gp_keyring, g_memdup(&key_record, sizeof(key_record_t)));
    }
}

static void zbee_free_key_record(gpointer ptr, gpointer user_data _U_)
{
    key_record_t *k;

    k = (key_record_t *)ptr;
    if (!k)
        return;

    g_free(k->label);
    g_free(k);
}

static void
gp_cleanup_zbee_security(void)
{
    if (!zbee_gp_keyring)
        return;

    g_slist_foreach(zbee_gp_keyring, zbee_free_key_record, NULL);

    g_slist_free(zbee_gp_keyring);
    zbee_gp_keyring = NULL;
}

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      proto_register_zbee_nwk_gp
 *  DESCRIPTION
 *      ZigBee NWK GP protocol registration routine.
 *  PARAMETERS
 *      none
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
void
proto_register_zbee_nwk_gp(void)
{
    module_t *gp_zbee_prefs;

    static hf_register_info hf[] = {
        { &hf_zbee_nwk_gp_auto_commissioning,
            { "Auto Commissioning", "zbee_nwk_gp.auto_commissioning", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_FCF_AUTO_COMMISSIONING, NULL, HFILL }},

        { &hf_zbee_nwk_gp_fc_ext,
            { "NWK Frame Extension", "zbee_nwk_gp.fc_extension", FT_BOOLEAN, 8, NULL, ZBEE_NWK_GP_FCF_CONTROL_EXTENSION,
                NULL, HFILL }},

        { &hf_zbee_nwk_gp_fcf,
            { "Frame Control Field", "zbee_nwk_gp.fcf", FT_UINT8, BASE_HEX, NULL,
                0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_frame_type,
            { "Frame Type", "zbee_nwk_gp.frame_type", FT_UINT8, BASE_HEX, VALS(zbee_nwk_gp_frame_types),
                ZBEE_NWK_GP_FCF_FRAME_TYPE, NULL, HFILL }},

        { &hf_zbee_nwk_gp_proto_version,
            { "Protocol Version", "zbee_nwk_gp.proto_version", FT_UINT8, BASE_DEC, NULL, ZBEE_NWK_GP_FCF_VERSION, NULL,
                HFILL }},

        { &hf_zbee_nwk_gp_fc_ext_field,
            { "Extended NWK Frame Control Field", "zbee_nwk_gp.fc_ext", FT_UINT8, BASE_HEX, NULL,
                0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_fc_ext_app_id,
            { "Application ID", "zbee_nwk_gp.fc_ext_app_id", FT_UINT8, BASE_HEX, VALS(zbee_nwk_gp_app_id_names),
                ZBEE_NWK_GP_FCF_EXT_APP_ID, NULL, HFILL }},

        { &hf_zbee_nwk_gp_fc_ext_direction,
            { "Direction", "zbee_nwk_gp.fc_ext_direction", FT_UINT8, BASE_HEX, VALS(zbee_nwk_gp_directions),
                ZBEE_NWK_GP_FCF_EXT_DIRECTION, NULL, HFILL }},

        { &hf_zbee_nwk_gp_fc_ext_rx_after_tx,
            { "Rx After Tx", "zbee_nwk_gp.fc_ext_rxaftertx", FT_BOOLEAN, 8, NULL, ZBEE_NWK_GP_FCF_EXT_RX_AFTER_TX, NULL,
                HFILL }},

        { &hf_zbee_nwk_gp_fc_ext_sec_key,
            { "Security Key", "zbee_nwk_gp.fc_ext_security_key", FT_BOOLEAN, 8, NULL, ZBEE_NWK_GP_FCF_EXT_SECURITY_KEY,
                NULL, HFILL }},

        { &hf_zbee_nwk_gp_fc_ext_sec_level,
            { "Security Level", "zbee_nwk_gp.fc_ext_security_level", FT_UINT8, BASE_HEX,
                VALS(zbee_nwk_gp_src_sec_levels_names), ZBEE_NWK_GP_FCF_EXT_SECURITY_LEVEL, NULL, HFILL }},

        { &hf_zbee_nwk_gp_zgpd_src_id,
            { "Src ID", "zbee_nwk_gp.source_id", FT_UINT32, BASE_HEX, VALS(zbee_nwk_gp_src_id_names), 0x0, NULL,
                HFILL }},

        { &hf_zbee_nwk_gp_security_frame_counter,
            { "Security Frame Counter", "zbee_nwk_gp.security_frame_counter", FT_UINT32, BASE_DEC, NULL, 0x0, NULL,
                HFILL }},

        { &hf_zbee_nwk_gp_security_mic_2b,
            { "Security MIC", "zbee_nwk_gp.security_mic2", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_security_mic_4b,
            { "Security MIC", "zbee_nwk_gp.security_mic4", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_command_id,
            { "ZGPD Command ID", "zbee_nwk_gp.command_id", FT_UINT8, BASE_HEX | BASE_EXT_STRING, &zbee_nwk_gp_cmd_names_ext, 0x0, NULL,
                HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_device_id,
            { "ZGPD Device ID", "zbee_nwk_gp.cmd.comm.dev_id", FT_UINT8, BASE_HEX | BASE_EXT_STRING, &zbee_nwk_gp_device_ids_names_ext,
                0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_ext_opt_gpd_key_encr,
            { "GPD Key Encryption", "zbee_nwk_gp.cmd.comm.ext_opt.gpd_key_encr", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_GPD_KEY_ENCR, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_ext_opt_gpd_key_present,
            { "GPD Key Present", "zbee_nwk_gp.cmd.comm.ext_opt.gpd_key_present", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_GPD_KEY_PRESENT, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_ext_opt_key_type,
            { "Key Type", "zbee_nwk_gp.cmd.comm.ext_opt.key_type", FT_UINT8, BASE_HEX,
                VALS(zbee_nwk_gp_src_sec_keys_type_names), ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_KEY_TYPE, NULL,
                HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_outgoing_counter,
            { "GPD Outgoing Counter", "zbee_nwk_gp.cmd.comm.out_counter", FT_UINT32, BASE_HEX, NULL, 0x0, NULL,
                HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_ext_opt_sec_level_cap,
            { "Security Level Capabilities", "zbee_nwk_gp.cmd.comm.ext_opt.seclevel_cap", FT_UINT8, BASE_HEX, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_SEC_LEVEL_CAP, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_security_key,
            { "Security Key", "zbee_nwk_gp.cmd.comm.security_key", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_gpd_sec_key_mic,
            { "GPD Key MIC", "zbee_nwk_gp.cmd.comm.gpd_key_mic", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_opt_ext_opt,
            { "Extended Option Field", "zbee_nwk_gp.cmd.comm.opt.ext_opt_field", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_EXT_OPTIONS, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_opt,
            { "Options Field", "zbee_nwk_gp.cmd.comm.opt", FT_UINT8, BASE_HEX, NULL,
                0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_opt_fixed_location,
            { "Fixed Location", "zbee_nwk_gp.cmd.comm.opt.fixed_location", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_FIXED_LOCATION, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_opt_mac_sec_num_cap,
            { "MAC Sequence number capability", "zbee_nwk_gp.cmd.comm.opt.mac_seq_num_cap", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_MAC_SEQ, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_opt_ms_ext_present,
            { "MS Extensions Present", "zbee_nwk_gp.cmd.comm.opt.ms_ext_present", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_MANUFACTURER_INFO, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_opt_panid_req,
            { "PANId request", "zbee_nwk_gp.cmd.comm.opt.panid_req", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_PAN_ID_REQ, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_opt_rx_on_cap,
            { "RxOnCapability", "zbee_nwk_gp.cmd.comm.opt.rxon_cap", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_RX_ON_CAP, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_opt_sec_key_req,
            { "GP Security Key Request", "zbee_nwk_gp.cmd.comm.opt.seq_key_req", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_OPT_GP_SEC_KEY_REQ, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_ext_opt,
            { "Extended Options Field", "zbee_nwk_gp.cmd.comm.ext_opt", FT_UINT8, BASE_HEX, NULL,
                0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_ext_opt_outgoing_counter,
            { "GPD Outgoing present", "zbee_nwk_gp.cmd.comm.ext_opt.outgoing_counter", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_EXT_OPT_OUT_COUNTER, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_manufacturer_greenpeak_dev_id,
            { "Manufacturer Model ID", "zbee_nwk_gp.cmd.comm.manufacturer_model_id", FT_UINT16, BASE_HEX,
                VALS(zbee_nwk_gp_manufacturer_greenpeak_dev_names), 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_manufacturer_dev_id,
            { "Manufacturer Model ID", "zbee_nwk_gp.cmd.comm.manufacturer_model_id", FT_UINT16, BASE_HEX,
                NULL, 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_manufacturer_id,
            { "Manufacturer ID", "zbee_nwk_gp.cmd.comm.manufacturer_id", FT_UINT16, BASE_HEX,
                VALS(zbee_mfr_code_names), 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_ms_ext_crp,
            { "Cluster reports present", "zbee_nwk_gp.cmd.comm.ms_ext.crp", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_MS_EXT_CRP , NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_ms_ext_gclp,
            { "GP commands list present", "zbee_nwk_gp.cmd.comm.ms_ext.gclp", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_MS_EXT_GCLP , NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_ms_ext,
            { "MS Extensions Field", "zbee_nwk_gp.cmd.comm.ms_ext", FT_UINT8, BASE_HEX, NULL,
                0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_ms_ext_mip,
            { "Manufacturer ID present", "zbee_nwk_gp.cmd.comm.ms_ext.mip", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_MS_EXT_MIP , NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_ms_ext_mmip,
            { "Manufacturer Model ID present", "zbee_nwk_gp.cmd.comm.ms_ext.mmip", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_MS_EXT_MMIP , NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_rep_opt_key_encr,
            { "GPD Key Encryption", "zbee_nwk_gp.cmd.comm_reply.opt.sec_key_encr", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_KEY_ENCR, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_rep_opt,
            { "Options Field", "zbee_nwk_gp.cmd.comm_reply.opt", FT_UINT8, BASE_HEX, NULL,
                0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_rep_opt_panid_present,
            { "PANID Present", "zbee_nwk_gp.cmd.comm_reply.opt.pan_id_present", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_PAN_ID_PRESENT, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_rep_opt_sec_key_present,
            { "GPD Security Key Present", "zbee_nwk_gp.cmd.comm_reply.opt.sec_key_present", FT_BOOLEAN, 8, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_SEC_KEY_PRESENT, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_rep_opt_sec_level,
            { "Security Level", "zbee_nwk_gp.cmd.comm_reply.opt.sec_level", FT_UINT8, BASE_HEX, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_SEC_LEVEL, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_rep_opt_sec_type,
            { "Key Type", "zbee_nwk_gp.cmd.comm_reply.opt.key_type", FT_UINT8, BASE_HEX, NULL,
                ZBEE_NWK_GP_CMD_COMMISSIONING_REP_OPT_KEY_TYPE, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_comm_rep_pan_id,
            { "Manufacturer ID", "zbee_nwk_gp.cmd.comm_reply.pan_id", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_attr_report_cluster_id,
            { "ZigBee Cluster ID", "zbee_nwk_gp.cmd.comm.attr_report", FT_UINT16, BASE_HEX, VALS(zbee_aps_cid_names),
                0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_channel_request_toggling_behaviour,
            { "Channel Toggling Behaviour", "zbee_nwk_gp.cmd.ch_req", FT_UINT8, BASE_HEX, NULL,
                0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_channel_request_toggling_behaviour_1st,
            { "Rx channel in the next attempt", "zbee_nwk_gp.cmd.ch_req.1st", FT_UINT8, BASE_HEX, NULL,
                ZBEE_NWK_GP_CMD_CHANNEL_REQUEST_1ST, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_channel_request_toggling_behaviour_2nd,
            { "Rx channel in the second next attempt", "zbee_nwk_gp.ch_req.2nd", FT_UINT8, BASE_HEX, NULL,
                ZBEE_NWK_GP_CMD_CHANNEL_REQUEST_2ND, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_operational_channel,
            { "Operational Channel", "zbee_nwk_gp.cmd.configuration_ch", FT_UINT8, BASE_HEX, NULL,
                0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_channel_configuration,
            { "Operation channel", "zbee_nwk_gp.cmd.configuration_ch.operation_ch", FT_UINT8, BASE_HEX, NULL,
                ZBEE_NWK_GP_CMD_CHANNEL_CONFIGURATION_OPERATION_CH, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_move_color_ratex,
            { "RateX", "zbee_nwk_gp.cmd.move_color.ratex", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_move_color_ratey,
            { "RateY", "zbee_nwk_gp.cmd.move_color.ratey", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_move_up_down_rate,
            { "Rate", "zbee_nwk_gp.cmd.move_up_down.rate", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_step_color_stepx,
            { "StepX", "zbee_nwk_gp.cmd.step_color.stepx", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_step_color_stepy,
            { "StepY", "zbee_nwk_gp.cmd.step_color.stepy", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_step_color_transition_time,
            { "Transition Time", "zbee_nwk_gp.cmd.step_color.transition_time", FT_UINT16, BASE_DEC, NULL, 0x0, NULL,
                HFILL }},

        { &hf_zbee_nwk_gp_cmd_step_up_down_step_size,
            { "Step Size", "zbee_nwk_gp.cmd.step_up_down.step_size", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},

        { &hf_zbee_nwk_gp_cmd_step_up_down_transition_time,
            { "Transition Time", "zbee_nwk_gp.cmd.step_up_down.transition_time", FT_UINT16, BASE_DEC, NULL, 0x0, NULL,
                HFILL }}
    };

    static gint *ett[] = {
        &ett_zbee_nwk,
        &ett_zbee_nwk_cmd,
        &ett_zbee_nwk_cmd_cinfo,
        &ett_zbee_nwk_cmd_ms_ext,
        &ett_zbee_nwk_cmd_options,
        &ett_zbee_nwk_fcf,
        &ett_zbee_nwk_fcf_ext
    };

    static uat_field_t key_uat_fields[] = {
        UAT_FLD_CSTRING(gp_uat_key_records, string, "Key", "A 16-byte key."),
        UAT_FLD_VS(gp_uat_key_records, byte_order, "Byte Order", byte_order_vals, "Byte order of a key."),
        UAT_FLD_LSTRING(gp_uat_key_records, label, "Label", "User label for a key."),
        UAT_END_FIELDS
    };

    proto_zbee_nwk_gp = proto_register_protocol("ZigBee Green Power Profile", "ZigBee Green Power",
        ZBEE_PROTOABBREV_NWK_GP);

    gp_zbee_prefs = prefs_register_protocol(proto_zbee_nwk_gp, NULL);

    zbee_gp_sec_key_table_uat = uat_new("ZigBee GP Security Keys", sizeof(uat_key_record_t), "zigbee_gp_keys", TRUE,
        &gp_uat_key_records, &num_uat_key_records, UAT_AFFECTS_DISSECTION, NULL, uat_key_record_copy_cb,
        uat_key_record_update_cb, uat_key_record_free_cb, NULL, key_uat_fields);

    prefs_register_uat_preference(gp_zbee_prefs, "gp_key_table", "Pre-configured GP Security Keys",
        "Pre-configured GP Security Keys.", zbee_gp_sec_key_table_uat);

    register_init_routine(gp_init_zbee_security);
    register_cleanup_routine(gp_cleanup_zbee_security);

    /* Register the Wireshark protocol. */
    proto_register_field_array(proto_zbee_nwk_gp, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));

    /* Register the dissectors. */
    new_register_dissector(ZBEE_PROTOABBREV_NWK_GP, dissect_zbee_nwk_gp, proto_zbee_nwk_gp);
} /* proto_register_zbee_nwk_gp */

/*FUNCTION:------------------------------------------------------
 *  NAME
 *      proto_reg_handoff_zbee_nwk_gp
 *  DESCRIPTION
 *      Registers the ZigBee dissector with Wireshark.
 *  PARAMETERS
 *      none
 *  RETURNS
 *      void
 *---------------------------------------------------------------
 */
void
proto_reg_handoff_zbee_nwk_gp(void)
{
    /* Find the other dissectors we need. */
    data_handle = find_dissector("data");
    /* Register our dissector with IEEE 802.15.4. */
    dissector_add_for_decode_as(IEEE802154_PROTOABBREV_WPAN_PANID, find_dissector(ZBEE_PROTOABBREV_NWK_GP));
    heur_dissector_add(IEEE802154_PROTOABBREV_WPAN, dissect_zbee_nwk_heur_gp, "ZigBee Green Power over IEEE 802.15.4", "zbee_nwk_gp_wlan", proto_zbee_nwk_gp, HEURISTIC_ENABLE);
} /* proto_reg_handoff_zbee */

/*
 * Editor modelines
 *
 * Local Variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * ex: set shiftwidth=4 tabstop=8 expandtab:
 * :indentSize=4:tabSize=8:noTabs=true:
 */
