diff --git a/epan/dissectors/packet-netlink-nl80211.c b/epan/dissectors/packet-netlink-nl80211.c index 76c1740494..435fbe4766 100644 --- a/epan/dissectors/packet-netlink-nl80211.c +++ b/epan/dissectors/packet-netlink-nl80211.c @@ -2,6 +2,7 @@ * Dissector for nl80211 (over Netlink). * * Copyright (c) 2017, Peter Wu + * Copyright (c) 2018, Mikael Kanstrup * * Wireshark - Network traffic analyzer * By Gerald Combs @@ -15,12 +16,15 @@ #include "config.h" #include +#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"); diff --git a/tools/generate-nl80211-fields.py b/tools/generate-nl80211-fields.py index 4254eefd3f..80dece2da1 100755 --- a/tools/generate-nl80211-fields.py +++ b/tools/generate-nl80211-fields.py @@ -3,6 +3,7 @@ # (value_string) for packet-netlink-nl80211.c # # Copyright (c) 2017, Peter Wu +# Copyright (c) 2018, Mikael Kanstrup # # Wireshark - Network traffic analyzer # By Gerald Combs @@ -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\w+)(?:\ /\*.*?\*\/)?(?:\s*=\s*(?P.*?))?(?:\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\w+)(?: *= *(?P\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\w+)\s+?\{(?P.*?)\}\;', + 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()