From patchwork Mon Jul 29 09:19:12 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kazior X-Patchwork-Id: 262702 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from maxx.maxx.shmoo.com (maxx.shmoo.com [205.134.188.171]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "maxx.shmoo.com", Issuer "CA Cert Signing Authority" (not verified)) by ozlabs.org (Postfix) with ESMTPS id 143722C00D3 for ; Mon, 29 Jul 2013 19:53:38 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by maxx.maxx.shmoo.com (Postfix) with ESMTP id DD78D17CA4B; Mon, 29 Jul 2013 05:53:33 -0400 (EDT) X-Virus-Scanned: amavisd-new at maxx.shmoo.com Received: from maxx.maxx.shmoo.com ([127.0.0.1]) by localhost (maxx.shmoo.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id CifVAzZSoU+G; Mon, 29 Jul 2013 05:53:33 -0400 (EDT) Received: from maxx.shmoo.com (localhost [127.0.0.1]) by maxx.maxx.shmoo.com (Postfix) with ESMTP id 4E34417C93F; Mon, 29 Jul 2013 05:53:28 -0400 (EDT) X-Original-To: mailman-post+hostap@maxx.shmoo.com Delivered-To: mailman-post+hostap@maxx.shmoo.com Received: from localhost (localhost [127.0.0.1]) by maxx.maxx.shmoo.com (Postfix) with ESMTP id 2858717C93D for ; Mon, 29 Jul 2013 05:40:20 -0400 (EDT) X-Virus-Scanned: amavisd-new at maxx.shmoo.com Received: from maxx.maxx.shmoo.com ([127.0.0.1]) by localhost (maxx.shmoo.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Uj4pPiN91GSU for ; Mon, 29 Jul 2013 05:40:14 -0400 (EDT) Received: from ebb05.tieto.com (ebb05.tieto.com [131.207.168.36]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "ebb05.tieto.com", Issuer "VeriSign Class 3 Secure Server CA - G3" (not verified)) by maxx.maxx.shmoo.com (Postfix) with ESMTPS id 91B3317CAD1 for ; Mon, 29 Jul 2013 05:20:53 -0400 (EDT) X-AuditID: 83cfa824-b7f718e000000f53-25-51f633f301d9 Received: from FIHGA-EXHUB01.eu.tieto.com ( [131.207.136.34]) by ebb05.tieto.com (SMTP Mailer) with SMTP id 72.EF.03923.3F336F15; Mon, 29 Jul 2013 12:20:51 +0300 (EEST) Received: from uw001058.eu.tieto.com (10.28.19.57) by inbound.tieto.com (131.207.136.49) with Microsoft SMTP Server id 8.3.298.1; Mon, 29 Jul 2013 12:20:51 +0300 From: Michal Kazior To: Subject: [RFC v2 2/4] hostapd: Add survey dump support Date: Mon, 29 Jul 2013 11:19:12 +0200 Message-ID: <1375089554-5885-3-git-send-email-michal.kazior@tieto.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1375089554-5885-1-git-send-email-michal.kazior@tieto.com> References: <1374750707-8187-1-git-send-email-michal.kazior@tieto.com> <1375089554-5885-1-git-send-email-michal.kazior@tieto.com> MIME-Version: 1.0 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrEIsWRmVeSWpSXmKPExsXSfL5DSfez8bdAg0PHjCwWNT1gsvi29QGb A5PH55l32Tx2n9rHEsAUxWWTkpqTWZZapG+XwJXxq+kVe8G6koqWbTPZGhgvx3YxcnJICJhI fPvxmQXCFpO4cG89WxcjF4eQwCpGiTfPN0E5Sxklrv54xwhSxSagK/Gq8SwriC0iICPxcvMv sG5mAW2J9jfXweLCAmYSN67fZwexWQRUJaa8aAXr5RVwk1jSdxYozgG0TUFiziQbkDCngLvE gpYTTBC7mhklXs5bzwJRLyhxcuYTqPkSEgdfvGAGsYUEVCQOrt/PPIFRYBaSsllIyhYwMq1i 5E9NSjIw1SvJTC3J10vOz93ECA66FSo7GM8+kDrEKMDBqMTDa+H6JVCINbGsuDL3EKMkB5OS KK+W4bdAIb6k/JTKjMTijPii0pzU4kOMEhzMSiK8HEZfA4V4UxIrq1KL8mFS0hwsSuK8huvv BQoJpCeWpGanphakFsFkZTg4lCR4rxkBDRUsSk1PrUjLzClBSDNxcIIM5wEavh6khre4IDG3 ODMdIn+KUVFKnHcjSEIAJJFRmgfXC0sKrxjFgV4R5t0OUsUDTChw3a+ABjMBDd6tAja4JBEh JdXAqFa1r7f60vmmpw/f16w8xfEk3PhT8Tm/O4xt7xdPkd0y5YdsB3+UsNdPobcLO2MOROdt FVVl8UuaUVa9w/9klxdT5DmNf8nzNrbYVRfO8g7hcpab0yt9i89ttePjXw/4t69Nv2ZmWjNH P3NT9ZQUuRqG095n9+/p8v5kdej9gQWiz/a09iRZKrEUZyQaajEXFScCAOR0yAPlAgAA X-BeenThere: hostap@lists.shmoo.com X-Mailman-Version: 2.1.11 Precedence: list List-Id: HostAP Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: hostap-bounces@lists.shmoo.com Errors-To: hostap-bounces@lists.shmoo.com This adds survey dump support for all frequencies and for specific desired frequencies. This will later be used by ACS code for spectrum heuristics. Signed-hostap: Michal Kazior --- v2: Fix failure if survey data from a different mode appeared in survey list. v2: Fix wpa_printf() argument order of channel_time_busy/rx src/ap/ap_drv_ops.h | 10 +++ src/ap/drv_callbacks.c | 72 ++++++++++++++++ src/ap/hostapd.h | 9 ++ src/drivers/driver.h | 99 +++++++++++++++++++++- src/drivers/driver_common.c | 1 + src/drivers/driver_nl80211.c | 185 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 374 insertions(+), 2 deletions(-) diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index cfc30ce..269f4ef 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -225,4 +225,14 @@ static inline void hostapd_drv_poll_client(struct hostapd_data *hapd, hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos); } +static inline int hostapd_drv_get_survey(struct hostapd_data *hapd, + unsigned int freq) +{ + if (hapd->driver == NULL) + return -1; + if (!hapd->driver->get_survey) + return -1; + return hapd->driver->get_survey(hapd->drv_priv, freq); +} + #endif /* AP_DRV_OPS */ diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index fa4b5e4..e0f0491 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -715,6 +715,75 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, } +struct hostapd_channel_data * hostapd_get_mode_channel( + struct hostapd_iface *iface, + unsigned int freq) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (!chan) + return NULL; + if ((unsigned int) chan->freq == freq) + return chan; + } + + return NULL; +} + + +static void hostapd_update_nf(struct hostapd_iface *iface, + struct hostapd_channel_data *chan, + struct freq_survey *survey) +{ + if (!iface->chans_surveyed) { + chan->min_nf = survey->nf; + iface->lowest_nf = survey->nf; + } else { + if (dl_list_empty(&chan->survey_list)) + chan->min_nf = survey->nf; + else if (survey->nf < chan->min_nf) + chan->min_nf = survey->nf; + if (survey->nf < iface->lowest_nf) + iface->lowest_nf = survey->nf; + } +} + + +static void hostapd_event_get_survey(struct hostapd_data *hapd, + struct survey_results *survey_results) +{ + struct hostapd_iface *iface = hapd->iface; + struct freq_survey *survey, *tmp; + struct hostapd_channel_data *chan; + + if (dl_list_empty(&survey_results->survey_list)) { + wpa_printf(MSG_DEBUG, "No survey data received"); + return; + } + + dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, + struct freq_survey, list) { + chan = hostapd_get_mode_channel(iface, survey->freq); + if (!chan) + continue; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + dl_list_del(&survey->list); + dl_list_add_tail(&chan->survey_list, &survey->list); + + hostapd_update_nf(iface, chan, survey); + + iface->chans_surveyed++; + } + + return; +} + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -856,6 +925,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, hapd, data->connect_failed_reason.addr, data->connect_failed_reason.code); break; + case EVENT_SURVEY: + hostapd_event_get_survey(hapd, &data->survey_results); + break; default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index c0b1306..ea70fe8 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -280,6 +280,15 @@ struct hostapd_iface { int olbc_ht; u16 ht_op_mode; + + /* surveying helpers */ + + /* number of channels surveyed */ + unsigned int chans_surveyed; + + /* lowest observed noise floor in dBm */ + s8 lowest_nf; + void (*scan_cb)(struct hostapd_iface *iface); }; diff --git a/src/drivers/driver.h b/src/drivers/driver.h index d78bdd0..661f427 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -20,6 +20,7 @@ #define WPA_SUPPLICANT_DRIVER_VERSION 4 #include "common/defs.h" +#include "utils/list.h" #define HOSTAPD_CHAN_DISABLED 0x00000001 #define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 @@ -55,9 +56,20 @@ struct hostapd_channel_data { int flag; /** - * max_tx_power - maximum transmit power in dBm + * max_tx_power - Maximum transmit power in dBm */ u8 max_tx_power; + + /* + * survey_list - Linked list of surveys + */ + struct dl_list survey_list; + + /** + * min_nf - Minimum observed noise floor, in dBm, based on all + * surveyed channel data + */ + s8 min_nf; }; #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0) @@ -2461,6 +2473,7 @@ struct wpa_driver_ops { * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in * struct wpa_driver_capa. */ + int (*p2p_sd_response)(void *priv, int freq, const u8 *dst, u8 dialog_token, const struct wpabuf *resp_tlvs); @@ -2726,6 +2739,31 @@ struct wpa_driver_ops { * mode. */ int (*stop_ap)(void *priv); + + /** + * get_survey - Retrieve survey data + * @priv: Private driver interface data + * @freq: If set, survey data for the specified frequency is only + * being requested. If not set, all survey data is requested. + * Returns: 0 on success, -1 on failure + * + * Use this to retrieve: + * + * - the observed channel noise floor + * - the amount of time we have spent on the channel + * - the amount of time during which we have spent on the channel that + * the radio has determined the medium is busy and we cannot + * transmit + * - the amount of time we have spent receiving data + * - the amount of time we have spent transmitting data + * + * This data can be used for spectrum heuristics. One example is + * Automatic Channel Selection (ACS). The channel survey data is + * kept on a linked list on the channel data, one entry is added + * for each survey. The min_nf of the channel is updated for each + * survey. + */ + int (*get_survey)(void *priv, unsigned int freq); }; @@ -3210,11 +3248,57 @@ enum wpa_event_type { * * The channel which was previously unavailable is now available again. */ - EVENT_DFS_NOP_FINISHED + EVENT_DFS_NOP_FINISHED, + + /* + * EVENT_SURVEY - Received survey data + * + * This event gets triggered when a driver query is issued for survey + * data and it's returned. The returned data is stored in + * struct survey_results. The results provide at most one survey + * entry for each frequency and at minimum will provide one survey + * entry for one frequency. The survey data can be os_malloc()'d and + * then os_free()'d, so the event callback must only copy data. + */ + EVENT_SURVEY }; /** + * struct freq_survey - Channel survey info + * + * @ifidx: Interface index in which this survey was observed + * @freq: Center of frequency of the surveyed channel + * @nf: Channel noise floor in dBm + * @channel_time: Amount of time in ms the radio spent on the channel + * @channel_time_busy: Amount of time in ms the radio detected some signal + * that indicated to the radio the channel was not clear + * @channel_time_rx: Amount of time the radio spent receiving data + * @channel_time_tx: Amount of time the radio spent transmitting data + * @filled: bitmask indicating which fields have been reported, see + * SURVEY_HAS_* defines. + * @list: Internal list pointers + */ +struct freq_survey { + u32 ifidx; + unsigned int freq; + s8 nf; + u64 channel_time; + u64 channel_time_busy; + u64 channel_time_rx; + u64 channel_time_tx; + unsigned int filled; + struct dl_list list; +}; + +#define SURVEY_HAS_NF BIT(0) +#define SURVEY_HAS_CHAN_TIME BIT(1) +#define SURVEY_HAS_CHAN_TIME_BUSY BIT(2) +#define SURVEY_HAS_CHAN_TIME_RX BIT(3) +#define SURVEY_HAS_CHAN_TIME_TX BIT(4) + + +/** * union wpa_event_data - Additional data for wpa_supplicant_event() calls */ union wpa_event_data { @@ -3856,6 +3940,17 @@ union wpa_event_data { struct dfs_event { int freq; } dfs_event; + + /** + * survey_results - Survey result data for EVENT_SURVEY + * @freq_filter: Requested frequency survey filter, 0 if request + * was for all survey data + * @survey_list: Linked list of survey data + */ + struct survey_results { + unsigned int freq_filter; + struct dl_list survey_list; /* struct freq_survey */ + } survey_results; }; /** diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index 12ccc14..8d1d22e 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -84,6 +84,7 @@ const char * event_to_string(enum wpa_event_type event) E2S(DFS_CAC_FINISHED); E2S(DFS_CAC_ABORTED); E2S(DFS_NOP_FINISHED); + E2S(SURVEY); } return "UNKNOWN"; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 83d4a23..034fe26 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -9931,6 +9931,190 @@ static int nl80211_flush_pmkid(void *priv) } +static void clean_survey_results(struct survey_results *survey_results) +{ + struct freq_survey *survey, *tmp; + + if (dl_list_empty(&survey_results->survey_list)) + return; + + dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, + struct freq_survey, list) { + dl_list_del(&survey->list); + os_free(survey); + } +} + + +static void add_survey(struct nlattr **sinfo, u32 ifidx, + struct dl_list *survey_list) +{ + struct freq_survey *survey; + + survey = os_zalloc(sizeof(struct freq_survey)); + if (!survey) + return; + + survey->ifidx = ifidx; + survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); + survey->filled = 0; + + if (sinfo[NL80211_SURVEY_INFO_NOISE]) { + survey->nf = (int8_t) + nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + survey->filled |= SURVEY_HAS_NF; + } + + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) { + survey->channel_time = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]); + survey->filled |= SURVEY_HAS_CHAN_TIME; + } + + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) { + survey->channel_time_busy = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]); + survey->filled |= SURVEY_HAS_CHAN_TIME_BUSY; + } + + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) { + survey->channel_time_rx = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]); + survey->filled |= SURVEY_HAS_CHAN_TIME_RX; + } + + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) { + survey->channel_time_tx = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]); + survey->filled |= SURVEY_HAS_CHAN_TIME_TX; + } + + wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz " + "noise=%d channel_time=%ld busy_time=%ld tx_time=%ld " + "rx_time=%ld filled=%04x)", + survey->freq, + survey->nf, + (unsigned long int) survey->channel_time, + (unsigned long int) survey->channel_time_busy, + (unsigned long int) survey->channel_time_tx, + (unsigned long int) survey->channel_time_rx, + survey->filled); + + dl_list_add_tail(survey_list, &survey->list); +} + + +static int check_survey_ok(struct nlattr **sinfo, u32 surveyed_freq, + unsigned int freq_filter) +{ + if (!freq_filter) + return 1; + + if (freq_filter == surveyed_freq) + return 1; + + return 0; +} + + +static int survey_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + struct survey_results *survey_results; + u32 surveyed_freq = 0; + u32 ifidx; + + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + + survey_results = (struct survey_results *) arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) + return NL_SKIP; + + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) { + wpa_printf(MSG_ERROR, "nl80211: invalid survey data"); + return NL_SKIP; + } + + surveyed_freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); + + if (!check_survey_ok(sinfo, surveyed_freq, + survey_results->freq_filter)) + return NL_SKIP; + + if (survey_results->freq_filter && + survey_results->freq_filter != surveyed_freq) { + wpa_printf(MSG_EXCESSIVE, "nl80211: Ignoring survey data " + "for freq %d MHz", surveyed_freq); + return NL_SKIP; + } + + add_survey(sinfo, ifidx, &survey_results->survey_list); + + return NL_SKIP; +} + + +static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int err = -ENOBUFS; + union wpa_event_data data; + struct survey_results *survey_results; + + os_memset(&data, 0, sizeof(data)); + survey_results = &data.survey_results; + + dl_list_init(&survey_results->survey_list); + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + if (freq) + data.survey_results.freq_filter = freq; + + do { + err = send_and_recv_msgs(drv, msg, survey_handler, + survey_results); + } while (err > 0); + + if (err) { + wpa_printf(MSG_ERROR, "nl80211: Failed to process survey " + "data"); + goto out_clean; + } + + wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data); + +out_clean: + clean_survey_results(survey_results); +nla_put_failure: + return err; +} + + static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck, const u8 *replay_ctr) { @@ -10557,4 +10741,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { #endif /* CONFIG_TDLS */ .update_ft_ies = wpa_driver_nl80211_update_ft_ies, .get_mac_addr = wpa_driver_nl80211_get_macaddr, + .get_survey = wpa_driver_nl80211_get_survey, };