static void
dissect_eapol(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  int         offset = 0;
  guint8      eapol_type;
  guint8      keydesc_type;
  guint16     eapol_len;
  guint       len;
  guint16     eapol_key_len, eapol_data_len;
  guint8      key_index;
  guint16     keyinfo;
  proto_tree *ti = NULL;
  proto_tree *eapol_tree = NULL;
  proto_tree *keyinfo_item = NULL;
  proto_tree *keyinfo_tree = NULL;
  proto_tree *key_index_tree, *keydes_tree;
  tvbuff_t   *next_tvb;

  col_set_str(pinfo->cinfo, COL_PROTOCOL, "EAPOL");
  col_clear(pinfo->cinfo, COL_INFO);

  if (tree) {
    ti = proto_tree_add_item(tree, proto_eapol, tvb, 0, -1, FALSE);
    eapol_tree = proto_item_add_subtree(ti, ett_eapol);

    proto_tree_add_item(eapol_tree, hf_eapol_version, tvb, offset, 1, FALSE);
  }
  offset++;

  eapol_type = tvb_get_guint8(tvb, offset);
  if (tree)
    proto_tree_add_uint(eapol_tree, hf_eapol_type, tvb, offset, 1, eapol_type);
  if (check_col(pinfo->cinfo, COL_INFO))
    col_add_str(pinfo->cinfo, COL_INFO,
		val_to_str(eapol_type, eapol_type_vals, "Unknown type (0x%02X)"));
  offset++;

  eapol_len = tvb_get_ntohs(tvb, offset);
  len = EAPOL_HDR_LEN + eapol_len;
  set_actual_length(tvb, len);
  if (tree) {
    proto_item_set_len(ti, len);
    proto_tree_add_uint(eapol_tree, hf_eapol_len, tvb, offset, 2, eapol_len);
  }
  offset += 2;

  switch (eapol_type) {

  case EAP_PACKET:
    next_tvb = tvb_new_subset_remaining(tvb, offset);
    call_dissector(eap_handle, next_tvb, pinfo, eapol_tree);
    break;

  case EAPOL_KEY:
    if (tree) {
      keydesc_type = tvb_get_guint8(tvb, offset);
      proto_tree_add_item(eapol_tree, hf_eapol_keydes_type, tvb, offset, 1, FALSE);
      offset += 1;
      if (keydesc_type == EAPOL_WPA_KEY || keydesc_type == EAPOL_RSN_KEY) {
	keyinfo = tvb_get_ntohs(tvb, offset);
	keyinfo_item =
	  proto_tree_add_uint(eapol_tree, hf_eapol_wpa_keydes_keyinfo, tvb,
			      offset, 2, keyinfo);

	keyinfo_tree = proto_item_add_subtree(keyinfo_item, ett_keyinfo);
	proto_tree_add_uint(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_keydes_ver, tvb, offset, 2, keyinfo);
	proto_tree_add_boolean(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_key_type, tvb, offset, 2, keyinfo);
	proto_tree_add_uint(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_key_index, tvb, offset, 2, keyinfo);
	proto_tree_add_boolean(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_install, tvb, offset, 2, keyinfo);
	proto_tree_add_boolean(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_key_ack, tvb, offset, 2, keyinfo);
	proto_tree_add_boolean(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_key_mic, tvb, offset, 2, keyinfo);
	proto_tree_add_boolean(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_secure, tvb, offset, 2, keyinfo);
	proto_tree_add_boolean(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_error, tvb, offset, 2, keyinfo);
	proto_tree_add_boolean(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_request, tvb, offset, 2, keyinfo);
	proto_tree_add_boolean(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_encr_key_data, tvb, offset, 2, keyinfo);

        offset += 2;
        proto_tree_add_uint(eapol_tree, hf_eapol_keydes_keylen, tvb, offset,
        		    2, tvb_get_ntohs(tvb, offset));
        offset += 2;
        proto_tree_add_item(eapol_tree, hf_eapol_keydes_replay_counter, tvb,
        		    offset, 8, FALSE);
        offset += 8;
        proto_tree_add_item(eapol_tree, hf_eapol_wpa_keydes_nonce, tvb, offset,
        		    32, FALSE);
        offset += 32;
        proto_tree_add_item(eapol_tree, hf_eapol_keydes_key_iv, tvb,
        		    offset, 16, FALSE);
        offset += 16;
        proto_tree_add_item(eapol_tree, hf_eapol_wpa_keydes_rsc, tvb, offset,
        		    8, FALSE);
        offset += 8;
        proto_tree_add_item(eapol_tree, hf_eapol_wpa_keydes_id, tvb, offset, 8,
        		    FALSE);
        offset += 8;
        proto_tree_add_item(eapol_tree, hf_eapol_wpa_keydes_mic, tvb, offset,
        		    16, FALSE);
        offset += 16;
        eapol_data_len = tvb_get_ntohs(tvb, offset);
        proto_tree_add_uint(eapol_tree, hf_eapol_wpa_keydes_datalen, tvb,
        		    offset, 2, eapol_data_len);
        offset += 2;
        if (eapol_data_len != 0) {
          ti = proto_tree_add_item(eapol_tree, hf_eapol_wpa_keydes_data,
          	tvb, offset, eapol_data_len, FALSE);
	  if ((keyinfo & KEY_INFO_ENCR_KEY_DATA_MASK) ||
	      !(keyinfo & KEY_INFO_KEY_TYPE_MASK)) {
	    /* RSN: EAPOL-Key Key Data is encrypted.
	     * WPA: Group Keys use encrypted Key Data.
	     * Cannot parse this without knowing the key.
	     * IEEE 802.11i-2004 8.5.2.
	     */
	  } else {
	    keydes_tree = proto_item_add_subtree(ti, ett_eapol_keydes_data);
	    ieee_80211_add_tagged_parameters(tvb, offset, pinfo, keydes_tree,
					     eapol_data_len);
	  }
        }
      } else {
        eapol_key_len = tvb_get_ntohs(tvb, offset);
        proto_tree_add_uint(eapol_tree, hf_eapol_keydes_keylen, tvb, offset, 2, eapol_key_len);
        offset += 2;
        proto_tree_add_item(eapol_tree, hf_eapol_keydes_replay_counter, tvb,
  			  offset, 8, FALSE);
        offset += 8;
        proto_tree_add_item(eapol_tree, hf_eapol_keydes_key_iv, tvb,
  			  offset, 16, FALSE);
        offset += 16;
        key_index = tvb_get_guint8(tvb, offset);
        ti = proto_tree_add_text(eapol_tree, tvb, offset, 1,
  			       "Key Index: %s, index %u",
  			       (key_index & 0x80) ? "unicast" : "broadcast",
  			       key_index & 0x7F);
        key_index_tree = proto_item_add_subtree(ti, ett_eapol_key_index);
        proto_tree_add_boolean(eapol_tree, hf_eapol_keydes_key_index_keytype,
  			     tvb, offset, 1, key_index);
        proto_tree_add_uint(eapol_tree, hf_eapol_keydes_key_index_indexnum,
  			     tvb, offset, 1, key_index);
        offset += 1;
        proto_tree_add_item(eapol_tree, hf_eapol_keydes_key_signature, tvb,
  			  offset, 16, FALSE);
        offset += 16;
        if (eapol_key_len != 0) {
          if (eapol_len > 44) { /* Size of rc4 key with no key content */
            proto_tree_add_item(eapol_tree, hf_eapol_keydes_key, tvb, offset,
  			    eapol_key_len, FALSE);
          } else {
		/* IEEE 802.1X-2004 7.6.3.6: If no bytes remain, then  */
		proto_tree_add_text(eapol_tree, tvb, offset, 0, "Key: Use key locally generated by peer");
          }
        }
      }
    }
    break;

  case EAPOL_ENCAP_ASF_ALERT:	/* XXX - is this an SNMP trap? */
  default:
    next_tvb = tvb_new_subset_remaining(tvb, offset);
    call_dissector(data_handle, next_tvb, pinfo, eapol_tree);
    break;
  }
}
static void
dissect_eapol(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  int         offset = 0;
  guint8      eapol_type;
  guint8      keydesc_type;
  guint16     eapol_len;
  guint       len;
  guint16     eapol_key_len, eapol_data_len;
  guint16     keyinfo;
  gboolean    generated_locally;
  proto_tree *ti = NULL;
  proto_tree *eapol_tree = NULL;
  proto_tree *keyinfo_item = NULL;
  proto_tree *keyinfo_tree = NULL;
  proto_tree *key_index_tree, *keydes_tree;
  tvbuff_t   *next_tvb;
  guint8     counter;

  col_set_str(pinfo->cinfo, COL_PROTOCOL, "EAPOL");
  col_clear(pinfo->cinfo, COL_INFO);

  if (tree) {
    ti = proto_tree_add_item(tree, proto_eapol, tvb, 0, -1, ENC_NA);
    eapol_tree = proto_item_add_subtree(ti, ett_eapol);

    proto_tree_add_item(eapol_tree, hf_eapol_version, tvb, offset, 1, ENC_BIG_ENDIAN);
  }
  offset++;

  eapol_type = tvb_get_guint8(tvb, offset);
  if (tree)
    proto_tree_add_item(eapol_tree, hf_eapol_type, tvb, offset, 1, ENC_BIG_ENDIAN);
  col_add_str(pinfo->cinfo, COL_INFO,
                val_to_str(eapol_type, eapol_type_vals, "Unknown Type (0x%02X)"));
  offset++;

  eapol_len = tvb_get_ntohs(tvb, offset);
  len = EAPOL_HDR_LEN + eapol_len;
  set_actual_length(tvb, len);
  if (tree) {
    proto_item_set_len(ti, len);
    proto_tree_add_item(eapol_tree, hf_eapol_len, tvb, offset, 2, ENC_BIG_ENDIAN);
  }
  offset += 2;

  switch (eapol_type) {

  case EAP_PACKET:
    next_tvb = tvb_new_subset_remaining(tvb, offset);
    call_dissector(eap_handle, next_tvb, pinfo, eapol_tree);
    break;

  case EAPOL_KEY:
    if (tree) {
      keydesc_type = tvb_get_guint8(tvb, offset);
      proto_tree_add_item(eapol_tree, hf_eapol_keydes_type, tvb, offset, 1, ENC_BIG_ENDIAN);
      offset += 1;
      if (keydesc_type == EAPOL_WPA_KEY || keydesc_type == EAPOL_RSN_KEY) {
        keyinfo = tvb_get_ntohs(tvb, offset);
        if (keyinfo & KEY_INFO_REQUEST_MASK) {
          col_set_str(pinfo->cinfo, COL_INFO, "Key (Request)");
          if (keyinfo & KEY_INFO_ERROR_MASK)
            col_set_str(pinfo->cinfo, COL_INFO, "Key (Request, Error)");
        } else if (keyinfo & KEY_INFO_KEY_TYPE_MASK) {
          guint16 masked;
          masked = keyinfo &
            (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK |
             KEY_INFO_KEY_MIC_MASK | KEY_INFO_SECURE_MASK);

         if (keydesc_type == EAPOL_WPA_KEY) {
             switch (masked) {
                     case KEY_INFO_KEY_ACK_MASK:
                     col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 1 of 4)");
                     break;
                     case KEY_INFO_KEY_MIC_MASK:
                     counter = tvb_get_guint8(tvb, offset+11);
                     if (!counter)
                             col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 2 of 4)");
                     else
                             col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 4 of 4)");
                     break;
                     case (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK |
                     KEY_INFO_KEY_MIC_MASK):
                     col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 3 of 4)");
                     break;
             }
         }

         if (keydesc_type == EAPOL_RSN_KEY) {
             switch (masked) {
                     case KEY_INFO_KEY_ACK_MASK:
                     col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 1 of 4)");
                     break;
                     case KEY_INFO_KEY_MIC_MASK:
                     col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 2 of 4)");
                     break;
                     case (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK |
                     KEY_INFO_KEY_MIC_MASK | KEY_INFO_SECURE_MASK):
                     col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 3 of 4)");
                     break;
                     case (KEY_INFO_KEY_MIC_MASK | KEY_INFO_SECURE_MASK):
                     col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 4 of 4)");
                     break;
              }
         }

        } else {
          if (keyinfo & KEY_INFO_KEY_ACK_MASK)
            col_set_str(pinfo->cinfo, COL_INFO, "Key (Group Message 1 of 2)");
          else
            col_set_str(pinfo->cinfo, COL_INFO, "Key (Group Message 2 of 2)");
        }
        keyinfo_item =
          proto_tree_add_item(eapol_tree, hf_eapol_wpa_keydes_keyinfo, tvb,
                              offset, 2, ENC_BIG_ENDIAN);

        keyinfo_tree = proto_item_add_subtree(keyinfo_item, ett_keyinfo);
        proto_tree_add_item(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_keydes_version, tvb, offset, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_key_type, tvb, offset, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_key_index, tvb, offset, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_install, tvb, offset, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_key_ack, tvb, offset, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_key_mic, tvb, offset, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_secure, tvb, offset, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_error, tvb, offset, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_request, tvb, offset, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(keyinfo_tree, hf_eapol_wpa_keydes_keyinfo_encrypted_key_data, tvb, offset, 2, ENC_BIG_ENDIAN);

        offset += 2;
        proto_tree_add_item(eapol_tree, hf_eapol_keydes_key_len, tvb, offset,
                            2, ENC_BIG_ENDIAN);
        offset += 2;
        proto_tree_add_item(eapol_tree, hf_eapol_keydes_replay_counter, tvb,
                            offset, 8, ENC_BIG_ENDIAN);
        offset += 8;
        proto_tree_add_item(eapol_tree, hf_eapol_wpa_keydes_nonce, tvb, offset,
                            32, ENC_NA);
        offset += 32;
        proto_tree_add_item(eapol_tree, hf_eapol_keydes_key_iv, tvb,
                            offset, 16, ENC_NA);
        offset += 16;
        proto_tree_add_item(eapol_tree, hf_eapol_wpa_keydes_rsc, tvb, offset,
                            8, ENC_NA);
        offset += 8;
        proto_tree_add_item(eapol_tree, hf_eapol_wpa_keydes_id, tvb, offset, 8,
                            ENC_NA);
        offset += 8;
        proto_tree_add_item(eapol_tree, hf_eapol_wpa_keydes_mic, tvb, offset,
                            16, ENC_NA);
        offset += 16;
        eapol_data_len = tvb_get_ntohs(tvb, offset);
        proto_tree_add_item(eapol_tree, hf_eapol_wpa_keydes_data_len, tvb,
                            offset, 2, ENC_BIG_ENDIAN);
        offset += 2;
        if (eapol_data_len != 0) {
          ti = proto_tree_add_item(eapol_tree, hf_eapol_wpa_keydes_data,
                tvb, offset, eapol_data_len, ENC_NA);
          if ((keyinfo & KEY_INFO_ENCRYPTED_KEY_DATA_MASK) ||
              !(keyinfo & KEY_INFO_KEY_TYPE_MASK)) {
            /* RSN: EAPOL-Key Key Data is encrypted.
             * WPA: Group Keys use encrypted Key Data.
             * Cannot parse this without knowing the key.
             * IEEE 802.11i-2004 8.5.2.
             */
          } else {
            keydes_tree = proto_item_add_subtree(ti, ett_eapol_keydes_data);
            ieee_80211_add_tagged_parameters(tvb, offset, pinfo, keydes_tree,
                                             eapol_data_len, -1);
          }
        }
      } else {
        eapol_key_len = tvb_get_ntohs(tvb, offset);
        proto_tree_add_item(eapol_tree, hf_eapol_keydes_key_len, tvb, offset, 2, ENC_BIG_ENDIAN);
        offset += 2;
        proto_tree_add_item(eapol_tree, hf_eapol_keydes_replay_counter, tvb,
                            offset, 8, ENC_BIG_ENDIAN);
        offset += 8;
        proto_tree_add_item(eapol_tree, hf_eapol_keydes_key_iv, tvb,
                            offset, 16, ENC_NA);
        offset += 16;
        ti = proto_tree_add_item(eapol_tree, hf_eapol_keydes_key_index, tvb, offset, 1, ENC_BIG_ENDIAN);
        key_index_tree = proto_item_add_subtree(ti, ett_eapol_key_index);
        proto_tree_add_item(key_index_tree, hf_eapol_keydes_key_index_type,
                            tvb, offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(key_index_tree, hf_eapol_keydes_key_index_number,
                            tvb, offset, 1, ENC_BIG_ENDIAN);
        offset += 1;
        proto_tree_add_item(eapol_tree, hf_eapol_keydes_key_signature, tvb,
                            offset, 16, ENC_NA);
        offset += 16;
        if (eapol_key_len != 0) {
          /* IEEE 802.1X-2004 7.6.3.6: If no bytes remain, then */
          generated_locally = eapol_len <= 44; /* Size of rc4 key with no key content */
          if (!generated_locally) {
              proto_tree_add_item(eapol_tree, hf_eapol_keydes_key, tvb, offset,
                                  eapol_key_len, ENC_NA);
          }

          proto_tree_add_boolean(eapol_tree, hf_eapol_keydes_key_generated_locally, tvb, offset,
                                 0, generated_locally);
        }
      }
    }
    break;

  case EAPOL_ENCAP_ASF_ALERT:   /* XXX - is this an SNMP trap? */
  default:
    next_tvb = tvb_new_subset_remaining(tvb, offset);
    call_dissector(data_handle, next_tvb, pinfo, eapol_tree);
    break;
  }
}