diff --git a/.gitignore b/.gitignore index 1c93d7f9..4ccf0c54 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ autom4te.cache ell src/iwd client/iwctl +monitor/iwmon unit/test-pbkdf2-sha1 unit/test-prf-sha1 unit/*.log diff --git a/Makefile.am b/Makefile.am index fa20c594..f8c5bac6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,7 +34,7 @@ ell_sources = ell/ell.h ell/private.h \ ell_libell_internal_la_SOURCES = $(ell_sources) -bin_PROGRAMS = src/iwd client/iwctl +bin_PROGRAMS = src/iwd client/iwctl monitor/iwmon src_iwd_SOURCES = src/main.c linux/nl80211.h linux/kdbus.h \ src/kdbus.h src/kdbus.c \ @@ -48,6 +48,10 @@ client_iwctl_SOURCES = client/main.c linux/kdbus.h \ src/kdbus.h src/kdbus.c client_iwctl_LDADD = ell/libell-internal.la +monitor_iwmon_SOURCES = monitor/main.c linux/nl80211.h \ + monitor/nlmon.h monitor/nlmon.c +monitor_iwmon_LDADD = ell/libell-internal.la + noinst_PROGRAMS = unit_tests = unit/test-pbkdf2-sha1 unit/test-prf-sha1 unit/test-ie diff --git a/monitor/main.c b/monitor/main.c new file mode 100644 index 00000000..c48e5352 --- /dev/null +++ b/monitor/main.c @@ -0,0 +1,165 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "linux/nl80211.h" +#include "monitor/nlmon.h" + +static struct nlmon *nlmon = NULL; + +#define NLA_OK(nla,len) ((len) >= (int) sizeof(struct nlattr) && \ + (nla)->nla_len >= sizeof(struct nlattr) && \ + (nla)->nla_len <= (len)) +#define NLA_NEXT(nla,attrlen) ((attrlen) -= NLA_ALIGN((nla)->nla_len), \ + (struct nlattr*)(((char*)(nla)) + \ + NLA_ALIGN((nla)->nla_len))) + +#define NLA_LENGTH(len) (NLA_ALIGN(sizeof(struct nlattr)) + (len)) +#define NLA_DATA(nla) ((void*)(((char*)(nla)) + NLA_LENGTH(0))) +#define NLA_PAYLOAD(nla) ((int)((nla)->nla_len - NLA_LENGTH(0))) + +static void genl_parse(uint16_t type, const void *data, uint32_t len) +{ + const struct genlmsghdr *genlmsg = data; + const struct nlattr *nla; + char name[GENL_NAMSIZ]; + uint16_t id = GENL_ID_GENERATE; + + if (nlmon) + return; + + if (type != GENL_ID_CTRL) + return; + + if (genlmsg->cmd != CTRL_CMD_NEWFAMILY) + return; + + for (nla = data + GENL_HDRLEN; NLA_OK(nla, len); + nla = NLA_NEXT(nla, len)) { + switch (nla->nla_type & NLA_TYPE_MASK) { + case CTRL_ATTR_FAMILY_ID: + id = *((uint16_t *) NLA_DATA(nla)); + break; + case CTRL_ATTR_FAMILY_NAME: + strncpy(name, NLA_DATA(nla), GENL_NAMSIZ); + break; + } + } + + if (id == GENL_ID_GENERATE) + return; + + if (!strcmp(name, NL80211_GENL_NAME)) + nlmon = nlmon_open(id); +} + +static void genl_notify(uint16_t type, const void *data, + uint32_t len, void *user_data) +{ + genl_parse(type, data, len); +} + +static void genl_callback(int error, uint16_t type, const void *data, + uint32_t len, void *user_data) +{ + if (error < 0) { + fprintf(stderr, "Failed to lookup nl80211 family\n"); + l_main_quit(); + return; + } + + genl_parse(type, data, len); +} + +static struct l_netlink *genl_lookup(void) +{ + struct l_netlink *genl; + char buf[GENL_HDRLEN + NLA_HDRLEN + GENL_NAMSIZ]; + struct genlmsghdr *genlmsg; + struct nlattr *nla; + + genl = l_netlink_new(NETLINK_GENERIC); + + l_netlink_register(genl, GENL_ID_CTRL, genl_notify, NULL, NULL); + + genlmsg = (struct genlmsghdr *) buf; + genlmsg->cmd = CTRL_CMD_GETFAMILY; + genlmsg->version = 0; + genlmsg->reserved = 0; + + nla = (struct nlattr *) (buf + GENL_HDRLEN); + nla->nla_len = NLA_HDRLEN + GENL_NAMSIZ; + nla->nla_type = CTRL_ATTR_FAMILY_NAME; + strncpy(buf + GENL_HDRLEN + NLA_HDRLEN, + NL80211_GENL_NAME, GENL_NAMSIZ); + + l_netlink_send(genl, GENL_ID_CTRL, 0, buf, sizeof(buf), + genl_callback, NULL, NULL); + + return genl; +} + +static void signal_handler(struct l_signal *signal, uint32_t signo, + void *user_data) +{ + switch (signo) { + case SIGINT: + case SIGTERM: + l_main_quit(); + break; + } +} + +int main(int argc, char *argv[]) +{ + struct l_signal *signal; + struct l_netlink *genl; + sigset_t mask; + int exit_status; + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + signal = l_signal_create(&mask, signal_handler, NULL, NULL); + + genl = genl_lookup(); + + l_main_run(); + + l_netlink_destroy(genl); + nlmon_close(nlmon); + + exit_status = EXIT_SUCCESS; + + l_signal_remove(signal); + + return exit_status; +} diff --git a/monitor/nlmon.c b/monitor/nlmon.c new file mode 100644 index 00000000..c4cace86 --- /dev/null +++ b/monitor/nlmon.c @@ -0,0 +1,951 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "linux/nl80211.h" +#include "monitor/nlmon.h" + +struct nlmon { + uint16_t id; + struct l_io *io; + struct l_queue *req_list; +}; + +struct nlmon_req { + uint32_t seq; + uint32_t pid; + uint16_t flags; +}; + +static void nlmon_req_free(void *data) +{ + struct nlmon_req *req = data; + + l_free(req); +} + +enum attr_type { + ATTR_UNSPEC, + ATTR_FLAG, + ATTR_U8, + ATTR_U16, + ATTR_U32, + ATTR_U64, + ATTR_STRING, + ATTR_ADDRESS, + ATTR_BINARY, +}; + +struct attr_entry { + uint16_t attr; + const char *str; + enum attr_type type; +}; + +static const struct attr_entry attr_table[] = { + { NL80211_ATTR_WIPHY, + "Wiphy", ATTR_U32 }, + { NL80211_ATTR_WIPHY_NAME, + "Wiphy Name", ATTR_STRING }, + { NL80211_ATTR_IFINDEX, + "Interface Index", ATTR_U32 }, + { NL80211_ATTR_IFNAME, + "Interface Name", ATTR_STRING }, + { NL80211_ATTR_IFTYPE, + "Interface Type", ATTR_U32 }, + { NL80211_ATTR_MAC, + "MAC Address", ATTR_ADDRESS }, + { NL80211_ATTR_KEY_DATA, + "Key Data", ATTR_BINARY }, + { NL80211_ATTR_KEY_IDX, + "Key Index", ATTR_U8 }, + { NL80211_ATTR_KEY_CIPHER, + "Key Cipher", ATTR_U32 }, + { NL80211_ATTR_KEY_SEQ, + "Key Sequence", ATTR_BINARY }, + { NL80211_ATTR_KEY_DEFAULT, + "Key Default", ATTR_FLAG }, + { NL80211_ATTR_BEACON_INTERVAL, + "Beacon Interval", ATTR_U32 }, + { NL80211_ATTR_DTIM_PERIOD, + "DTIM Period", ATTR_U32 }, + { NL80211_ATTR_BEACON_HEAD, + "Beacon Head", ATTR_BINARY }, + { NL80211_ATTR_BEACON_TAIL, + "Beacon Tail", ATTR_BINARY }, + { NL80211_ATTR_STA_AID, + "Station AID", ATTR_U16 }, + { NL80211_ATTR_STA_FLAGS, + "Station Flags" }, + { NL80211_ATTR_STA_LISTEN_INTERVAL, + "Station Listen Interval", ATTR_U16 }, + { NL80211_ATTR_STA_SUPPORTED_RATES, + "Station Supported Rates", ATTR_BINARY }, + { NL80211_ATTR_STA_VLAN, + "Station VLAN", ATTR_U32 }, + { NL80211_ATTR_STA_INFO, + "Station Info" }, + { NL80211_ATTR_WIPHY_BANDS, + "Wiphy Bands" }, + { NL80211_ATTR_MNTR_FLAGS, + "MNTR Flags" }, + { NL80211_ATTR_MESH_ID, + "Mesh ID", ATTR_BINARY }, + { NL80211_ATTR_STA_PLINK_ACTION, + "Station P-Link Action", ATTR_U8 }, + { NL80211_ATTR_MPATH_NEXT_HOP, + "Mesh Path Next Hop", ATTR_U32 }, + { NL80211_ATTR_MPATH_INFO, + "Mesh Path Info" }, + { NL80211_ATTR_BSS_CTS_PROT, + "BSS CTS Protection", ATTR_U8 }, + { NL80211_ATTR_BSS_SHORT_PREAMBLE, + "BSS Short Preamble", ATTR_U8 }, + { NL80211_ATTR_BSS_SHORT_SLOT_TIME, + "BSS Short Slot Time", ATTR_U8 }, + { NL80211_ATTR_HT_CAPABILITY, + "HT Capability" }, + { NL80211_ATTR_SUPPORTED_IFTYPES, + "Supported Interface Types" }, + { NL80211_ATTR_REG_ALPHA2, + "Regulatory Alpha2", ATTR_STRING }, + { NL80211_ATTR_REG_RULES, + "Regulatory Rules" }, + { NL80211_ATTR_MESH_CONFIG, + "Mesh Configuration" }, + { NL80211_ATTR_BSS_BASIC_RATES, + "BSS Basic Rates", ATTR_BINARY }, + { NL80211_ATTR_WIPHY_TXQ_PARAMS, + "Wiphy TXQ Parameters" }, + { NL80211_ATTR_WIPHY_FREQ, + "Wiphy Frequency", ATTR_U32 }, + { NL80211_ATTR_WIPHY_CHANNEL_TYPE, + "Wiphy Channel Type", ATTR_U32 }, + { NL80211_ATTR_KEY_DEFAULT_MGMT, + "Key Default Management", ATTR_FLAG }, + { NL80211_ATTR_MGMT_SUBTYPE, + "Management Subtype", ATTR_U8 }, + { NL80211_ATTR_IE, + "Information Elements", ATTR_BINARY }, + { NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + "Max Number Scan SSIDs", ATTR_U8 }, + { NL80211_ATTR_SCAN_FREQUENCIES, + "Scan Frequencies" }, + { NL80211_ATTR_SCAN_SSIDS, + "Scan SSIDs" }, + { NL80211_ATTR_GENERATION, + "Generation", ATTR_U32 }, + { NL80211_ATTR_BSS, + "BSS" }, + { NL80211_ATTR_REG_INITIATOR, + "Regulatory Initiator" }, + { NL80211_ATTR_REG_TYPE, + "Regulatory Type" }, + { NL80211_ATTR_SUPPORTED_COMMANDS, + "Supported Commands" }, + { NL80211_ATTR_FRAME, + "Frame", ATTR_BINARY }, + { NL80211_ATTR_SSID, + "SSID", ATTR_BINARY }, + { NL80211_ATTR_AUTH_TYPE, + "Auth Type", ATTR_U32 }, + { NL80211_ATTR_REASON_CODE, + "Reason Code", ATTR_U16 }, + { NL80211_ATTR_KEY_TYPE, + "Key Type", ATTR_U32 }, + { NL80211_ATTR_MAX_SCAN_IE_LEN, + "Max Scan IE Length", ATTR_U16 }, + { NL80211_ATTR_CIPHER_SUITES, + "Cipher Suites" }, + { NL80211_ATTR_FREQ_BEFORE, + "Frequency Before" }, + { NL80211_ATTR_FREQ_AFTER, + "Frequency After" }, + { NL80211_ATTR_FREQ_FIXED, + "Frequency Fixed", ATTR_FLAG }, + { NL80211_ATTR_WIPHY_RETRY_SHORT, + "Wiphy Retry Short", ATTR_U8 }, + { NL80211_ATTR_WIPHY_RETRY_LONG, + "Wiphy Retry Long", ATTR_U8 }, + { NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + "Wiphy Frag Threshold", ATTR_U32 }, + { NL80211_ATTR_WIPHY_RTS_THRESHOLD, + "Wiphy RTS Threshold", ATTR_U32 }, + { NL80211_ATTR_TIMED_OUT, + "Timed Out", ATTR_FLAG }, + { NL80211_ATTR_USE_MFP, + "Use MFP", ATTR_U32 }, + { NL80211_ATTR_STA_FLAGS2, + "Station Flags 2" }, + { NL80211_ATTR_CONTROL_PORT, + "Control Port", ATTR_FLAG }, + { NL80211_ATTR_TESTDATA, + "Test Data" }, + { NL80211_ATTR_PRIVACY, + "Privacy", ATTR_FLAG }, + { NL80211_ATTR_DISCONNECTED_BY_AP, + "Disconnect by AP", ATTR_FLAG }, + { NL80211_ATTR_STATUS_CODE, + "Status Code", ATTR_U16 }, + { NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + "Cipher Suites Pairwise" }, + { NL80211_ATTR_CIPHER_SUITE_GROUP, + "Cipher Suite Group", ATTR_U32 }, + { NL80211_ATTR_WPA_VERSIONS, + "WPA Versions", ATTR_U32 }, + { NL80211_ATTR_AKM_SUITES, + "AKM Suites" }, + { NL80211_ATTR_REQ_IE, + "Request IE" }, + { NL80211_ATTR_RESP_IE, + "Response IE" }, + { NL80211_ATTR_PREV_BSSID, + "Previous BSSID", ATTR_ADDRESS }, + { NL80211_ATTR_KEY, + "Key" }, + { NL80211_ATTR_KEYS, + "Keys" }, + { NL80211_ATTR_PID, + "PID", ATTR_U32 }, + { NL80211_ATTR_4ADDR, + "4-Address", ATTR_U8 }, + { NL80211_ATTR_SURVEY_INFO, + "Survey Info" }, + { NL80211_ATTR_PMKID, + "PMKID", ATTR_BINARY }, + { NL80211_ATTR_MAX_NUM_PMKIDS, + "Max Number PMKIDs", ATTR_U8 }, + { NL80211_ATTR_DURATION, + "Duration", ATTR_U32 }, + { NL80211_ATTR_COOKIE, + "Cookie", ATTR_U64 }, + { NL80211_ATTR_WIPHY_COVERAGE_CLASS, + "Wiphy Coverage Class", ATTR_U8 }, + { NL80211_ATTR_TX_RATES, + "TX Rates" }, + { NL80211_ATTR_FRAME_MATCH, + "Frame Match", ATTR_BINARY }, + { NL80211_ATTR_ACK, + "ACK", ATTR_FLAG }, + { NL80211_ATTR_PS_STATE, + "PS State", ATTR_U32 }, + { NL80211_ATTR_CQM, + "CQM" }, + { NL80211_ATTR_LOCAL_STATE_CHANGE, + "Local State Change", ATTR_FLAG }, + { NL80211_ATTR_AP_ISOLATE, + "AP Isolate", ATTR_U8 }, + { NL80211_ATTR_WIPHY_TX_POWER_SETTING, + "Wiphy TX Power Setting", ATTR_U32 }, + { NL80211_ATTR_WIPHY_TX_POWER_LEVEL, + "Wiphy TX Power Level", ATTR_U32 }, + { NL80211_ATTR_TX_FRAME_TYPES, + "TX Frame Types" }, + { NL80211_ATTR_RX_FRAME_TYPES, + "RX Frame Types" }, + { NL80211_ATTR_FRAME_TYPE, + "Frame Type", ATTR_U16 }, + { NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + "Control Port Ethertype", ATTR_U16 }, + { NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, + "Control Port No Encrypt", ATTR_FLAG }, + { NL80211_ATTR_SUPPORT_IBSS_RSN, + "Support IBSS RSN", ATTR_FLAG }, + { NL80211_ATTR_WIPHY_ANTENNA_TX, + "Wiphy Antenna TX", ATTR_U32 }, + { NL80211_ATTR_WIPHY_ANTENNA_RX, + "Wiphy Antenna RX", ATTR_U32 }, + { NL80211_ATTR_MCAST_RATE, + "Multicast Rate", ATTR_U32 }, + { NL80211_ATTR_OFFCHANNEL_TX_OK, + "Offchannel TX OK", ATTR_FLAG }, + { NL80211_ATTR_BSS_HT_OPMODE, + "BSS HT Operation Mode", ATTR_U16 }, + { NL80211_ATTR_KEY_DEFAULT_TYPES, + "Key Default Types" }, + { NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, + "Remain on Channel Duration " }, + { NL80211_ATTR_MESH_SETUP, + "Mesh Setup" }, + { NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, + "Wiphy Antenna Avail TX" }, + { NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, + "Wiphy Antenna Avail RX" }, + { NL80211_ATTR_SUPPORT_MESH_AUTH, + "Support Mesh Auth", ATTR_FLAG }, + { NL80211_ATTR_STA_PLINK_STATE, + "Station P-Link State" }, + { NL80211_ATTR_WOWLAN_TRIGGERS, + "WoWLAN Triggers" }, + { NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, + "WoWLAN Triggers Supported" }, + { NL80211_ATTR_SCHED_SCAN_INTERVAL, + "Scheduled Scan Interval", ATTR_U32 }, + { NL80211_ATTR_INTERFACE_COMBINATIONS, + "Interface Combinations" }, + { NL80211_ATTR_SOFTWARE_IFTYPES, + "Software Interface Types" }, + { NL80211_ATTR_REKEY_DATA, + "Rekey Data" }, + { NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, + "Max Num Sched Scan SSIDs", ATTR_U8 }, + { NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, + "Max Sched Scan IE Len", ATTR_U16 }, + { NL80211_ATTR_SCAN_SUPP_RATES, + "Scan Supported Rates" }, + { NL80211_ATTR_HIDDEN_SSID, + "Hidden SSID", ATTR_U32 }, + { NL80211_ATTR_IE_PROBE_RESP, + "IE Probe Response" }, + { NL80211_ATTR_IE_ASSOC_RESP, + "IE Assoc Response" }, + { NL80211_ATTR_STA_WME, + "Station WME" }, + { NL80211_ATTR_SUPPORT_AP_UAPSD, + "Support AP UAPSD", ATTR_FLAG }, + { NL80211_ATTR_ROAM_SUPPORT, + "Roaming Support", ATTR_FLAG }, + { NL80211_ATTR_SCHED_SCAN_MATCH, + "Scheduled Scan Match" }, + { NL80211_ATTR_MAX_MATCH_SETS, + "Max Match Sets", ATTR_U8 }, + { NL80211_ATTR_PMKSA_CANDIDATE, + "PMKSA Candidate" }, + { NL80211_ATTR_TX_NO_CCK_RATE, + "TX No CCK Rate", ATTR_FLAG }, + { NL80211_ATTR_TDLS_ACTION, + "TDLS Action", ATTR_U8 }, + { NL80211_ATTR_TDLS_DIALOG_TOKEN, + "TDLS Dialog Token", ATTR_U8 }, + { NL80211_ATTR_TDLS_OPERATION, + "TDLS Operation", ATTR_U8 }, + { NL80211_ATTR_TDLS_SUPPORT, + "TDLS Support", ATTR_FLAG }, + { NL80211_ATTR_TDLS_EXTERNAL_SETUP, + "TDLS External Setup", ATTR_FLAG }, + { NL80211_ATTR_DEVICE_AP_SME, + "Device AP SME" }, + { NL80211_ATTR_DONT_WAIT_FOR_ACK, + "Don't Wait for Ack", ATTR_FLAG }, + { NL80211_ATTR_FEATURE_FLAGS, + "Feature Flags" }, + { NL80211_ATTR_PROBE_RESP_OFFLOAD, + "Probe Response Offload" }, + { NL80211_ATTR_PROBE_RESP, + "Probe Response" }, + { NL80211_ATTR_DFS_REGION, + "DFS Region", ATTR_U8 }, + { NL80211_ATTR_DISABLE_HT, + "Diable HT", ATTR_FLAG }, + { NL80211_ATTR_HT_CAPABILITY_MASK, + "HT Capability Mask" }, + { NL80211_ATTR_NOACK_MAP, + "No-Ack Map", ATTR_U16 }, + { NL80211_ATTR_INACTIVITY_TIMEOUT, + "Inactivity Timeout", ATTR_U16 }, + { NL80211_ATTR_RX_SIGNAL_DBM, + "RX Signal dBm", ATTR_U32 }, + { NL80211_ATTR_BG_SCAN_PERIOD, + "Background Scan Period", ATTR_U16 }, + { NL80211_ATTR_WDEV, + "Wireless Device", ATTR_U64 }, + { NL80211_ATTR_USER_REG_HINT_TYPE, + "User Regulatroy Hint Type", ATTR_U32 }, + { NL80211_ATTR_CONN_FAILED_REASON, + "Connection Failed Reason" }, + { NL80211_ATTR_SAE_DATA, + "SAE Data" }, + { NL80211_ATTR_VHT_CAPABILITY, + "VHT Capability" }, + { NL80211_ATTR_SCAN_FLAGS, + "Scan Flags", ATTR_U32 }, + { NL80211_ATTR_CHANNEL_WIDTH, + "Channel Width", ATTR_U32 }, + { NL80211_ATTR_CENTER_FREQ1, + "Center Frequency 1", ATTR_U32 }, + { NL80211_ATTR_CENTER_FREQ2, + "Center Frequency 2", ATTR_U32 }, + { NL80211_ATTR_P2P_CTWINDOW, + "P2P CT Window", ATTR_U8 }, + { NL80211_ATTR_P2P_OPPPS, + "P2P OP PPS", ATTR_U8 }, + { NL80211_ATTR_LOCAL_MESH_POWER_MODE, + "Local Mesh Power Mode" }, + { NL80211_ATTR_ACL_POLICY, + "ACL Policy", ATTR_U32 }, + { NL80211_ATTR_MAC_ADDRS, + "MAC Addresses" }, + { NL80211_ATTR_MAC_ACL_MAX, + "MAC ACL Max" }, + { NL80211_ATTR_RADAR_EVENT, + "Radar Event" }, + { NL80211_ATTR_EXT_CAPA, + "Extended Capabilities" }, + { NL80211_ATTR_EXT_CAPA_MASK, + "Extended Capabilities Mask" }, + { NL80211_ATTR_STA_CAPABILITY, + "Station Capability", ATTR_U16 }, + { NL80211_ATTR_STA_EXT_CAPABILITY, + "Station Extended Capability" }, + { NL80211_ATTR_PROTOCOL_FEATURES, + "Protocol Features", ATTR_U32 }, + { NL80211_ATTR_SPLIT_WIPHY_DUMP, + "Split Wiphy Dump", ATTR_FLAG}, + { NL80211_ATTR_DISABLE_VHT, + "Disable VHT" }, + { NL80211_ATTR_VHT_CAPABILITY_MASK, + "VHT Capability Mask" }, + { NL80211_ATTR_MDID, + "MDID", ATTR_U16 }, + { NL80211_ATTR_IE_RIC, + "IE RIC", ATTR_BINARY }, + { NL80211_ATTR_CRIT_PROT_ID, + "Critical Protocol ID" }, + { NL80211_ATTR_MAX_CRIT_PROT_DURATION, + "Max Criticial Protocol Duration" }, + { NL80211_ATTR_PEER_AID, + "Peer AID", ATTR_U16 }, + { NL80211_ATTR_COALESCE_RULE, + "Coalesce Rule" }, + { NL80211_ATTR_CH_SWITCH_COUNT, + "Channel Switch Count", ATTR_U32 }, + { NL80211_ATTR_CH_SWITCH_BLOCK_TX, + "Channel Switch Block TX", ATTR_FLAG }, + { NL80211_ATTR_CSA_IES, + "CSA IEs" }, + { NL80211_ATTR_CSA_C_OFF_BEACON, + "CSA C Off Beacon" }, + { NL80211_ATTR_CSA_C_OFF_PRESP, + "CSA C Off Response" }, + { NL80211_ATTR_RXMGMT_FLAGS, + "RX Management Flags", ATTR_U32 }, + { NL80211_ATTR_STA_SUPPORTED_CHANNELS, + "Station Supported Channels" }, + { NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, + "Station Supported Operation Classes" }, + { NL80211_ATTR_HANDLE_DFS, + "Handle DFS", ATTR_FLAG }, + { NL80211_ATTR_SUPPORT_5_MHZ, + "Support 5 MHz" }, + { NL80211_ATTR_SUPPORT_10_MHZ, + "Support 10 MHz" }, + { NL80211_ATTR_OPMODE_NOTIF, + "Operation Mode Notification", ATTR_U8 }, + { NL80211_ATTR_VENDOR_ID, + "Vendor ID", ATTR_U32 }, + { NL80211_ATTR_VENDOR_SUBCMD, + "Vendor Subcommand", ATTR_U32 }, + { NL80211_ATTR_VENDOR_DATA, + "Vendor Data", ATTR_BINARY }, + { NL80211_ATTR_VENDOR_EVENTS, + "Vendor Events" }, + { NL80211_ATTR_QOS_MAP, + "QoS Map", ATTR_BINARY }, + { NL80211_ATTR_MAC_HINT, + "MAC Hint", ATTR_ADDRESS }, + { NL80211_ATTR_WIPHY_FREQ_HINT, + "Wiphy Frequency Hint", ATTR_U32 }, + { NL80211_ATTR_MAX_AP_ASSOC_STA, + "Max AP Assoc Station" }, + { NL80211_ATTR_TDLS_PEER_CAPABILITY, + "TDLS Peer Capability", ATTR_U32 }, + { NL80211_ATTR_IFACE_SOCKET_OWNER, + "Interface Socket Owner", ATTR_FLAG }, + { NL80211_ATTR_CSA_C_OFFSETS_TX, + "CSA C Offsets TX" }, + { NL80211_ATTR_MAX_CSA_COUNTERS, + "Max CSA Counters" }, + { } +}; + +#define NLA_OK(nla,len) ((len) >= (int) sizeof(struct nlattr) && \ + (nla)->nla_len >= sizeof(struct nlattr) && \ + (nla)->nla_len <= (len)) +#define NLA_NEXT(nla,attrlen) ((attrlen) -= NLA_ALIGN((nla)->nla_len), \ + (struct nlattr*)(((char*)(nla)) + \ + NLA_ALIGN((nla)->nla_len))) + +#define NLA_LENGTH(len) (NLA_ALIGN(sizeof(struct nlattr)) + (len)) +#define NLA_DATA(nla) ((void*)(((char*)(nla)) + NLA_LENGTH(0))) +#define NLA_PAYLOAD(nla) ((int)((nla)->nla_len - NLA_LENGTH(0))) + +static void print_attributes(int indent, const void *buf, uint32_t len) +{ + const struct nlattr *nla; + const char *str; + int i; + + for (nla = buf ; NLA_OK(nla, len); nla = NLA_NEXT(nla, len)) { + uint16_t nla_type = nla->nla_type & NLA_TYPE_MASK; + enum attr_type type; + uint64_t val64; + uint32_t val32; + uint16_t val16; + uint8_t val8; + + str = "Reserved"; + type = ATTR_UNSPEC; + + for (i = 0; attr_table[i].str; i++) { + if (nla_type == attr_table[i].attr) { + str = attr_table[i].str; + type = attr_table[i].type; + break; + } + } + + switch (type) { + case ATTR_UNSPEC: + printf("%*c%s: len %u\n", indent, ' ', str, + NLA_PAYLOAD(nla)); + break; + case ATTR_FLAG: + printf("%*c%s: true\n", indent, ' ', str); + if (NLA_PAYLOAD(nla) != 0) + printf("malformed packet\n"); + break; + case ATTR_U8: + val8 = *((uint32_t *) NLA_DATA(nla)); + printf("%*c%s: %u (0x%02x)\n", indent, ' ', str, + val8, val8); + if (NLA_PAYLOAD(nla) != 1) + printf("malformed packet\n"); + break; + case ATTR_U16: + val16 = *((uint32_t *) NLA_DATA(nla)); + printf("%*c%s: %u (0x%04x)\n", indent, ' ', str, + val16, val16); + if (NLA_PAYLOAD(nla) != 2) + printf("malformed packet\n"); + break; + case ATTR_U32: + val32 = *((uint32_t *) NLA_DATA(nla)); + printf("%*c%s: %u (0x%08x)\n", indent, ' ', str, + val32, val32); + if (NLA_PAYLOAD(nla) != 4) + printf("malformed packet\n"); + break; + case ATTR_U64: + val64 = *((uint64_t *) NLA_DATA(nla)); + printf("%*c%s: %lu (0x%016lx)\n", indent, ' ', str, + val64, val64); + if (NLA_PAYLOAD(nla) != 8) + printf("malformed packet\n"); + break; + case ATTR_STRING: + printf("%*c%s: %s\n", indent, ' ', str, + (char *) NLA_DATA(nla)); + break; + case ATTR_ADDRESS: + printf("%*c%s: len %u\n", indent, ' ', str, + NLA_PAYLOAD(nla)); + if (NLA_PAYLOAD(nla) != 6) + printf("malformed packet\n"); + break; + case ATTR_BINARY: + printf("%*c%s: len %u\n", indent, ' ', str, + NLA_PAYLOAD(nla)); + break; + } + + if (nla->nla_type & NLA_F_NESTED) + print_attributes(indent + 2, NLA_DATA(nla), + NLA_PAYLOAD(nla)); + } +} + +static const struct { + uint8_t cmd; + const char *str; +} cmd_table[] = { + { NL80211_CMD_GET_WIPHY, "Get Wiphy" }, + { NL80211_CMD_SET_WIPHY, "Set Wiphy" }, + { NL80211_CMD_NEW_WIPHY, "New Wiphy" }, + { NL80211_CMD_DEL_WIPHY, "Del Wiphy" }, + { NL80211_CMD_GET_INTERFACE, "Get Interface" }, + { NL80211_CMD_SET_INTERFACE, "Set Interface" }, + { NL80211_CMD_NEW_INTERFACE, "New Interface" }, + { NL80211_CMD_DEL_INTERFACE, "Del Interface" }, + { NL80211_CMD_GET_KEY, "Get Key" }, + { NL80211_CMD_SET_KEY, "Set Key" }, + { NL80211_CMD_NEW_KEY, "New Key" }, + { NL80211_CMD_DEL_KEY, "Del Key" }, + { NL80211_CMD_GET_BEACON, "Get Beacon" }, + { NL80211_CMD_SET_BEACON, "Set Beacon" }, + { NL80211_CMD_START_AP, "Start AP" }, + { NL80211_CMD_STOP_AP, "Stop AP" }, + { NL80211_CMD_GET_STATION, "Get Station" }, + { NL80211_CMD_SET_STATION, "Set Station" }, + { NL80211_CMD_NEW_STATION, "New Station" }, + { NL80211_CMD_DEL_STATION, "Del Station" }, + { NL80211_CMD_GET_MPATH, "Get Mesh Path" }, + { NL80211_CMD_SET_MPATH, "Set Mesh Path" }, + { NL80211_CMD_NEW_MPATH, "New Mesh Path" }, + { NL80211_CMD_DEL_MPATH, "Del Mesh Path" }, + { NL80211_CMD_SET_BSS, "Set BSS" }, + { NL80211_CMD_SET_REG, "Set Reg" }, + { NL80211_CMD_REQ_SET_REG, "Req Set Reg" }, + { NL80211_CMD_GET_MESH_CONFIG, "Get Mesh Config" }, + { NL80211_CMD_SET_MESH_CONFIG, "Set Mesh Config" }, + { NL80211_CMD_SET_MGMT_EXTRA_IE, "Mgmt Extra IE" }, + { NL80211_CMD_GET_REG, "Get Reg" }, + { NL80211_CMD_GET_SCAN, "Get Scan" }, + { NL80211_CMD_TRIGGER_SCAN, "Trigger Scan" }, + { NL80211_CMD_NEW_SCAN_RESULTS, "New Scan Results" }, + { NL80211_CMD_SCAN_ABORTED, "Scan Aborted" }, + { NL80211_CMD_REG_CHANGE, "Reg Change" }, + { NL80211_CMD_AUTHENTICATE, "Authenticate" }, + { NL80211_CMD_ASSOCIATE, "Associate" }, + { NL80211_CMD_DEAUTHENTICATE, "Deauthenticate" }, + { NL80211_CMD_DISASSOCIATE, "Disassociate" }, + { NL80211_CMD_MICHAEL_MIC_FAILURE, "Michael MIC Failure" }, + { NL80211_CMD_REG_BEACON_HINT, "Reg Beacon Hint" }, + { NL80211_CMD_JOIN_IBSS, "Join IBSS" }, + { NL80211_CMD_LEAVE_IBSS, "Leave IBSS" }, + { NL80211_CMD_TESTMODE, "Test Mode" }, + { NL80211_CMD_CONNECT, "Connect" }, + { NL80211_CMD_ROAM, "Roam" }, + { NL80211_CMD_DISCONNECT, "Disconnect" }, + { NL80211_CMD_SET_WIPHY_NETNS, "Set Wiphy Netns" }, + { NL80211_CMD_GET_SURVEY, "Get Survey" }, + { NL80211_CMD_NEW_SURVEY_RESULTS, "New Survey Results" }, + { NL80211_CMD_SET_PMKSA, "Set PMKSA" }, + { NL80211_CMD_DEL_PMKSA, "Del PMKSA" }, + { NL80211_CMD_FLUSH_PMKSA, "Flush PMKSA" }, + { NL80211_CMD_REMAIN_ON_CHANNEL, "Remain on Channel" }, + { NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, "Cancel Remain on Channel"}, + { NL80211_CMD_SET_TX_BITRATE_MASK, "Set TX Bitrate Mask" }, + { NL80211_CMD_REGISTER_FRAME, "Register Frame" }, + { NL80211_CMD_FRAME, "Frame" }, + { NL80211_CMD_FRAME_TX_STATUS, "Frame TX Status" }, + { NL80211_CMD_SET_POWER_SAVE, "Set Power Save" }, + { NL80211_CMD_GET_POWER_SAVE, "Get Power Save" }, + { NL80211_CMD_SET_CQM, "Set CQM" }, + { NL80211_CMD_NOTIFY_CQM, "Notify CQM" }, + { NL80211_CMD_SET_CHANNEL, "Set Channel" }, + { NL80211_CMD_SET_WDS_PEER, "Set WDS Peer" }, + { NL80211_CMD_FRAME_WAIT_CANCEL, "Frame Wait Cancel" }, + { NL80211_CMD_JOIN_MESH, "Join Mesh" }, + { NL80211_CMD_LEAVE_MESH, "Leave Mesh" }, + { NL80211_CMD_UNPROT_DEAUTHENTICATE, "Unprot Deauthenticate" }, + { NL80211_CMD_UNPROT_DISASSOCIATE, "Unprot Disassociate" }, + { NL80211_CMD_NEW_PEER_CANDIDATE, "New Peer Candidate" }, + { NL80211_CMD_GET_WOWLAN, "Get WoWLAN" }, + { NL80211_CMD_SET_WOWLAN, "Set WoWLAN" }, + { NL80211_CMD_START_SCHED_SCAN, "Start Sched Scan" }, + { NL80211_CMD_STOP_SCHED_SCAN, "Stop Sched Scan" }, + { NL80211_CMD_SCHED_SCAN_RESULTS, "Sched Scan Results" }, + { NL80211_CMD_SCHED_SCAN_STOPPED, "Sched Scan Stopped" }, + { NL80211_CMD_SET_REKEY_OFFLOAD, "Set Rekey Offload" }, + { NL80211_CMD_PMKSA_CANDIDATE, "PMKSA Candidate" }, + { NL80211_CMD_TDLS_OPER, "TDLS Oper" }, + { NL80211_CMD_TDLS_MGMT, "TDLS Mgmt" }, + { NL80211_CMD_UNEXPECTED_FRAME, "Unexpected Frame" }, + { NL80211_CMD_PROBE_CLIENT, "Probe Client" }, + { NL80211_CMD_REGISTER_BEACONS, "Register Beacons" }, + { NL80211_CMD_UNEXPECTED_4ADDR_FRAME, "Unexpected 4addr Frame"}, + { NL80211_CMD_SET_NOACK_MAP, "Set NoAck Map" }, + { NL80211_CMD_CH_SWITCH_NOTIFY, "Channel Switch Notify" }, + { NL80211_CMD_START_P2P_DEVICE, "Start P2P Device" }, + { NL80211_CMD_STOP_P2P_DEVICE, "Stop P2P Device" }, + { NL80211_CMD_CONN_FAILED, "Conn Failed" }, + { NL80211_CMD_SET_MCAST_RATE, "Set Mcast Rate" }, + { NL80211_CMD_SET_MAC_ACL, "Set MAC ACL" }, + { NL80211_CMD_RADAR_DETECT, "Radar Detect" }, + { NL80211_CMD_GET_PROTOCOL_FEATURES, "Get Protocol Features" }, + { NL80211_CMD_UPDATE_FT_IES, "Update FT IEs" }, + { NL80211_CMD_FT_EVENT, "FT Event" }, + { NL80211_CMD_CRIT_PROTOCOL_START, "Crit Protocol Start" }, + { NL80211_CMD_CRIT_PROTOCOL_STOP, "Crit Protocol Stop" }, + { NL80211_CMD_GET_COALESCE, "Get Coalesce" }, + { NL80211_CMD_SET_COALESCE, "Set Coalesce" }, + { NL80211_CMD_CHANNEL_SWITCH, "Channel Switch" }, + { NL80211_CMD_VENDOR, "Vendor" }, + { NL80211_CMD_SET_QOS_MAP, "Set QoS Map" }, + { } +}; + +static void print_message(const struct nlmsghdr *nlmsg) +{ + const struct genlmsghdr *genlmsg; + const char *str; + bool out; + int i; + + if (nlmsg->nlmsg_seq && (nlmsg->nlmsg_flags & NLM_F_REQUEST)) + out = true; + else + out = false; + + if (nlmsg->nlmsg_type < NLMSG_MIN_TYPE) { + switch (nlmsg->nlmsg_type) { + case NLMSG_NOOP: + str = "Noop"; + break; + case NLMSG_ERROR: + str = "Error"; + break; + case NLMSG_DONE: + str = "Done"; + break; + case NLMSG_OVERRUN: + str = "Overrun"; + break; + default: + str = "Reserved"; + break; + } + + printf("%c %s (%u) flags 0x%04x len %u\n", + out ? '<' : '>', str, + nlmsg->nlmsg_type, + nlmsg->nlmsg_flags, + NLMSG_PAYLOAD(nlmsg, 0)); + return; + } + + genlmsg = NLMSG_DATA(nlmsg); + + str = "Reserved"; + + for (i = 0; cmd_table[i].str; i++) { + if (genlmsg->cmd == cmd_table[i].cmd) { + str = cmd_table[i].str; + break; + } + } + + printf("%c %s (%u) flags 0x%04x len %u\n", + out ? '<' : '>', str, + genlmsg->cmd, + nlmsg->nlmsg_flags, + NLMSG_PAYLOAD(nlmsg, 0)); + + print_attributes(4, NLMSG_DATA(nlmsg) + GENL_HDRLEN, + NLMSG_PAYLOAD(nlmsg, GENL_HDRLEN)); +} + +struct nlmon_req_match { + uint32_t seq; + uint32_t pid; +}; + +static bool nlmon_req_match(const void *a, const void *b) +{ + const struct nlmon_req *req = a; + const struct nlmon_req_match *match = b; + + return (req->seq == match->seq && req->pid == match->pid); +} + +static void nlmon_message(struct nlmon *nlmon, const struct nlmsghdr *nlmsg) +{ + struct nlmon_req *req; + + if (nlmsg->nlmsg_type < NLMSG_MIN_TYPE) { + struct nlmon_req_match match = { + .seq = nlmsg->nlmsg_seq, + .pid = nlmsg->nlmsg_pid + }; + + req = l_queue_remove_if(nlmon->req_list, + nlmon_req_match, &match); + if (req) { + nlmon_req_free(req); + print_message(nlmsg); + } + return; + } + + if (nlmsg->nlmsg_type != nlmon->id) + return; + + if (nlmsg->nlmsg_flags & NLM_F_REQUEST) { + req = l_new(struct nlmon_req, 1); + + req->seq = nlmsg->nlmsg_seq; + req->pid = nlmsg->nlmsg_pid; + req->flags = nlmsg->nlmsg_flags; + + l_queue_push_tail(nlmon->req_list, req); + print_message(nlmsg); + } else { + struct nlmon_req_match match = { + .seq = nlmsg->nlmsg_seq, + .pid = nlmsg->nlmsg_pid + }; + + req = l_queue_find(nlmon->req_list, nlmon_req_match, &match); + if (req) { + if (!(req->flags & NLM_F_ACK)) { + l_queue_remove(nlmon->req_list, req); + nlmon_req_free(req); + } + } + + print_message(nlmsg); + } +} + +static bool nlmon_receive(struct l_io *io, void *user_data) +{ + struct nlmon *nlmon = user_data; + struct nlmsghdr *nlmsg; + struct msghdr msg; + struct sockaddr_ll sll; + struct iovec iov; + struct cmsghdr *cmsg; + unsigned char buf[8192]; + unsigned char control[32]; + ssize_t bytes_read; + int fd; + + fd = l_io_get_fd(io); + if (fd < 0) + return false; + + memset(&sll, 0, sizeof(sll)); + + memset(&iov, 0, sizeof(iov)); + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sll; + msg.msg_namelen = sizeof(sll); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + bytes_read = recvmsg(fd, &msg, 0); + if (bytes_read < 0) { + if (errno != EAGAIN && errno != EINTR) + return false; + + return true; + } + + if (ntohs(sll.sll_protocol) != NETLINK_GENERIC) + return true; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + struct tpacket_auxdata tp; + + if (cmsg->cmsg_level != SOL_PACKET) + continue; + + if (cmsg->cmsg_type != PACKET_AUXDATA) + continue; + + memcpy(&tp, CMSG_DATA(cmsg), sizeof(tp)); + } + + for (nlmsg = iov.iov_base; NLMSG_OK(nlmsg, bytes_read); + nlmsg = NLMSG_NEXT(nlmsg, bytes_read)) { + nlmon_message(nlmon, nlmsg); + } + + return true; +} + +static struct l_io *open_packet(const char *name) +{ + struct l_io *io; + struct sockaddr_ll sll; + struct ifreq ifr; + int fd; + + fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (fd < 0) { + perror("Failed to create packet socket"); + return NULL; + } + + strncpy(ifr.ifr_name, name, IFNAMSIZ); + + if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { + perror("Failed to get monitor index"); + close(fd); + return NULL; + } + + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_protocol = htons(ETH_P_ALL); + sll.sll_ifindex = ifr.ifr_ifindex; + + if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) < 0) { + perror("Failed to bind packet socket"); + close(fd); + return NULL; + } + + io = l_io_new(fd); + + l_io_set_close_on_destroy(io, true); + + return io; +} + +struct nlmon *nlmon_open(uint16_t id) +{ + struct nlmon *nlmon; + + nlmon = l_new(struct nlmon, 1); + + nlmon->id = id; + nlmon->io = open_packet("nlmon"); + nlmon->req_list = l_queue_new(); + + l_io_set_read_handler(nlmon->io, nlmon_receive, nlmon, NULL); + + return nlmon; +} + +void nlmon_close(struct nlmon *nlmon) +{ + if (!nlmon) + return; + + l_io_destroy(nlmon->io); + l_queue_destroy(nlmon->req_list, nlmon_req_free); + + l_free(nlmon); +} diff --git a/monitor/nlmon.h b/monitor/nlmon.h new file mode 100644 index 00000000..1b85415e --- /dev/null +++ b/monitor/nlmon.h @@ -0,0 +1,28 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +struct nlmon; + +struct nlmon *nlmon_open(uint16_t id); +void nlmon_close(struct nlmon *nlmon);