nl80211: Dissect further attributes

Add code to dissect:
- Top level nested attributes
- Top level nested array attributes
- Top level attributes with enum fields
- nl80211_band_attr sub attribute
- nl80211_bss sub attribute
- nl80211_sta_info sub attribute
- Attributes containing 802.11 information elements

Also update tools/generate-nl80211-fields.py to generate further
code blocks.

NOTE: This commit will not build alone as it does not contain the
generated definitions needed.

Depends-On: Ibd8c296c4a3d2f880f359f075271b89625367898
Change-Id: I23b87f41c6230d3fc1eb0f6b050b0d5209a9dd5c
Reviewed-on: https://code.wireshark.org/review/29317
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Mikael Kanstrup 2018-08-17 11:09:11 +02:00 committed by Anders Broman
parent b62822055f
commit 5442b51267
2 changed files with 540 additions and 101 deletions

View File

@ -2,6 +2,7 @@
* Dissector for nl80211 (over Netlink).
*
* Copyright (c) 2017, Peter Wu <peter@lekensteyn.nl>
* Copyright (c) 2018, Mikael Kanstrup <mikael.kanstrup@sony.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
@ -15,12 +16,15 @@
#include "config.h"
#include <epan/packet.h>
#include "packet-ieee80211.h"
#include "packet-netlink.h"
void proto_register_netlink_nl80211(void);
void proto_reg_handoff_netlink_nl80211(void);
/* Extracted using tools/make-nl80211-fields.py */
#define NETLINK_NL80211_HFI_INIT HFI_INIT(proto_netlink_generic)
/* Extracted using tools/generate-nl80211-fields.py */
/* Definitions from linux/nl80211.h {{{ */
enum ws_nl80211_commands {
WS_NL80211_CMD_UNSPEC,
@ -781,6 +785,17 @@ static const value_string ws_nl80211_attrs_vals[] = {
{ 0, NULL }
};
static value_string_ext ws_nl80211_attrs_vals_ext = VALUE_STRING_EXT_INIT(ws_nl80211_attrs_vals);
static header_field_info hfi_nl80211_commands NETLINK_NL80211_HFI_INIT =
{ "Command", "nl80211.cmd", FT_UINT8, BASE_DEC | BASE_EXT_STRING,
VALS(&ws_nl80211_commands_vals_ext), 0x00, "Generic Netlink Command", HFILL };
static header_field_info hfi_nl80211_attrs NETLINK_NL80211_HFI_INIT =
{ "Attribute Type", "nl80211.attr_type", FT_UINT16, BASE_DEC | BASE_EXT_STRING,
VALS(&ws_nl80211_attrs_vals_ext), 0x00, NULL, HFILL };
static gint ett_nl80211_commands = -1;
static gint ett_nl80211_attrs = -1;
/* }}} */
@ -790,14 +805,7 @@ static dissector_handle_t netlink_nl80211_handle;
static header_field_info *hfi_netlink_nl80211 = NULL;
#define NETLINK_NL80211_HFI_INIT HFI_INIT(proto_netlink_generic)
static gint ett_nl80211 = -1;
static gint ett_nl80211_attr = -1;
static header_field_info hfi_nl80211_attr_type NETLINK_NL80211_HFI_INIT =
{ "Attribute Type", "nl80211.attr_type", FT_UINT16, BASE_DEC | BASE_EXT_STRING,
VALS(&ws_nl80211_attrs_vals_ext), 0x00, NULL, HFILL };
static header_field_info hfi_nl80211_attr_value NETLINK_NL80211_HFI_INIT =
{ "Attribute Value", "nl80211.attr_value", FT_BYTES, BASE_NONE,
@ -812,39 +820,330 @@ static header_field_info hfi_nl80211_attr_value32 NETLINK_NL80211_HFI_INIT =
NULL, 0x00, NULL, HFILL };
static int
dissect_nl80211_attrs(tvbuff_t *tvb, void *data, proto_tree *tree, int nla_type, int offset, int len)
dissect_nl80211_generic(tvbuff_t *tvb, void *data, proto_tree *tree, _U_ int nla_type, int offset, int len)
{
enum ws_nl80211_commands type = (enum ws_nl80211_commands) nla_type;
genl_info_t *genl_info = (genl_info_t *)data;
switch (type) {
/* TODO add more fields here? */
default:
/*
* No specific dissection available, apply arbitrary heuristics to
* determine whether we have an u16 or u32 field and treat others as
* opaque bytes.
*/
if (len) {
if (len == 2) {
proto_tree_add_item(tree, &hfi_nl80211_attr_value16, tvb, offset, len, genl_info->encoding);
} else if (len == 4) {
proto_tree_add_item(tree, &hfi_nl80211_attr_value32, tvb, offset, len, genl_info->encoding);
} else {
proto_tree_add_item(tree, &hfi_nl80211_attr_value, tvb, offset, len, genl_info->encoding);
}
offset += len;
/*
* No specific dissection available, apply arbitrary heuristics to
* determine whether we have an u16 or u32 field and treat others as
* opaque bytes.
*/
if (len) {
if (len == 2) {
proto_tree_add_item(tree, &hfi_nl80211_attr_value16, tvb, offset, len, genl_info->encoding);
} else if (len == 4) {
proto_tree_add_item(tree, &hfi_nl80211_attr_value32, tvb, offset, len, genl_info->encoding);
} else {
proto_tree_add_item(tree, &hfi_nl80211_attr_value, tvb, offset, len, genl_info->encoding);
}
offset += len;
}
return offset;
}
struct attr_lookup {
unsigned int attr_type;
header_field_info* hfi;
gint* ett;
int (*func)(tvbuff_t *tvb, void *data, proto_tree *tree, int nla_type, int offset, int len);
};
static int
dissect_nested_attr(tvbuff_t *tvb, void *data, proto_tree *tree, int nla_type, int offset, int len, const struct attr_lookup *nested)
{
genl_info_t *genl_info = (genl_info_t *)data;
for (int i = 0; nested[i].hfi; i++) {
if (nested[i].attr_type != (nla_type & NLA_TYPE_MASK)) {
continue;
}
offset = dissect_netlink_attributes(tvb, nested[i].hfi, *nested[i].ett, genl_info,
genl_info->data, tree, offset, len,
nested[i].func ? nested[i].func : dissect_nl80211_generic);
break;
}
return offset;
}
static int
dissect_nested_attr_array(tvbuff_t *tvb, void *data, proto_tree *tree, int nla_type, int offset, int len, const struct attr_lookup *nested_arr)
{
genl_info_t *genl_info = (genl_info_t *)data;
for (int i = 0; nested_arr[i].hfi; i++) {
if (nested_arr[i].attr_type != (nla_type & NLA_TYPE_MASK)) {
continue;
}
offset = dissect_netlink_attributes_array(tvb, nested_arr[i].hfi, *nested_arr[i].ett,
*nested_arr[i].ett, genl_info,
genl_info->data, tree, offset, len,
nested_arr[i].func ?
nested_arr[i].func : dissect_nl80211_generic);
break;
}
return offset;
}
static int
dissect_value(tvbuff_t *tvb, void *data, proto_tree *tree, int nla_type, int offset, int len, const struct attr_lookup *values)
{
genl_info_t *genl_info = (genl_info_t *)data;
for (int i = 0; values[i].hfi; i++) {
if (values[i].attr_type != (nla_type & NLA_TYPE_MASK)) {
continue;
}
proto_tree_add_item(tree, values[i].hfi, tvb, offset, len, genl_info->encoding);
return offset + len;
}
return offset;
}
static packet_info *m_pinfo = NULL; /* TODO find a better way to pass pinfo to functions */
static int
dissect_information_elements(tvbuff_t *tvb, proto_tree *tree, int offset, int len)
{
int offset_end = offset + len;
while (offset < offset_end) {
int tlen = add_tagged_field(m_pinfo, tree, tvb, offset, 0, NULL, 0, NULL);
if (tlen == 0) {
break;
}
offset += tlen;
}
return offset;
}
static int
dissect_nl80211_frequency_attr(tvbuff_t *tvb, void *data, proto_tree *tree, int nla_type, int offset, int len)
{
static const struct attr_lookup nested[] = {
{ WS_NL80211_FREQUENCY_ATTR_DFS_STATE, &hfi_nl80211_dfs_state, &ett_nl80211_dfs_state, NULL},
{ WS_NL80211_FREQUENCY_ATTR_WMM, &hfi_nl80211_wmm_rule, &ett_nl80211_wmm_rule, NULL},
{ 0, NULL, NULL, NULL }
};
enum ws_nl80211_frequency_attr type = (enum ws_nl80211_frequency_attr) nla_type & NLA_TYPE_MASK;
int offset_end = offset + len;
if (offset < offset_end) {
offset = dissect_nested_attr(tvb, data, tree, nla_type, offset, len, nested);
}
if (offset < offset_end) {
switch (type) {
default:
offset = dissect_nl80211_generic(tvb, data, tree, nla_type, offset, len);
break;
}
}
return offset;
}
static int
dissect_nl80211_band_attr(tvbuff_t *tvb, void *data, proto_tree *tree, int nla_type, int offset, int len)
{
static const struct attr_lookup nested_arr[] = {
{ WS_NL80211_BAND_ATTR_FREQS, &hfi_nl80211_frequency_attr, &ett_nl80211_frequency_attr, dissect_nl80211_frequency_attr },
{ WS_NL80211_BAND_ATTR_RATES, &hfi_nl80211_bitrate_attr, &ett_nl80211_bitrate_attr, NULL },
{ WS_NL80211_BAND_ATTR_IFTYPE_DATA, &hfi_nl80211_band_iftype_attr, &ett_nl80211_band_iftype_attr, NULL },
{ 0, NULL, NULL, NULL }
};
enum ws_nl80211_band_attr type = (enum ws_nl80211_band_attr) nla_type & NLA_TYPE_MASK;
int offset_end = offset + len;
if (offset < offset_end) {
offset = dissect_nested_attr_array(tvb, data, tree, nla_type, offset, len, nested_arr);
}
if (offset < offset_end) {
switch (type) {
/* TODO add more fields here? */
default:
offset = dissect_nl80211_generic(tvb, data, tree, nla_type, offset, len);
break;
}
}
return offset;
}
static int
dissect_nl80211_bss(tvbuff_t *tvb, void *data, proto_tree *tree, int nla_type, int offset, int len)
{
static const struct attr_lookup values[] = {
{ WS_NL80211_BSS_STATUS, &hfi_nl80211_bss_status, NULL, NULL },
{ WS_NL80211_BSS_CHAN_WIDTH, &hfi_nl80211_bss_scan_width, NULL, NULL },
{ 0, NULL, NULL, NULL }
};
enum ws_nl80211_bss type = (enum ws_nl80211_bss) nla_type & NLA_TYPE_MASK;
int offset_end = offset + len;
if (offset < offset_end) {
offset = dissect_value(tvb, data, tree, nla_type, offset, len, values);
}
if (offset < offset_end) {
switch (type) {
case WS_NL80211_BSS_INFORMATION_ELEMENTS:
case WS_NL80211_BSS_BEACON_IES:
offset = dissect_information_elements(tvb, tree, offset, len);
break;
default:
offset = dissect_nl80211_generic(tvb, data, tree, nla_type, offset, len);
break;
}
}
return offset;
}
static int
dissect_nl80211_tid_stats(tvbuff_t *tvb, void *data, proto_tree *tree, int nla_type, int offset, int len)
{
static const struct attr_lookup nested[] = {
{ WS_NL80211_TID_STATS_TXQ_STATS, &hfi_nl80211_txq_stats, &ett_nl80211_txq_stats, NULL},
{ 0, NULL, NULL, NULL }
};
enum ws_nl80211_tid_stats type = (enum ws_nl80211_tid_stats) nla_type & NLA_TYPE_MASK;
int offset_end = offset + len;
if (offset < offset_end) {
offset = dissect_nested_attr(tvb, data, tree, nla_type, offset, len, nested);
}
if (offset < offset_end) {
switch (type) {
default:
offset = dissect_nl80211_generic(tvb, data, tree, nla_type, offset, len);
break;
}
}
return offset;
}
static int
dissect_nl80211_sta_info(tvbuff_t *tvb, void *data, proto_tree *tree, int nla_type, int offset, int len)
{
static const struct attr_lookup nested[] = {
{ WS_NL80211_STA_INFO_TX_BITRATE, &hfi_nl80211_rate_info, &ett_nl80211_rate_info, NULL},
{ WS_NL80211_STA_INFO_RX_BITRATE, &hfi_nl80211_rate_info, &ett_nl80211_rate_info, NULL},
{ WS_NL80211_STA_INFO_BSS_PARAM, &hfi_nl80211_sta_bss_param, &ett_nl80211_sta_bss_param, NULL },
{ 0, NULL, NULL, NULL }
};
static const struct attr_lookup nested_arr[] = {
{ WS_NL80211_STA_INFO_TID_STATS, &hfi_nl80211_tid_stats, &ett_nl80211_tid_stats, dissect_nl80211_tid_stats},
{ 0, NULL, NULL, NULL }
};
enum ws_nl80211_sta_info type = (enum ws_nl80211_sta_info) nla_type & NLA_TYPE_MASK;
int offset_end = offset + len;
if (offset < offset_end) {
offset = dissect_nested_attr(tvb, data, tree, nla_type, offset, len, nested);
}
if (offset < offset_end) {
offset = dissect_nested_attr_array(tvb, data, tree, nla_type, offset, len, nested_arr);
}
if (offset < offset_end) {
switch (type) {
default:
offset = dissect_nl80211_generic(tvb, data, tree, nla_type, offset, len);
break;
}
}
return offset;
}
static header_field_info hfi_nl80211_cmd NETLINK_NL80211_HFI_INIT =
{ "Command", "nl80211.cmd", FT_UINT8, BASE_DEC | BASE_EXT_STRING,
&ws_nl80211_commands_vals_ext, 0x00, "Generic Netlink command", HFILL };
static int
dissect_nl80211_attrs(tvbuff_t *tvb, void *data, proto_tree *tree, int nla_type, int offset, int len)
{
static const struct attr_lookup nested[] = {
{ WS_NL80211_ATTR_SUPPORTED_IFTYPES, &hfi_nl80211_iftype, &ett_nl80211_iftype, NULL },
{ WS_NL80211_ATTR_STA_FLAGS, &hfi_nl80211_sta_flags, &ett_nl80211_sta_flags, NULL },
{ WS_NL80211_ATTR_STA_INFO, &hfi_nl80211_sta_info, &ett_nl80211_sta_info, dissect_nl80211_sta_info },
{ WS_NL80211_ATTR_MPATH_INFO, &hfi_nl80211_mpath_info, &ett_nl80211_mpath_info, NULL },
{ WS_NL80211_ATTR_MNTR_FLAGS, &hfi_nl80211_mntr_flags, &ett_nl80211_mntr_flags, NULL },
{ WS_NL80211_ATTR_BSS, &hfi_nl80211_bss, &ett_nl80211_bss, dissect_nl80211_bss },
{ WS_NL80211_ATTR_KEY, &hfi_nl80211_key_attributes, &ett_nl80211_key_attributes, NULL },
{ WS_NL80211_ATTR_SURVEY_INFO, &hfi_nl80211_survey_info, &ett_nl80211_survey_info, NULL },
{ WS_NL80211_ATTR_FREQ_BEFORE, &hfi_nl80211_frequency_attr, &ett_nl80211_frequency_attr, NULL },
{ WS_NL80211_ATTR_FREQ_AFTER, &hfi_nl80211_frequency_attr, &ett_nl80211_frequency_attr, NULL },
{ WS_NL80211_ATTR_TX_RATES, &hfi_nl80211_tx_rate_attributes, &ett_nl80211_tx_rate_attributes, NULL },
{ WS_NL80211_ATTR_CQM, &hfi_nl80211_attr_cqm, &ett_nl80211_attr_cqm, NULL },
{ WS_NL80211_ATTR_KEY_DEFAULT_TYPES, &hfi_nl80211_key_default_types, &ett_nl80211_key_default_types, NULL },
{ WS_NL80211_ATTR_MESH_SETUP, &hfi_nl80211_mesh_setup_params, &ett_nl80211_mesh_setup_params, NULL },
{ WS_NL80211_ATTR_MESH_CONFIG, &hfi_nl80211_meshconf_params, &ett_nl80211_meshconf_params, NULL },
{ WS_NL80211_ATTR_SCHED_SCAN_MATCH, &hfi_nl80211_sched_scan_match_attr, &ett_nl80211_sched_scan_match_attr, NULL },
{ WS_NL80211_ATTR_INTERFACE_COMBINATIONS, &hfi_nl80211_if_combination_attrs, &ett_nl80211_if_combination_attrs, NULL },
{ WS_NL80211_ATTR_REKEY_DATA, &hfi_nl80211_rekey_data, &ett_nl80211_rekey_data, NULL },
{ WS_NL80211_ATTR_STA_WME, &hfi_nl80211_sta_wme_attr, &ett_nl80211_sta_wme_attr, NULL },
{ WS_NL80211_ATTR_PMKSA_CANDIDATE, &hfi_nl80211_pmksa_candidate_attr, &ett_nl80211_pmksa_candidate_attr, NULL },
{ WS_NL80211_ATTR_SCHED_SCAN_PLANS, &hfi_nl80211_sched_scan_plan, &ett_nl80211_sched_scan_plan, NULL },
{ WS_NL80211_ATTR_BSS_SELECT, &hfi_nl80211_bss_select_attr, &ett_nl80211_bss_select_attr, NULL },
{ WS_NL80211_ATTR_IFTYPE_EXT_CAPA, &hfi_nl80211_attrs, &ett_nl80211_attrs, dissect_nl80211_attrs },
{ WS_NL80211_ATTR_NAN_FUNC, &hfi_nl80211_nan_func_attributes, &ett_nl80211_nan_func_attributes, NULL },
{ WS_NL80211_ATTR_NAN_MATCH, &hfi_nl80211_nan_match_attributes, &ett_nl80211_nan_match_attributes, NULL },
{ WS_NL80211_ATTR_TXQ_STATS, &hfi_nl80211_txq_stats, &ett_nl80211_txq_stats, NULL },
{ 0, NULL, NULL, NULL }
};
static const struct attr_lookup nested_arr[] = {
{ WS_NL80211_ATTR_WIPHY_TXQ_PARAMS, &hfi_nl80211_txq_attr, &ett_nl80211_txq_attr, NULL },
{ WS_NL80211_ATTR_WIPHY_BANDS, &hfi_nl80211_band_attr, &ett_nl80211_band_attr, dissect_nl80211_band_attr },
{ WS_NL80211_ATTR_REG_RULES, &hfi_nl80211_reg_rule_attr, &ett_nl80211_reg_rule_attr, NULL },
{ 0, NULL, NULL, NULL }
};
static const struct attr_lookup values[] = {
{ WS_NL80211_ATTR_CHANNEL_WIDTH, &hfi_nl80211_chan_width, NULL, NULL },
{ WS_NL80211_ATTR_WIPHY_CHANNEL_TYPE, &hfi_nl80211_channel_type, NULL, NULL },
{ WS_NL80211_ATTR_IFTYPE, &hfi_nl80211_iftype, NULL, NULL },
{ WS_NL80211_ATTR_STA_PLINK_ACTION, &hfi_plink_actions, NULL, NULL },
{ WS_NL80211_ATTR_MPATH_INFO, &hfi_nl80211_mpath_info, NULL, NULL },
{ WS_NL80211_ATTR_REG_INITIATOR, &hfi_nl80211_reg_initiator, NULL, NULL },
{ WS_NL80211_ATTR_REG_TYPE, &hfi_nl80211_reg_type, NULL, NULL },
{ WS_NL80211_ATTR_AUTH_TYPE, &hfi_nl80211_auth_type, NULL, NULL },
{ WS_NL80211_ATTR_KEY_TYPE, &hfi_nl80211_key_type, NULL, NULL },
{ WS_NL80211_ATTR_USE_MFP, &hfi_nl80211_mfp, NULL, NULL },
{ WS_NL80211_ATTR_PS_STATE, &hfi_nl80211_ps_state, NULL, NULL },
{ WS_NL80211_ATTR_WIPHY_TX_POWER_SETTING, &hfi_nl80211_tx_power_setting, NULL, NULL },
{ WS_NL80211_ATTR_STA_PLINK_STATE, &hfi_nl80211_plink_state, NULL, NULL },
{ WS_NL80211_ATTR_TDLS_OPERATION, &hfi_nl80211_tdls_operation, NULL, NULL },
{ WS_NL80211_ATTR_DFS_REGION, &hfi_nl80211_dfs_regions, NULL, NULL },
{ WS_NL80211_ATTR_USER_REG_HINT_TYPE, &hfi_nl80211_user_reg_hint_type, NULL, NULL },
{ WS_NL80211_ATTR_CONN_FAILED_REASON, &hfi_nl80211_connect_failed_reason, NULL, NULL },
{ WS_NL80211_ATTR_LOCAL_MESH_POWER_MODE, &hfi_nl80211_mesh_power_mode, NULL, NULL },
{ WS_NL80211_ATTR_ACL_POLICY, &hfi_nl80211_acl_policy, NULL, NULL },
{ WS_NL80211_ATTR_RADAR_EVENT, &hfi_nl80211_radar_event, NULL, NULL },
{ WS_NL80211_ATTR_CRIT_PROT_ID, &hfi_nl80211_crit_proto_id, NULL, NULL },
{ WS_NL80211_ATTR_SMPS_MODE, &hfi_nl80211_smps_mode, NULL, NULL },
{ WS_NL80211_ATTR_STA_SUPPORT_P2P_PS, &hfi_nl80211_sta_p2p_ps_status, NULL, NULL },
{ WS_NL80211_ATTR_TIMEOUT_REASON, &hfi_nl80211_timeout_reason, NULL, NULL },
{ WS_NL80211_ATTR_EXTERNAL_AUTH_ACTION, &hfi_nl80211_external_auth_action, NULL, NULL },
{ 0, NULL, NULL, NULL }
};
enum ws_nl80211_attrs type = (enum ws_nl80211_attrs) nla_type & NLA_TYPE_MASK;
int offset_end = offset + len;
if (offset < offset_end) {
offset = dissect_nested_attr(tvb, data, tree, nla_type, offset, len, nested);
}
if (offset < offset_end) {
offset = dissect_nested_attr_array(tvb, data, tree, nla_type, offset, len, nested_arr);
}
if (offset < offset_end) {
offset = dissect_value(tvb, data, tree, nla_type, offset, len, values);
}
if (offset < offset_end) {
switch (type) {
case WS_NL80211_ATTR_HT_CAPABILITY:
case WS_NL80211_ATTR_IE:
case WS_NL80211_ATTR_REQ_IE:
case WS_NL80211_ATTR_RESP_IE:
case WS_NL80211_ATTR_IE_PROBE_RESP:
case WS_NL80211_ATTR_IE_ASSOC_RESP:
case WS_NL80211_ATTR_VHT_CAPABILITY:
case WS_NL80211_ATTR_CSA_IES:
case WS_NL80211_ATTR_HE_CAPABILITY:
offset = dissect_information_elements(tvb, tree, offset, len);
break;
/* TODO add more fields here? */
default:
offset = dissect_nl80211_generic(tvb, data, tree, nla_type, offset, len);
break;
}
}
return offset;
}
static int
dissect_netlink_nl80211(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
@ -858,13 +1157,14 @@ dissect_netlink_nl80211(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, voi
col_set_str(pinfo->cinfo, COL_PROTOCOL, "nl80211");
col_clear(pinfo->cinfo, COL_INFO);
m_pinfo = pinfo;
offset = dissect_genl_header(tvb, genl_info, &hfi_nl80211_cmd);
offset = dissect_genl_header(tvb, genl_info, &hfi_nl80211_commands);
pi = proto_tree_add_item(tree, proto_registrar_get_nth(proto_netlink_nl80211), tvb, offset, -1, ENC_NA);
nlmsg_tree = proto_item_add_subtree(pi, ett_nl80211);
offset = dissect_netlink_attributes(tvb, &hfi_nl80211_attr_type, ett_nl80211_attr, genl_info, genl_info->data, nlmsg_tree, offset, -1, dissect_nl80211_attrs);
offset = dissect_netlink_attributes(tvb, &hfi_nl80211_attrs, ett_nl80211_attrs, genl_info, genl_info->data, nlmsg_tree, offset, -1, dissect_nl80211_attrs);
return offset;
}
@ -874,17 +1174,24 @@ proto_register_netlink_nl80211(void)
{
#ifndef HAVE_HFI_SECTION_INIT
static header_field_info *hfi[] = {
&hfi_nl80211_cmd,
&hfi_nl80211_attr_type,
&hfi_nl80211_attr_value,
&hfi_nl80211_attr_value16,
&hfi_nl80211_attr_value32,
/* Extracted using tools/generate-nl80211-fields.py */
/* Definitions from linux/nl80211.h {{{ */
&hfi_nl80211_commands,
&hfi_nl80211_attrs,
/* }}} */
};
#endif
static gint *ett[] = {
&ett_nl80211,
&ett_nl80211_attr,
/* Extracted using tools/generate-nl80211-fields.py */
/* Definitions from linux/nl80211.h {{{ */
&ett_nl80211_commands,
&ett_nl80211_attrs,
/* }}} */
};
proto_netlink_nl80211 = proto_register_protocol("Linux 802.11 Netlink", "nl80211", "nl80211");

View File

@ -3,6 +3,7 @@
# (value_string) for packet-netlink-nl80211.c
#
# Copyright (c) 2017, Peter Wu <peter@lekensteyn.nl>
# Copyright (c) 2018, Mikael Kanstrup <mikael.kanstrup@sony.com>
#
# Wireshark - Network traffic analyzer
# By Gerald Combs <gerald@wireshark.org>
@ -25,20 +26,90 @@ import sys
HEADER = "/* Definitions from linux/nl80211.h {{{ */\n"
FOOTER = "/* }}} */\n"
# Enums to extract from the header file
EXPORT_ENUMS = ("nl80211_commands", "nl80211_attrs")
EXPORT_ENUMS = {
# 'enum_name': ('field_name', field_type', 'field_blurb')
'nl80211_commands': ('Command', 'FT_UINT8', '"Generic Netlink Command"'),
'nl80211_attrs': (None, None, None),
'nl80211_iftype': (None, None, None),
'nl80211_sta_flags': (None, None, None),
'nl80211_rate_info': (None, None, None),
'nl80211_sta_bss_param': (None, None, None),
'nl80211_sta_info': (None, None, None),
'nl80211_tid_stats': (None, None, None),
'nl80211_mpath_info': (None, None, None),
'nl80211_mntr_flags': (None, None, None),
'nl80211_bss': (None, None, None),
'nl80211_key_attributes': (None, None, None),
'nl80211_survey_info': (None, None, None),
'nl80211_frequency_attr': (None, None, None),
'nl80211_tx_rate_attributes': (None, None, None),
'nl80211_attr_cqm': (None, None, None),
'nl80211_key_default_types': (None, None, None),
'nl80211_mesh_setup_params': (None, None, None),
'nl80211_meshconf_params': (None, None, None),
'nl80211_if_combination_attrs': (None, None, None),
'nl80211_rekey_data': (None, None, None),
'nl80211_sta_wme_attr': (None, None, None),
'nl80211_pmksa_candidate_attr': (None, None, None),
'nl80211_sched_scan_plan': (None, None, None),
'nl80211_bss_select_attr': (None, None, None),
'nl80211_nan_func_attributes': (None, None, None),
'nl80211_nan_match_attributes': (None, None, None),
'nl80211_txq_stats': (None, None, None),
'nl80211_band_attr': (None, None, None),
'nl80211_bitrate_attr': (None, None, None),
'nl80211_reg_rule_attr': (None, None, None),
'nl80211_txq_attr': (None, None, None),
'nl80211_band_iftype_attr': (None, None, None),
'nl80211_dfs_state': (None, None, None),
'nl80211_wmm_rule': (None, None, None),
'nl80211_txq_stats': (None, None, None),
'nl80211_sched_scan_match_attr': (None, None, None),
'nl80211_chan_width': ('Attribute Value', 'FT_UINT32', None),
'nl80211_channel_type': ('Attribute Value', 'FT_UINT32', None),
'plink_actions': ('Attribute Value', 'FT_UINT8', None),
'nl80211_reg_initiator': ('Attribute Value', 'FT_UINT8', None),
'nl80211_reg_type': ('Attribute Value', 'FT_UINT8', None),
'nl80211_auth_type': ('Attribute Value', 'FT_UINT32', None),
'nl80211_key_type': ('Attribute Value', 'FT_UINT32', None),
'nl80211_bss_status': ('Attribute Value', 'FT_UINT32', None),
'nl80211_bss_scan_width': ('Attribute Value', 'FT_UINT32', None),
'nl80211_mfp': ('Attribute Value', 'FT_UINT32', None),
'nl80211_ps_state': ('Attribute Value', 'FT_UINT32', None),
'nl80211_tx_power_setting': ('Attribute Value', 'FT_UINT32', None),
'nl80211_plink_state': ('Attribute Value', 'FT_UINT8', None),
'nl80211_tdls_operation': ('Attribute Value', 'FT_UINT8', None),
'nl80211_user_reg_hint_type': ('Attribute Value', 'FT_UINT32', None),
'nl80211_connect_failed_reason': ('Attribute Value', 'FT_UINT32', None),
'nl80211_mesh_power_mode': ('Attribute Value', 'FT_UINT32', None),
'nl80211_acl_policy': ('Attribute Value', 'FT_UINT32', None),
'nl80211_radar_event': ('Attribute Value', 'FT_UINT32', None),
'nl80211_crit_proto_id': ('Attribute Value', 'FT_UINT16', None),
'nl80211_smps_mode': ('Attribute Value', 'FT_UINT8', None),
'nl80211_sta_p2p_ps_status': ('Attribute Value', 'FT_UINT8', None),
'nl80211_timeout_reason': ('Attribute Value', 'FT_UINT32', None),
'nl80211_external_auth_action': ('Attribute Value', 'FT_UINT32', None),
'nl80211_dfs_regions': ('Attribute Value', 'FT_UINT8', None),
}
# File to be patched
SOURCE_FILE = "epan/dissectors/packet-netlink-nl80211.c"
# URL where the latest version can be found
URL = "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/nl80211.h"
def make_enum(name, values, indent):
def make_enum(name, values, expressions, indent):
code = 'enum ws_%s {\n' % name
for value in values:
code += '%sWS_%s,\n' % (indent, value)
for value, expression in zip(values, expressions):
if expression and 'NL80211' in expression:
expression = 'WS_%s' % expression
if expression:
code += '%sWS_%s = %s,\n' % (indent, value, expression)
else:
code += '%sWS_%s,\n' % (indent, value)
code += '};\n'
return code
def make_value_string(name, values, indent):
def make_value_string(name, values, indent,):
code = 'static const value_string ws_%s_vals[] = {\n' % name
align = 40
for value in values:
@ -50,56 +121,94 @@ def make_value_string(name, values, indent):
code += ' VALUE_STRING_EXT_INIT(ws_%s_vals);\n' % name
return code
def make_hfi(name, indent):
(field_name, field_type, field_blurb) = EXPORT_ENUMS.get(name)
field_abbrev = name
# Fill in default values
if not field_name:
field_name = 'Attribute Type'
if not field_type:
field_type = 'FT_UINT16'
if not field_blurb:
field_blurb = 'NULL'
# Special treatment of already existing field names
rename_fields = {
'nl80211_attrs': 'nl80211_attr_type',
'nl80211_commands': 'nl80211_cmd'
}
if rename_fields.get(name):
field_abbrev = rename_fields[name]
field_abbrev = field_abbrev.lstrip('nl80211_')
code = 'static header_field_info hfi_%s NETLINK_NL80211_HFI_INIT =\n' % name
code += indent + '{ "%s", "nl80211.%s", %s, BASE_DEC | BASE_EXT_STRING,\n' % \
(field_name, field_abbrev, field_type)
code += indent + ' VALS(&ws_%s_vals_ext), 0x00, %s, HFILL };\n' % (name, field_blurb)
return code
def make_ett_defs(name, indent):
code = 'static gint ett_%s = -1;' % name
return code
def make_hfi_init(name, indent):
code = indent + indent + '&hfi_%s,' % name
return code
def make_ett(name, indent):
code = indent + indent + '&ett_%s,' % name
return code
class EnumStore(object):
def __init__(self, name):
__RE_ENUM_VALUE = re.compile(
r'\s+?(?P<value>\w+)(?:\ /\*.*?\*\/)?(?:\s*=\s*(?P<expression>.*?))?(?:\s*,|$)',
re.MULTILINE | re.DOTALL)
def __init__(self, name, values):
self.name = name
self.values = []
self.expressions = []
self.active = True
self.parse_values(values)
def update(self, line):
if not self.active:
return
# Skip comments and remove trailing comma
line = re.sub(r'\s*/\*.*?\*/\s*', '', line).rstrip(",")
if not line:
return
# Try to match a name. Allow aliases only for the previous item.
m = re.match(r'^(?P<name>\w+)(?: *= *(?P<alias_of>\w+))?$', line)
assert m, "Failed to find match in %r" % line
name, alias_of = m.groups()
if alias_of:
# Alias must match previous item, skip it otherwise.
assert alias_of == self.values[-1]
elif name.startswith("__"):
# Skip after hitting "__NL80211_CMD_AFTER_LAST"
self.active = False
else:
self.values.append(name)
def parse_values(self, values):
for m in self.__RE_ENUM_VALUE.finditer(values):
value, expression = m.groups()
if value.startswith('NUM_'):
break
if value.endswith('_AFTER_LAST'):
break
if value.endswith('_LAST'):
break
if value.startswith('__') and value.endswith('_NUM'):
break
if expression and expression in self.values:
# Skip aliases
continue
self.values.append(value)
self.expressions.append(expression)
def finish(self):
assert not self.active
assert self.values
return self.name, self.values
return self.name, self.values, self.expressions
RE_ENUM = re.compile(
r'enum\s+?(?P<enum>\w+)\s+?\{(?P<values>.*?)\}\;',
re.MULTILINE | re.DOTALL)
RE_COMMENT = re.compile(r'/\*.*?\*/', re.MULTILINE | re.DOTALL)
def parse_header(content):
# Strip comments
content = re.sub(RE_COMMENT, '', content)
def parse_header(f):
enum_store = None
enums = []
for line in f:
line = line.strip()
if line.startswith("enum "):
assert not enum_store
enum_keyword, enum_name, trailer = line.split()
assert trailer == "{"
if enum_name in EXPORT_ENUMS:
enum_store = EnumStore(enum_name)
elif enum_store:
if line == "};":
enums.append(enum_store.finish())
enum_store = None
elif line:
enum_store.update(line)
for m in RE_ENUM.finditer(content):
enum = m.group('enum')
values = m.group('values')
if enum in EXPORT_ENUMS:
enums.append(EnumStore(enum, values).finish())
return enums
def parse_source():
@ -108,6 +217,7 @@ def parse_source():
after the block.
"""
begin, block, end = '', '', ''
parts = []
# Stages: 1 (before block), 2 (in block, skip), 3 (after block)
stage = 1
with open(SOURCE_FILE) as f:
@ -116,15 +226,21 @@ def parse_source():
stage = 3 # End of block
if stage == 1:
begin += line
if line == HEADER:
stage = 2 # Begin of block
elif stage == 2:
block += line
elif stage == 3:
end += line
if stage != 3:
raise RuntimeError("Could not parse file (in stage %d)" % stage)
return begin, block, end
if line == HEADER and stage == 1:
stage = 2 # Begin of block
if line == HEADER and stage == 3:
stage = 2 # Begin of next code block
parts.append((begin, block, end))
begin, block, end = '', '', ''
parts.append((begin, block, end))
if stage != 3 or len(parts) != 3:
raise RuntimeError("Could not parse file (in stage %d) (parts %d)" % (stage, len(parts)))
return parts
parser = argparse.ArgumentParser()
parser.add_argument("--update", action="store_true",
@ -143,37 +259,53 @@ def main():
if any(args.header_file.startswith(proto) for proto in ('http:', 'https')):
r = requests.get(args.header_file)
r.raise_for_status()
enums = parse_header(r.text.splitlines())
enums = parse_header(r.text)
elif args.header_file == "-":
enums = parse_header(sys.stdin)
enums = parse_header(sys.stdin.read())
else:
with open(args.header_file) as f:
enums = parse_header(f)
enums = parse_header(f.read())
assert len(enums) == len(EXPORT_ENUMS), \
"Could not parse data, found %d/%d results" % \
(len(enums), len(EXPORT_ENUMS))
code_enums, code_vals = '', ''
for enum_name, enum_values in enums:
code_enums += make_enum(enum_name, enum_values, indent) + '\n'
code_enums, code_vals, code_hfi, code_ett_defs, code_hfi_init, code_ett = '', '', '', '', '', ''
for enum_name, enum_values, expressions in enums:
code_enums += make_enum(enum_name, enum_values, expressions, indent) + '\n'
code_vals += make_value_string(enum_name, enum_values, indent) + '\n'
code_hfi += make_hfi(enum_name, indent) + '\n'
code_ett_defs += make_ett_defs(enum_name, indent) + '\n'
code_hfi_init += make_hfi_init(enum_name, indent) + '\n'
code_ett += make_ett(enum_name, indent) + '\n'
code = code_enums + code_vals
code = code.rstrip("\n") + "\n"
code_top = code_enums + code_vals + code_hfi + code_ett_defs
code_top = code_top.rstrip("\n") + "\n"
code = [code_top, code_hfi_init, code_ett]
update = False
if args.update:
begin, block, end = parse_source()
if block == code:
parts = parse_source()
# Check if file needs update
for (begin, old_code, end), new_code in zip(parts, code):
if old_code != new_code:
update = True
break
if not update:
print("File is up-to-date")
else:
with open(SOURCE_FILE, "w") as f:
return
# Update file
with open(SOURCE_FILE, "w") as f:
for (begin, old_code, end), new_code in zip(parts, code):
f.write(begin)
f.write(code)
f.write(new_code)
f.write(end)
print("Updated %s" % SOURCE_FILE)
print("Updated %s" % SOURCE_FILE)
else:
print(code)
for new_code in code:
print(new_code)
if __name__ == '__main__':
main()