From ec15e68ba6a505631016f230899bafbb7b8cd0d6 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 23 Mar 2011 15:29:52 +0200 Subject: cfg80211: Add nl80211 event for deletion of a station entry Indicate an NL80211_CMD_DEL_STATION event when a station entry in mac80211 is deleted to match with the NL80211_CMD_NEW_STATION event that is used when the entry was added. This is needed, e.g., to allow user space to remove a peer from RSN IBSS Authenticator state machine to avoid re-authentication and re-keying delays when the peer is not reachable anymore. Signed-off-by: Jouni Malinen Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/mlme.c | 9 +++++++++ net/wireless/nl80211.c | 34 ++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 3 +++ 3 files changed, 46 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index aa5df8865ff..16881fea4ce 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -770,6 +770,15 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, } EXPORT_SYMBOL(cfg80211_new_sta); +void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp); +} +EXPORT_SYMBOL(cfg80211_del_sta); + struct cfg80211_mgmt_registration { struct list_head list; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4ebce4284e9..40c90fb461c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5966,6 +5966,40 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, nl80211_mlme_mcgrp.id, gfp); } +void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *mac_addr, + gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct net_device *netdev, u32 nlpid, int freq, const u8 *buf, size_t len, gfp_t gfp) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index e3f7fa88696..dcac5cd6f01 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -79,6 +79,9 @@ void nl80211_send_remain_on_channel_cancel( void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo, gfp_t gfp); +void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *mac_addr, + gfp_t gfp); int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct net_device *netdev, u32 nlpid, int freq, -- cgit v1.2.3-70-g09d2 From f4263c9857e6411ef2388868cc6c79a1602a654e Mon Sep 17 00:00:00 2001 From: Paul Stewart Date: Thu, 31 Mar 2011 09:25:41 -0700 Subject: nl80211: Add BSS parameters to station This allows user-space monitoring of BSS parameters for the associated station. This is useful for debugging and verifying that the paramaters are as expected. [Exactly the same as before but bundled into a single message] Signed-off-by: Paul Stewart Cc: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 35 ++++++++++++++++++++++++++++++++++- include/net/cfg80211.h | 34 ++++++++++++++++++++++++++++++++++ net/mac80211/cfg.c | 13 ++++++++++++- net/wireless/nl80211.c | 21 ++++++++++++++++++++- 4 files changed, 100 insertions(+), 3 deletions(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 30022189104..16eea7229e9 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1221,6 +1221,36 @@ enum nl80211_rate_info { NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 }; +/** + * enum nl80211_sta_bss_param - BSS information collected by STA + * + * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM + * when getting information about the bitrate of a station. + * + * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved + * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag) + * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: whether short preamble is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: whether short slot time is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8) + * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16) + * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined + * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use + */ +enum nl80211_sta_bss_param { + __NL80211_STA_BSS_PARAM_INVALID, + NL80211_STA_BSS_PARAM_CTS_PROT, + NL80211_STA_BSS_PARAM_SHORT_PREAMBLE, + NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME, + NL80211_STA_BSS_PARAM_DTIM_PERIOD, + NL80211_STA_BSS_PARAM_BEACON_INTERVAL, + + /* keep last */ + __NL80211_STA_BSS_PARAM_AFTER_LAST, + NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1 +}; + /** * enum nl80211_sta_info - station information * @@ -1233,7 +1263,7 @@ enum nl80211_rate_info { * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute - * containing info as possible, see &enum nl80211_sta_info_txrate. + * containing info as possible, see &enum nl80211_rate_info * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this * station) @@ -1245,6 +1275,8 @@ enum nl80211_rate_info { * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested * attribute, like NL80211_STA_INFO_TX_BITRATE. + * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute + * containing info as possible, see &enum nl80211_sta_bss_param * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1264,6 +1296,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_TX_FAILED, NL80211_STA_INFO_SIGNAL_AVG, NL80211_STA_INFO_RX_BITRATE, + NL80211_STA_INFO_BSS_PARAM, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 2c453045172..ba7384acf4e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -422,6 +422,7 @@ struct station_parameters { * @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled * @STATION_INFO_SIGNAL_AVG: @signal_avg filled * @STATION_INFO_RX_BITRATE: @rxrate fields are filled + * @STATION_INFO_BSS_PARAM: @bss_param filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -439,6 +440,7 @@ enum station_info_flags { STATION_INFO_RX_DROP_MISC = 1<<12, STATION_INFO_SIGNAL_AVG = 1<<13, STATION_INFO_RX_BITRATE = 1<<14, + STATION_INFO_BSS_PARAM = 1<<15, }; /** @@ -472,6 +474,37 @@ struct rate_info { u16 legacy; }; +/** + * enum station_info_rate_flags - bitrate info flags + * + * Used by the driver to indicate the specific rate transmission + * type for 802.11n transmissions. + * + * @BSS_PARAM_FLAGS_CTS_PROT: whether CTS protection is enabled + * @BSS_PARAM_FLAGS_SHORT_PREAMBLE: whether short preamble is enabled + * @BSS_PARAM_FLAGS_SHORT_SLOT_TIME: whether short slot time is enabled + */ +enum bss_param_flags { + BSS_PARAM_FLAGS_CTS_PROT = 1<<0, + BSS_PARAM_FLAGS_SHORT_PREAMBLE = 1<<1, + BSS_PARAM_FLAGS_SHORT_SLOT_TIME = 1<<2, +}; + +/** + * struct sta_bss_parameters - BSS parameters for the attached station + * + * Information about the currently associated BSS + * + * @flags: bitflag of flags from &enum bss_param_flags + * @dtim_period: DTIM period for the BSS + * @beacon_interval: beacon interval + */ +struct sta_bss_parameters { + u8 flags; + u8 dtim_period; + u16 beacon_interval; +}; + /** * struct station_info - station information * @@ -515,6 +548,7 @@ struct station_info { u32 tx_retries; u32 tx_failed; u32 rx_dropped_misc; + struct sta_bss_parameters bss_param; int generation; }; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 334213571ad..bf5d28da46e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -342,7 +342,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) STATION_INFO_TX_FAILED | STATION_INFO_TX_BITRATE | STATION_INFO_RX_BITRATE | - STATION_INFO_RX_DROP_MISC; + STATION_INFO_RX_DROP_MISC | + STATION_INFO_BSS_PARAM; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); sinfo->rx_bytes = sta->rx_bytes; @@ -389,6 +390,16 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->plink_state = sta->plink_state; #endif } + + sinfo->bss_param.flags = 0; + if (sdata->vif.bss_conf.use_cts_prot) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT; + if (sdata->vif.bss_conf.use_short_preamble) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (sdata->vif.bss_conf.use_short_slot) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period; + sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int; } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 40c90fb461c..297d7ce4117 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2002,7 +2002,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, const u8 *mac_addr, struct station_info *sinfo) { void *hdr; - struct nlattr *sinfoattr; + struct nlattr *sinfoattr, *bss_param; hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); if (!hdr) @@ -2062,6 +2062,25 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, if (sinfo->filled & STATION_INFO_TX_FAILED) NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED, sinfo->tx_failed); + if (sinfo->filled & STATION_INFO_BSS_PARAM) { + bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM); + if (!bss_param) + goto nla_put_failure; + + if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) + NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_CTS_PROT); + if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) + NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE); + if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) + NLA_PUT_FLAG(msg, + NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME); + NLA_PUT_U8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD, + sinfo->bss_param.dtim_period); + NLA_PUT_U16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL, + sinfo->bss_param.beacon_interval); + + nla_nest_end(msg, bss_param); + } nla_nest_end(msg, sinfoattr); return genlmsg_end(msg, hdr); -- cgit v1.2.3-70-g09d2 From 146095557b01cf5ff5d66554d96cbb8133d94eb9 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Apr 2011 10:49:03 -0700 Subject: cfg80211: fix regulatory restore upon user hints When we restore regulatory settings its possible CRDA will not reply because of a bogus user entry. In this case the bogus entry will prevent any further processing on cfg80211 for regulatory domains even if we restore regulatory settings. To prevent this we suck out all pending requests when restoring regulatory settings and add them back into the queue after we have queued up the reset work. The impact of not having this applied is that a user with privileges can issue a userspace regulatory hint while we are disasocciating and this would prevent any further processing of regulatory domains. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 3332d5bce31..7b1a89b20eb 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1744,6 +1744,8 @@ static void restore_regulatory_settings(bool reset_user) { char alpha2[2]; struct reg_beacon *reg_beacon, *btmp; + struct regulatory_request *reg_request, *tmp; + LIST_HEAD(tmp_reg_req_list); mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); @@ -1751,6 +1753,25 @@ static void restore_regulatory_settings(bool reset_user) reset_regdomains(); restore_alpha2(alpha2, reset_user); + /* + * If there's any pending requests we simply + * stash them to a temporary pending queue and + * add then after we've restored regulatory + * settings. + */ + spin_lock(®_requests_lock); + if (!list_empty(®_requests_list)) { + list_for_each_entry_safe(reg_request, tmp, + ®_requests_list, list) { + if (reg_request->initiator != + NL80211_REGDOM_SET_BY_USER) + continue; + list_del(®_request->list); + list_add_tail(®_request->list, &tmp_reg_req_list); + } + } + spin_unlock(®_requests_lock); + /* Clear beacon hints */ spin_lock_bh(®_pending_beacons_lock); if (!list_empty(®_pending_beacons)) { @@ -1785,8 +1806,31 @@ static void restore_regulatory_settings(bool reset_user) */ if (is_an_alpha2(alpha2)) regulatory_hint_user(user_alpha2); -} + if (list_empty(&tmp_reg_req_list)) + return; + + mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); + + spin_lock(®_requests_lock); + list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { + REG_DBG_PRINT("Adding request for country %c%c back " + "into the queue\n", + reg_request->alpha2[0], + reg_request->alpha2[1]); + list_del(®_request->list); + list_add_tail(®_request->list, ®_requests_list); + } + spin_unlock(®_requests_lock); + + mutex_unlock(®_mutex); + mutex_unlock(&cfg80211_mutex); + + REG_DBG_PRINT("Kicking the queue\n"); + + schedule_work(®_work); +} void regulatory_hint_disconnect(void) { -- cgit v1.2.3-70-g09d2 From a90c7a313a1c5b4fc99f987a2ae8f92ab0ae35c7 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Apr 2011 10:49:04 -0700 Subject: cfg80211: add a timer for invalid user reg hints We have no other option but to inform userspace that we have queued up their regulatory hint request when we are given one given that nl80211 operates atomically on user requests. The best we can do is accept the request, and add a delayed work item for processing failure and cancel it if we succeeed. Upon failure we restore the regulatory settings and ignore the user input. This fixes this reported bug: https://bugzilla.kernel.org/show_bug.cgi?id=28112 Reported-by: gregoryx.alagnou@intel.com Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7b1a89b20eb..2714379ce2d 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -106,6 +106,9 @@ struct reg_beacon { static void reg_todo(struct work_struct *work); static DECLARE_WORK(reg_work, reg_todo); +static void reg_timeout_work(struct work_struct *work); +static DECLARE_DELAYED_WORK(reg_timeout, reg_timeout_work); + /* We keep a static world regulatory domain in case of the absence of CRDA */ static const struct ieee80211_regdomain world_regdom = { .n_reg_rules = 5, @@ -1330,6 +1333,9 @@ static void reg_set_request_processed(void) need_more_processing = true; spin_unlock(®_requests_lock); + if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) + cancel_delayed_work_sync(®_timeout); + if (need_more_processing) schedule_work(®_work); } @@ -1440,8 +1446,17 @@ static void reg_process_hint(struct regulatory_request *reg_request) r = __regulatory_hint(wiphy, reg_request); /* This is required so that the orig_* parameters are saved */ if (r == -EALREADY && wiphy && - wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) + wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { wiphy_update_regulatory(wiphy, initiator); + return; + } + + /* + * We only time out user hints, given that they should be the only + * source of bogus requests. + */ + if (reg_request->initiator == NL80211_REGDOM_SET_BY_USER) + schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); } /* @@ -2169,6 +2184,13 @@ out: mutex_unlock(®_mutex); } +static void reg_timeout_work(struct work_struct *work) +{ + REG_DBG_PRINT("Timeout while waiting for CRDA to reply, " + "restoring regulatory settings"); + restore_regulatory_settings(true); +} + int __init regulatory_init(void) { int err = 0; @@ -2222,6 +2244,7 @@ void /* __init_or_exit */ regulatory_exit(void) struct reg_beacon *reg_beacon, *btmp; cancel_work_sync(®_work); + cancel_delayed_work_sync(®_timeout); mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); -- cgit v1.2.3-70-g09d2 From 581a8b0feeed8877aab3a8ca4c972419790cd07f Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Thu, 7 Apr 2011 15:08:27 -0700 Subject: nl80211: rename NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE To NL80211_MESH_SETUP_IE. This reflects our ability to insert any ie into a mesh beacon, not simply path selection ies. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- include/linux/nl80211.h | 9 +++++---- include/net/cfg80211.h | 8 ++++---- net/mac80211/cfg.c | 15 +++++++-------- net/mac80211/ieee80211_i.h | 4 ++-- net/mac80211/mesh.c | 6 +++--- net/mac80211/mesh_plink.c | 2 +- net/mac80211/tx.c | 2 +- net/wireless/mesh.c | 4 ++-- net/wireless/nl80211.c | 11 ++++++----- 9 files changed, 31 insertions(+), 30 deletions(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 16eea7229e9..ecf6b68a96d 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -545,6 +545,7 @@ enum nl80211_commands { /* source-level API compatibility */ #define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG #define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG +#define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE /** * enum nl80211_attrs - nl80211 netlink attributes @@ -1719,9 +1720,9 @@ enum nl80211_meshconf_params { * vendor specific path metric or disable it to use the default Airtime * metric. * - * @NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE: A vendor specific information - * element that vendors will use to identify the path selection methods and - * metrics in use. + * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a + * robust security network ie, or a vendor specific information element that + * vendors will use to identify the path selection methods and metrics in use. * * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use @@ -1730,7 +1731,7 @@ enum nl80211_mesh_setup_params { __NL80211_MESH_SETUP_INVALID, NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL, NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC, - NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE, + NL80211_MESH_SETUP_IE, /* keep last */ __NL80211_MESH_SETUP_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ba7384acf4e..1d02ddf5a8a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -689,8 +689,8 @@ struct mesh_config { * @mesh_id_len: length of the mesh ID, at least 1 and at most 32 bytes * @path_sel_proto: which path selection protocol to use * @path_metric: which metric to use - * @vendor_ie: vendor information elements (optional) - * @vendor_ie_len: length of vendor information elements + * @ie: vendor information elements (optional) + * @ie_len: length of vendor information elements * * These parameters are fixed when the mesh is created. */ @@ -699,8 +699,8 @@ struct mesh_setup { u8 mesh_id_len; u8 path_sel_proto; u8 path_metric; - const u8 *vendor_ie; - u8 vendor_ie_len; + const u8 *ie; + u8 ie_len; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index bf5d28da46e..d9428afd8bf 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1034,26 +1034,25 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, u8 *new_ie; const u8 *old_ie; - /* first allocate the new vendor information element */ + /* allocate information elements */ new_ie = NULL; - old_ie = ifmsh->vendor_ie; + old_ie = ifmsh->ie; - ifmsh->vendor_ie_len = setup->vendor_ie_len; - if (setup->vendor_ie_len) { - new_ie = kmemdup(setup->vendor_ie, setup->vendor_ie_len, + if (setup->ie_len) { + new_ie = kmemdup(setup->ie, setup->ie_len, GFP_KERNEL); if (!new_ie) return -ENOMEM; } + ifmsh->ie_len = setup->ie_len; + ifmsh->ie = new_ie; + kfree(old_ie); /* now copy the rest of the setup parameters */ ifmsh->mesh_id_len = setup->mesh_id_len; memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len); ifmsh->mesh_pp_id = setup->path_sel_proto; ifmsh->mesh_pm_id = setup->path_metric; - ifmsh->vendor_ie = new_ie; - - kfree(old_ie); return 0; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6eb2c8523ee..6450100594b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -488,8 +488,8 @@ struct ieee80211_if_mesh { struct mesh_config mshcfg; u32 mesh_seqnum; bool accepting_plinks; - const u8 *vendor_ie; - u8 vendor_ie_len; + const u8 *ie; + u8 ie_len; }; #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 2a57cc02c61..1c244c0c766 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -279,9 +279,9 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; *pos++ = 0x00; - if (sdata->u.mesh.vendor_ie) { - int len = sdata->u.mesh.vendor_ie_len; - const u8 *data = sdata->u.mesh.vendor_ie; + if (sdata->u.mesh.ie) { + int len = sdata->u.mesh.ie_len; + const u8 *data = sdata->u.mesh.ie; if (skb_tailroom(skb) > len) memcpy(skb_put(skb, len), data, len); } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 44b53931ba5..c705b20e1ac 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -161,7 +161,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, __le16 reason) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 + - sdata->u.mesh.vendor_ie_len); + sdata->u.mesh.ie_len); struct ieee80211_mgmt *mgmt; bool include_plid = false; static const u8 meshpeeringproto[] = { 0x00, 0x0F, 0xAC, 0x2A }; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ce4596ed126..17b10be31f5 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2262,7 +2262,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, /* headroom, head length, tail length and maximum TIM length */ skb = dev_alloc_skb(local->tx_headroom + 400 + - sdata->u.mesh.vendor_ie_len); + sdata->u.mesh.ie_len); if (!skb) goto out; diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 73e39c171ff..0d4b2260f96 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -53,8 +53,8 @@ const struct mesh_config default_mesh_config = { const struct mesh_setup default_mesh_setup = { .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, .path_metric = IEEE80211_PATH_METRIC_AIRTIME, - .vendor_ie = NULL, - .vendor_ie_len = 0, + .ie = NULL, + .ie_len = 0, }; int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 297d7ce4117..ccd825a5857 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2823,7 +2823,7 @@ static const struct nla_policy nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = { [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 }, [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, - [NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE] = { .type = NLA_BINARY, + [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, }; @@ -2925,13 +2925,14 @@ static int nl80211_parse_mesh_setup(struct genl_info *info, IEEE80211_PATH_METRIC_VENDOR : IEEE80211_PATH_METRIC_AIRTIME; - if (tb[NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE]) { + + if (tb[NL80211_MESH_SETUP_IE]) { struct nlattr *ieattr = - tb[NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE]; + tb[NL80211_MESH_SETUP_IE]; if (!is_valid_ie_attr(ieattr)) return -EINVAL; - setup->vendor_ie = nla_data(ieattr); - setup->vendor_ie_len = nla_len(ieattr); + setup->ie = nla_data(ieattr); + setup->ie_len = nla_len(ieattr); } return 0; -- cgit v1.2.3-70-g09d2 From 15d5dda623139bbf6165030fc251bbd5798f4130 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Thu, 7 Apr 2011 15:08:28 -0700 Subject: cfg80211/nl80211: Add userspace authentication flag to mesh setup During mesh setup, use NL80211_MESH_SETUP_USERSPACE_AUTH flag to create a secure mesh and route management frames to userspace. Also, NL80211_CMD_GET_WIPHY now returns a flag NL80211_SUPPORT_MESH_AUTH if the wiphy's mesh implementation supports routing of mesh auth frames to userspace. This is useful for forward compatibility between old kernels and new userspace tools. Signed-off-by: Javier Cardona Signed-off-by: Thomas Pedersen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 9 +++++++++ include/net/cfg80211.h | 5 +++++ net/wireless/mesh.c | 4 ++++ net/wireless/nl80211.c | 5 +++++ 4 files changed, 23 insertions(+) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index ecf6b68a96d..0e652d86081 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -887,6 +887,9 @@ enum nl80211_commands { * changed once the mesh is active. * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute * containing attributes from &enum nl80211_meshconf_params. + * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver + * allows auth frames in a mesh to be passed to userspace for processing via + * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -1075,6 +1078,8 @@ enum nl80211_attrs { NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, + NL80211_ATTR_SUPPORT_MESH_AUTH, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1724,6 +1729,9 @@ enum nl80211_meshconf_params { * robust security network ie, or a vendor specific information element that * vendors will use to identify the path selection methods and metrics in use. * + * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication + * daemon will be authenticating mesh candidates. + * * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use */ @@ -1732,6 +1740,7 @@ enum nl80211_mesh_setup_params { NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL, NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC, NL80211_MESH_SETUP_IE, + NL80211_MESH_SETUP_USERSPACE_AUTH, /* keep last */ __NL80211_MESH_SETUP_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1d02ddf5a8a..e77603bd163 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -691,6 +691,7 @@ struct mesh_config { * @path_metric: which metric to use * @ie: vendor information elements (optional) * @ie_len: length of vendor information elements + * @is_secure: or not * * These parameters are fixed when the mesh is created. */ @@ -701,6 +702,7 @@ struct mesh_setup { u8 path_metric; const u8 *ie; u8 ie_len; + bool is_secure; }; /** @@ -1451,6 +1453,8 @@ struct cfg80211_ops { * @WIPHY_FLAG_IBSS_RSN: The device supports IBSS RSN. * @WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS: The device supports separate * unicast and multicast TX keys. + * @WIPHY_FLAG_MESH_AUTH: The device supports mesh authentication by routing + * auth frames to userspace. See @NL80211_MESH_SETUP_USERSPACE_AUTH. */ enum wiphy_flags { WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), @@ -1463,6 +1467,7 @@ enum wiphy_flags { WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), WIPHY_FLAG_IBSS_RSN = BIT(8), WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS= BIT(9), + WIPHY_FLAG_MESH_AUTH = BIT(10), }; struct mac_address { diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 0d4b2260f96..0e5c122ce32 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -72,6 +72,10 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; + if (!(rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) && + setup->is_secure) + return -EOPNOTSUPP; + if (wdev->mesh_id_len) return -EALREADY; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ccd825a5857..cbedfc2a42a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -124,6 +124,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 }, [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED }, + [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG }, [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, .len = NL80211_HT_CAPABILITY_LEN }, @@ -594,6 +595,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN); + if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) + NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH); NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES, sizeof(u32) * dev->wiphy.n_cipher_suites, @@ -2823,6 +2826,7 @@ static const struct nla_policy nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = { [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 }, [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, + [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, }; @@ -2934,6 +2938,7 @@ static int nl80211_parse_mesh_setup(struct genl_info *info, setup->ie = nla_data(ieattr); setup->ie_len = nla_len(ieattr); } + setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); return 0; } -- cgit v1.2.3-70-g09d2 From 5cff5e01e818029a5d2c3c31b7ae5e5e7ee70452 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Thu, 7 Apr 2011 15:08:29 -0700 Subject: mac80211: ignore peers if security is enabled for this mesh Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 1 + net/mac80211/ieee80211_i.h | 1 + net/mac80211/mesh.c | 4 ++++ net/mac80211/mesh_plink.c | 4 ++++ net/wireless/mesh.c | 1 + 5 files changed, 11 insertions(+) (limited to 'net/wireless') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d9428afd8bf..dc623d884d0 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1053,6 +1053,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len); ifmsh->mesh_pp_id = setup->path_sel_proto; ifmsh->mesh_pm_id = setup->path_metric; + ifmsh->is_secure = setup->is_secure; return 0; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6450100594b..8d6d6e3d95d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -490,6 +490,7 @@ struct ieee80211_if_mesh { bool accepting_plinks; const u8 *ie; u8 ie_len; + bool is_secure; }; #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 1c244c0c766..47a26c0f699 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -573,6 +573,10 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, &elems); + /* ignore beacons from secure mesh peers if our security is off */ + if (elems.rsn_len && !sdata->u.mesh.is_secure) + return; + if (elems.ds_params && elems.ds_params_len == 1) freq = ieee80211_channel_to_frequency(elems.ds_params[0], band); else diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index c705b20e1ac..bafe25594e0 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -449,6 +449,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m mpl_dbg("Mesh plink: missing necessary peer link ie\n"); return; } + if (elems.rsn_len && !sdata->u.mesh.is_secure) { + mpl_dbg("Mesh plink: can't establish link with secure peer\n"); + return; + } ftype = mgmt->u.action.u.plink_action.action_code; ie_len = elems.peer_link_len; diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 0e5c122ce32..e0226e8265a 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -55,6 +55,7 @@ const struct mesh_setup default_mesh_setup = { .path_metric = IEEE80211_PATH_METRIC_AIRTIME, .ie = NULL, .ie_len = 0, + .is_secure = false, }; int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, -- cgit v1.2.3-70-g09d2 From b39c48fac1fc915a5dcd024bf6e9aabc855ed591 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Thu, 7 Apr 2011 15:08:30 -0700 Subject: nl80211/mac80211: let userspace authenticate stations Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- include/linux/nl80211.h | 2 ++ net/mac80211/cfg.c | 6 ++++++ net/wireless/nl80211.c | 5 ++++- 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 0e652d86081..5ec4ac3a0ef 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1174,6 +1174,7 @@ enum nl80211_iftype { * with short barker preamble * @NL80211_STA_FLAG_WME: station is WME/QoS capable * @NL80211_STA_FLAG_MFP: station uses management frame protection + * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated * @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ @@ -1183,6 +1184,7 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_SHORT_PREAMBLE, NL80211_STA_FLAG_WME, NL80211_STA_FLAG_MFP, + NL80211_STA_FLAG_AUTHENTICATED, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index dc623d884d0..1c25723eacd 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -686,6 +686,12 @@ static void sta_apply_parameters(struct ieee80211_local *local, if (set & BIT(NL80211_STA_FLAG_MFP)) sta->flags |= WLAN_STA_MFP; } + + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { + sta->flags &= ~WLAN_STA_AUTH; + if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) + sta->flags |= WLAN_STA_AUTH; + } spin_unlock_irqrestore(&sta->flaglock, flags); /* diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index cbedfc2a42a..ce29a0d0e88 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1925,6 +1925,7 @@ static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG }, [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG }, [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG }, + [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG }, }; static int parse_station_flags(struct genl_info *info, @@ -2284,7 +2285,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) err = -EINVAL; if (params.supported_rates) err = -EINVAL; - if (params.sta_flags_mask) + if (params.sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_AUTHORIZED))) err = -EINVAL; break; default: -- cgit v1.2.3-70-g09d2 From 96b78dff0321d881ef27d858a462c476e0444619 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Thu, 7 Apr 2011 15:08:33 -0700 Subject: nl80211/mac80211: Perform PLINK_ACTION on new station Modify the NEW_STATION command to accept PLINK_ACTIONS, in case userspace wants to create stations and initiate a peer link right away (for authenticated stations) or create a blocked station (for debugging). Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/mesh_plink.c | 2 +- net/wireless/nl80211.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 5d0dd9217e5..b327e0e6c73 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -105,7 +105,7 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, if (!sta) return NULL; - sta->flags = WLAN_STA_AUTHORIZED; + sta->flags = WLAN_STA_AUTHORIZED | WLAN_STA_AUTH; sta->sta.supp_rates[local->hw.conf.channel->band] = rates; rate_control_rate_init(sta); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ce29a0d0e88..f4cb8efe2e5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2349,11 +2349,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); + if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) + params.plink_action = + nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + if (parse_station_flags(info, ¶ms)) return -EINVAL; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EINVAL; -- cgit v1.2.3-70-g09d2 From c93b5e717ec47b57abfe0229360bc11e77520984 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Thu, 7 Apr 2011 15:08:34 -0700 Subject: nl80211: New notification to discover mesh peer candidates. Notify userspace when a beacon/presp is received from a suitable mesh peer candidate for whom no sta information exists. Userspace can then decide to create a sta info for the candidate. If userspace is not ready to authenticate the peer right away, it can create the sta info with the authenticated flag unset and set it later. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- include/linux/nl80211.h | 12 ++++++++++++ include/net/cfg80211.h | 16 ++++++++++++++++ net/wireless/mesh.c | 14 ++++++++++++++ net/wireless/nl80211.c | 38 ++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 4 ++++ 5 files changed, 84 insertions(+) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 5ec4ac3a0ef..b87481866dd 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -410,6 +410,16 @@ * notification. This event is used to indicate that an unprotected * disassociation frame was dropped when MFP is in use. * + * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a + * beacon or probe response from a compatible mesh peer. This is only + * sent while no station information (sta_info) exists for the new peer + * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set. On + * reception of this notification, userspace may decide to create a new + * station (@NL80211_CMD_NEW_STATION). To stop this notification from + * reoccurring, the userspace authentication daemon may want to create the + * new station with the AUTHENTICATED flag unset and maybe change it later + * depending on the authentication result. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -522,6 +532,8 @@ enum nl80211_commands { NL80211_CMD_UNPROT_DEAUTHENTICATE, NL80211_CMD_UNPROT_DISASSOCIATE, + NL80211_CMD_NEW_PEER_CANDIDATE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e77603bd163..f40cd30847d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2488,6 +2488,22 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, */ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp); +/** + * cfg80211_notify_new_candidate - notify cfg80211 of a new mesh peer candidate + * + * @dev: network device + * @macaddr: the MAC address of the new candidate + * @ie: information elements advertised by the peer candidate + * @ie_len: lenght of the information elements buffer + * @gfp: allocation flags + * + * This function notifies cfg80211 that the mesh peer candidate has been + * detected, most likely via a beacon or, less likely, via a probe response. + * cfg80211 then sends a notification to userspace. + */ +void cfg80211_notify_new_peer_candidate(struct net_device *dev, + const u8 *macaddr, const u8 *ie, u8 ie_len, gfp_t gfp); + /** * DOC: RFkill integration * diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index e0226e8265a..5c116083eec 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -1,5 +1,6 @@ #include #include +#include "nl80211.h" #include "core.h" /* Default values, timeouts in ms */ @@ -110,6 +111,19 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, return err; } +void cfg80211_notify_new_peer_candidate(struct net_device *dev, + const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT)) + return; + + nl80211_send_new_peer_candidate(wiphy_to_dev(wdev->wiphy), dev, + macaddr, ie, ie_len, gfp); +} +EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate); + static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f4cb8efe2e5..58f501a3502 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5818,6 +5818,44 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *macaddr, const u8* ie, u8 ie_len, + gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr); + if (ie_len && ie) + NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, enum nl80211_key_type key_type, int key_id, diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index dcac5cd6f01..f2af6955a66 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -50,6 +50,10 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, const u8 *ie, size_t ie_len, bool from_ap); +void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *macaddr, const u8* ie, u8 ie_len, + gfp_t gfp); void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, -- cgit v1.2.3-70-g09d2 From ebe27c91af8b7f4810ae906fbd3eeb2d87850026 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Fri, 8 Apr 2011 21:24:24 +0530 Subject: {mac|nl}80211: Add station connected time Add station connected time in debugfs. This will be helpful to get a measure of stability of the connection and for debugging stress issues Cc: Senthilkumar Balasubramanian Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- include/linux/nl80211.h | 2 ++ include/net/cfg80211.h | 4 ++++ net/mac80211/cfg.c | 7 ++++++- net/mac80211/debugfs_sta.c | 26 ++++++++++++++++++++++++++ net/mac80211/sta_info.c | 3 +++ net/mac80211/sta_info.h | 2 ++ net/wireless/nl80211.c | 3 +++ 7 files changed, 46 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index b87481866dd..be8df57b789 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1297,6 +1297,7 @@ enum nl80211_sta_bss_param { * attribute, like NL80211_STA_INFO_TX_BITRATE. * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute * containing info as possible, see &enum nl80211_sta_bss_param + * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1317,6 +1318,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_SIGNAL_AVG, NL80211_STA_INFO_RX_BITRATE, NL80211_STA_INFO_BSS_PARAM, + NL80211_STA_INFO_CONNECTED_TIME, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f40cd30847d..d30eada7c6c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -423,6 +423,7 @@ struct station_parameters { * @STATION_INFO_SIGNAL_AVG: @signal_avg filled * @STATION_INFO_RX_BITRATE: @rxrate fields are filled * @STATION_INFO_BSS_PARAM: @bss_param filled + * @STATION_INFO_CONNECTED_TIME: @connected_time filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -441,6 +442,7 @@ enum station_info_flags { STATION_INFO_SIGNAL_AVG = 1<<13, STATION_INFO_RX_BITRATE = 1<<14, STATION_INFO_BSS_PARAM = 1<<15, + STATION_INFO_CONNECTED_TIME = 1<<16 }; /** @@ -511,6 +513,7 @@ struct sta_bss_parameters { * Station information filled by driver for get_station() and dump_station. * * @filled: bitflag of flags from &enum station_info_flags + * @connected_time: time(in secs) since a station is last connected * @inactive_time: time since last station activity (tx/rx) in milliseconds * @rx_bytes: bytes received from this station * @tx_bytes: bytes transmitted to this station @@ -533,6 +536,7 @@ struct sta_bss_parameters { */ struct station_info { u32 filled; + u32 connected_time; u32 inactive_time; u32 rx_bytes; u32 tx_bytes; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 1c25723eacd..a6d191f2a0f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -330,6 +330,7 @@ static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, in static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; + struct timespec uptime; sinfo->generation = sdata->local->sta_generation; @@ -343,7 +344,11 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) STATION_INFO_TX_BITRATE | STATION_INFO_RX_BITRATE | STATION_INFO_RX_DROP_MISC | - STATION_INFO_BSS_PARAM; + STATION_INFO_BSS_PARAM | + STATION_INFO_CONNECTED_TIME; + + do_posix_clock_monotonic_gettime(&uptime); + sinfo->connected_time = uptime.tv_sec - sta->last_connected; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); sinfo->rx_bytes = sta->rx_bytes; diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index c04a1396cf8..c008232731e 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -92,6 +92,31 @@ static ssize_t sta_inactive_ms_read(struct file *file, char __user *userbuf, } STA_OPS(inactive_ms); + +static ssize_t sta_connected_time_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct sta_info *sta = file->private_data; + struct timespec uptime; + struct tm result; + long connected_time_secs; + char buf[100]; + int res; + do_posix_clock_monotonic_gettime(&uptime); + connected_time_secs = uptime.tv_sec - sta->last_connected; + time_to_tm(connected_time_secs, 0, &result); + result.tm_year -= 70; + result.tm_mday -= 1; + res = scnprintf(buf, sizeof(buf), + "years - %d\nmonths - %d\ndays - %d\nclock - %d:%d:%d\n\n", + result.tm_year, result.tm_mon, result.tm_mday, + result.tm_hour, result.tm_min, result.tm_sec); + return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} +STA_OPS(connected_time); + + + static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -324,6 +349,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD(flags); DEBUGFS_ADD(num_ps_buf_frames); DEBUGFS_ADD(inactive_ms); + DEBUGFS_ADD(connected_time); DEBUGFS_ADD(last_seq_ctrl); DEBUGFS_ADD(agg_status); DEBUGFS_ADD(dev); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 999f8fbf0b4..8a9068ac067 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -228,6 +228,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct sta_info *sta; + struct timespec uptime; int i; sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); @@ -245,6 +246,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta->sdata = sdata; sta->last_rx = jiffies; + do_posix_clock_monotonic_gettime(&uptime); + sta->last_connected = uptime.tv_sec; ewma_init(&sta->avg_signal, 1024, 8); if (sta_prepare_rate_control(local, sta, gfp)) { diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 43238e99cfb..984a03db355 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -226,6 +226,7 @@ enum plink_state { * @rx_bytes: Number of bytes received from this STA * @wep_weak_iv_count: number of weak WEP IVs received from this station * @last_rx: time (in jiffies) when last frame was received from this STA + * @last_connected: time (in seconds) when a station got connected * @num_duplicates: number of duplicate frames received from this STA * @rx_fragments: number of received MPDUs * @rx_dropped: number of dropped MPDUs from this STA @@ -295,6 +296,7 @@ struct sta_info { unsigned long rx_packets, rx_bytes; unsigned long wep_weak_iv_count; unsigned long last_rx; + long last_connected; unsigned long num_duplicates; unsigned long rx_fragments; unsigned long rx_dropped; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 58f501a3502..0efa7fd0115 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2020,6 +2020,9 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); if (!sinfoattr) goto nla_put_failure; + if (sinfo->filled & STATION_INFO_CONNECTED_TIME) + NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME, + sinfo->connected_time); if (sinfo->filled & STATION_INFO_INACTIVE_TIME) NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME, sinfo->inactive_time); -- cgit v1.2.3-70-g09d2 From 40db6c77ab48c3f3240422ff92fd6da222e2eb95 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 21 Apr 2011 14:10:27 -0700 Subject: cfg80211: module_param to disable HT40 in 2.4GHz band Currently mac80211 uses ieee80211_disable_40mhz_24ghz module parameter to allow disabling 40MHz operation in the 2.4GHz band. Move this handling from mac80211 to cfg80211 so that the feature will be more generic. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/main.c | 18 ------------------ net/wireless/core.c | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 18 deletions(-) (limited to 'net/wireless') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0ab2a8df312..61877662e8f 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -33,12 +33,6 @@ #include "cfg.h" #include "debugfs.h" - -static bool ieee80211_disable_40mhz_24ghz; -module_param(ieee80211_disable_40mhz_24ghz, bool, 0644); -MODULE_PARM_DESC(ieee80211_disable_40mhz_24ghz, - "Disable 40MHz support in the 2.4GHz band"); - static struct lock_class_key ieee80211_rx_skb_queue_class; void ieee80211_configure_filter(struct ieee80211_local *local) @@ -728,18 +722,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) } channels += sband->n_channels; - /* - * Since ieee80211_disable_40mhz_24ghz is global, we can - * modify the sband's ht data even if the driver uses a - * global structure for that. - */ - if (ieee80211_disable_40mhz_24ghz && - band == IEEE80211_BAND_2GHZ && - sband->ht_cap.ht_supported) { - sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; - } - if (max_bitrates < sband->n_bitrates) max_bitrates = sband->n_bitrates; supp_ht = supp_ht || sband->ht_cap.ht_supported; diff --git a/net/wireless/core.c b/net/wireless/core.c index fe01de29bfe..bbf1fa11107 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -46,6 +46,11 @@ static struct dentry *ieee80211_debugfs_dir; /* for the cleanup, scan and event works */ struct workqueue_struct *cfg80211_wq; +static bool cfg80211_disable_40mhz_24ghz; +module_param(cfg80211_disable_40mhz_24ghz, bool, 0644); +MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz, + "Disable 40MHz support in the 2.4GHz band"); + /* requires cfg80211_mutex to be held! */ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) { @@ -450,6 +455,18 @@ int wiphy_register(struct wiphy *wiphy) if (WARN_ON(!sband->n_channels || !sband->n_bitrates)) return -EINVAL; + /* + * Since cfg80211_disable_40mhz_24ghz is global, we can + * modify the sband's ht data even if the driver uses a + * global structure for that. + */ + if (cfg80211_disable_40mhz_24ghz && + band == IEEE80211_BAND_2GHZ && + sband->ht_cap.ht_supported) { + sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; + } + /* * Since we use a u32 for rate bitmaps in * ieee80211_get_response_rate, we cannot -- cgit v1.2.3-70-g09d2 From c989bb15e95a93e20fc86783264f6298116e8651 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 25 Apr 2011 18:35:48 -0700 Subject: cfg80211: fix regresion on reg user timeout The patch "cfg80211: add a timer for invalid user reg hints" introduced a regression for the case where a secondary identical regulatory hint from a user is sent. What would happen is the second hint would schedule delayed work in to catch a timeout but since we are never processing it given that the hint was already applied we'd always hit the timeout and and restore regulatory settings back to world regulatory domain. This is fixed by simply avoiding sheduling work if the hint was already applied. Tested-by: Felix Fietkau Reported-by: Felix Fietkau Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2714379ce2d..8982053f996 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1455,7 +1455,8 @@ static void reg_process_hint(struct regulatory_request *reg_request) * We only time out user hints, given that they should be the only * source of bogus requests. */ - if (reg_request->initiator == NL80211_REGDOM_SET_BY_USER) + if (r != -EALREADY && + reg_request->initiator == NL80211_REGDOM_SET_BY_USER) schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); } -- cgit v1.2.3-70-g09d2 From 0e579d6a8f4aea346da818f13ee71401c125e639 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 3 May 2011 22:45:16 -0700 Subject: nl80211: Fix set_key regression with some drivers Commit dbd2fd656f2060abfd3a16257f8b51ec60f6d2ed added a mechanism for user space to indicate whether a default key is being configured for only unicast or only multicast frames instead of all frames. This commit added a driver capability flag for indicating whether separate default keys are supported and validation of the set_key command based on that capability. However, this single capability flag is not enough to cover possible difference based on mode (AP/IBSS/STA) and the way this change was introduced resulted in a regression with drivers that do not indicate the new capability (i.e.., more or less any non-mac80211 driver using cfg80211) when using a recent wpa_supplicant snapshot. Fix the regression by removing the new check which is not strictly speaking needed. The new separate default key functionality is needed only for RSN IBSS which has a separate capability indication. Cc: stable@kernel.org Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0efa7fd0115..ab77f943dc0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1682,14 +1682,6 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) if (err) goto out; - if (!(rdev->wiphy.flags & - WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS)) { - if (!key.def_uni || !key.def_multi) { - err = -EOPNOTSUPP; - goto out; - } - } - err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx, key.def_uni, key.def_multi); -- cgit v1.2.3-70-g09d2 From ff1b6e69ad4f31fb3c9c6da2665655f2e798dd70 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 4 May 2011 15:37:28 +0200 Subject: nl80211/cfg80211: WoWLAN support This is based on (but now quite far from) the original work from Luis and Eliad. Add support for configuring WoWLAN triggers, and getting the configuration out again. Changes from the original patchset are too numerous to list, but one important change needs highlighting: the suspend() callback is passed NULL for the trigger configuration if userspace has not configured WoWLAN at all. Signed-off-by: Luis R. Rodriguez Signed-off-by: Eliad Peller Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 99 ++++++++++++++++++++ include/net/cfg80211.h | 74 ++++++++++++++- net/mac80211/cfg.c | 3 +- net/wireless/core.c | 8 ++ net/wireless/core.h | 14 +++ net/wireless/nl80211.c | 243 ++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/sysfs.c | 2 +- 7 files changed, 439 insertions(+), 4 deletions(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index be8df57b789..a75dea9c416 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -420,6 +420,14 @@ * new station with the AUTHENTICATED flag unset and maybe change it later * depending on the authentication result. * + * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings. + * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings. + * Since wireless is more complex than wired ethernet, it supports + * various triggers. These triggers can be configured through this + * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For + * more background information, see + * http://wireless.kernel.org/en/users/Documentation/WoWLAN. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -534,6 +542,9 @@ enum nl80211_commands { NL80211_CMD_NEW_PEER_CANDIDATE, + NL80211_CMD_GET_WOWLAN, + NL80211_CMD_SET_WOWLAN, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -903,6 +914,13 @@ enum nl80211_commands { * allows auth frames in a mesh to be passed to userspace for processing via * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. * + * @NL80211_ATTR_WOWLAN_SUPPORTED: indicates, as part of the wiphy capabilities, + * the supported WoWLAN triggers + * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to + * indicate which WoW triggers should be enabled. This is also + * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN + * triggers. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1092,6 +1110,9 @@ enum nl80211_attrs { NL80211_ATTR_SUPPORT_MESH_AUTH, + NL80211_ATTR_WOWLAN_TRIGGERS, + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2061,4 +2082,82 @@ enum nl80211_tx_power_setting { NL80211_TX_POWER_FIXED, }; +/** + * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute + * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute + * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has + * a zero bit are ignored + * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have + * a bit for each byte in the pattern. The lowest-order bit corresponds + * to the first byte of the pattern, but the bytes of the pattern are + * in a little-endian-like format, i.e. the 9th byte of the pattern + * corresponds to the lowest-order bit in the second byte of the mask. + * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where + * xx indicates "don't care") would be represented by a pattern of + * twelve zero bytes, and a mask of "0xed,0x07". + * Note that the pattern matching is done as though frames were not + * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked + * first (including SNAP header unpacking) and then matched. + * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes + * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number + */ +enum nl80211_wowlan_packet_pattern_attr { + __NL80211_WOWLAN_PKTPAT_INVALID, + NL80211_WOWLAN_PKTPAT_MASK, + NL80211_WOWLAN_PKTPAT_PATTERN, + + NUM_NL80211_WOWLAN_PKTPAT, + MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1, +}; + +/** + * struct nl80211_wowlan_pattern_support - pattern support information + * @max_patterns: maximum number of patterns supported + * @min_pattern_len: minimum length of each pattern + * @max_pattern_len: maximum length of each pattern + * + * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when + * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the + * capability information given by the kernel to userspace. + */ +struct nl80211_wowlan_pattern_support { + __u32 max_patterns; + __u32 min_pattern_len; + __u32 max_pattern_len; +} __attribute__((packed)); + +/** + * enum nl80211_wowlan_triggers - WoWLAN trigger definitions + * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes + * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put + * the chip into a special state -- works best with chips that have + * support for low-power operation already (flag) + * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect + * is detected is implementation-specific (flag) + * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed + * by 16 repetitions of MAC addr, anywhere in payload) (flag) + * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns + * which are passed in an array of nested attributes, each nested attribute + * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern. + * Each pattern defines a wakeup packet. The matching is done on the MSDU, + * i.e. as though the packet was an 802.3 packet, so the pattern matching + * is done after the packet is converted to the MSDU. + * + * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute + * carrying a &struct nl80211_wowlan_pattern_support. + * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers + * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number + */ +enum nl80211_wowlan_triggers { + __NL80211_WOWLAN_TRIG_INVALID, + NL80211_WOWLAN_TRIG_ANY, + NL80211_WOWLAN_TRIG_DISCONNECT, + NL80211_WOWLAN_TRIG_MAGIC_PKT, + NL80211_WOWLAN_TRIG_PKT_PATTERN, + + /* keep last */ + NUM_NL80211_WOWLAN_TRIG, + MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1 +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4932dfcb72b..0920daf3680 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1087,6 +1087,38 @@ struct cfg80211_pmksa { u8 *pmkid; }; +/** + * struct cfg80211_wowlan_trig_pkt_pattern - packet pattern + * @mask: bitmask where to match pattern and where to ignore bytes, + * one bit per byte, in same format as nl80211 + * @pattern: bytes to match where bitmask is 1 + * @pattern_len: length of pattern (in bytes) + * + * Internal note: @mask and @pattern are allocated in one chunk of + * memory, free @mask only! + */ +struct cfg80211_wowlan_trig_pkt_pattern { + u8 *mask, *pattern; + int pattern_len; +}; + +/** + * struct cfg80211_wowlan - Wake on Wireless-LAN support info + * + * This structure defines the enabled WoWLAN triggers for the device. + * @any: wake up on any activity -- special trigger if device continues + * operating as normal during suspend + * @disconnect: wake up if getting disconnected + * @magic_pkt: wake up on receiving magic packet + * @patterns: wake up on receiving packet matching a pattern + * @n_patterns: number of patterns + */ +struct cfg80211_wowlan { + bool any, disconnect, magic_pkt; + struct cfg80211_wowlan_trig_pkt_pattern *patterns; + int n_patterns; +}; + /** * struct cfg80211_ops - backend description for wireless configuration * @@ -1100,7 +1132,9 @@ struct cfg80211_pmksa { * wireless extensions but this is subject to reevaluation as soon as this * code is used more widely and we have a first user without wext. * - * @suspend: wiphy device needs to be suspended + * @suspend: wiphy device needs to be suspended. The variable @wow will + * be %NULL or contain the enabled Wake-on-Wireless triggers that are + * configured for the device. * @resume: wiphy device needs to be resumed * * @add_virtual_intf: create a new virtual interface with the given name, @@ -1244,7 +1278,7 @@ struct cfg80211_pmksa { * @get_ringparam: Get tx and rx ring current and maximum sizes. */ struct cfg80211_ops { - int (*suspend)(struct wiphy *wiphy); + int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); int (*resume)(struct wiphy *wiphy); struct net_device * (*add_virtual_intf)(struct wiphy *wiphy, @@ -1479,6 +1513,38 @@ struct ieee80211_txrx_stypes { u16 tx, rx; }; +/** + * enum wiphy_wowlan_support_flags - WoWLAN support flags + * @WIPHY_WOWLAN_ANY: supports wakeup for the special "any" + * trigger that keeps the device operating as-is and + * wakes up the host on any activity, for example a + * received packet that passed filtering; note that the + * packet should be preserved in that case + * @WIPHY_WOWLAN_MAGIC_PKT: supports wakeup on magic packet + * (see nl80211.h) + * @WIPHY_WOWLAN_DISCONNECT: supports wakeup on disconnect + */ +enum wiphy_wowlan_support_flags { + WIPHY_WOWLAN_ANY = BIT(0), + WIPHY_WOWLAN_MAGIC_PKT = BIT(1), + WIPHY_WOWLAN_DISCONNECT = BIT(2), +}; + +/** + * struct wiphy_wowlan_support - WoWLAN support data + * @flags: see &enum wiphy_wowlan_support_flags + * @n_patterns: number of supported wakeup patterns + * (see nl80211.h for the pattern definition) + * @pattern_max_len: maximum length of each pattern + * @pattern_min_len: minimum length of each pattern + */ +struct wiphy_wowlan_support { + u32 flags; + int n_patterns; + int pattern_max_len; + int pattern_min_len; +}; + /** * struct wiphy - wireless hardware description * @reg_notifier: the driver's regulatory notification callback, @@ -1546,6 +1612,8 @@ struct ieee80211_txrx_stypes { * * @max_remain_on_channel_duration: Maximum time a remain-on-channel operation * may request, if implemented. + * + * @wowlan: WoWLAN support information */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -1583,6 +1651,8 @@ struct wiphy { char fw_version[ETHTOOL_BUSINFO_LEN]; u32 hw_version; + struct wiphy_wowlan_support wowlan; + u16 max_remain_on_channel_duration; u8 max_num_pmkids; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 12d52cec951..321d598eb8c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1297,7 +1297,8 @@ static int ieee80211_set_channel(struct wiphy *wiphy, } #ifdef CONFIG_PM -static int ieee80211_suspend(struct wiphy *wiphy) +static int ieee80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wowlan) { return __ieee80211_suspend(wiphy_priv(wiphy)); } diff --git a/net/wireless/core.c b/net/wireless/core.c index bbf1fa11107..bea0d80710c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -493,6 +493,13 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; } + if (rdev->wiphy.wowlan.n_patterns) { + if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len || + rdev->wiphy.wowlan.pattern_min_len > + rdev->wiphy.wowlan.pattern_max_len)) + return -EINVAL; + } + /* check and set up bitrates */ ieee80211_set_bitrate_flags(wiphy); @@ -631,6 +638,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev) mutex_destroy(&rdev->devlist_mtx); list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) cfg80211_put_bss(&scan->pub); + cfg80211_rdev_free_wowlan(rdev); kfree(rdev); } diff --git a/net/wireless/core.h b/net/wireless/core.h index 26a0a084e16..7a18c10a7fb 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -70,6 +70,8 @@ struct cfg80211_registered_device { struct work_struct conn_work; struct work_struct event_work; + struct cfg80211_wowlan *wowlan; + /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); @@ -89,6 +91,18 @@ bool wiphy_idx_valid(int wiphy_idx) return wiphy_idx >= 0; } +static inline void +cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) +{ + int i; + + if (!rdev->wowlan) + return; + for (i = 0; i < rdev->wowlan->n_patterns; i++) + kfree(rdev->wowlan->patterns[i].mask); + kfree(rdev->wowlan->patterns); + kfree(rdev->wowlan); +} extern struct workqueue_struct *cfg80211_wq; extern struct mutex cfg80211_mutex; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ab77f943dc0..0a199a1ca9b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -173,6 +173,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, + [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, }; /* policy for the key attributes */ @@ -194,6 +195,15 @@ nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = { [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, }; +/* policy for WoWLAN attributes */ +static const struct nla_policy +nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { + [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, +}; + /* ifidx get helper */ static int nl80211_get_ifidx(struct netlink_callback *cb) { @@ -821,6 +831,35 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, nla_nest_end(msg, nl_ifs); } + if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) { + struct nlattr *nl_wowlan; + + nl_wowlan = nla_nest_start(msg, + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED); + if (!nl_wowlan) + goto nla_put_failure; + + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); + if (dev->wiphy.wowlan.n_patterns) { + struct nl80211_wowlan_pattern_support pat = { + .max_patterns = dev->wiphy.wowlan.n_patterns, + .min_pattern_len = + dev->wiphy.wowlan.pattern_min_len, + .max_pattern_len = + dev->wiphy.wowlan.pattern_max_len, + }; + NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, + sizeof(pat), &pat); + } + + nla_nest_end(msg, nl_wowlan); + } + return genlmsg_end(msg, hdr); nla_put_failure: @@ -4808,6 +4847,194 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) return cfg80211_leave_mesh(rdev, dev); } +static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct sk_buff *msg; + void *hdr; + + if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) + return -EOPNOTSUPP; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_GET_WOWLAN); + if (!hdr) + goto nla_put_failure; + + if (rdev->wowlan) { + struct nlattr *nl_wowlan; + + nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); + if (!nl_wowlan) + goto nla_put_failure; + + if (rdev->wowlan->any) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); + if (rdev->wowlan->disconnect) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); + if (rdev->wowlan->magic_pkt) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); + if (rdev->wowlan->n_patterns) { + struct nlattr *nl_pats, *nl_pat; + int i, pat_len; + + nl_pats = nla_nest_start(msg, + NL80211_WOWLAN_TRIG_PKT_PATTERN); + if (!nl_pats) + goto nla_put_failure; + + for (i = 0; i < rdev->wowlan->n_patterns; i++) { + nl_pat = nla_nest_start(msg, i + 1); + if (!nl_pat) + goto nla_put_failure; + pat_len = rdev->wowlan->patterns[i].pattern_len; + NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK, + DIV_ROUND_UP(pat_len, 8), + rdev->wowlan->patterns[i].mask); + NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN, + pat_len, + rdev->wowlan->patterns[i].pattern); + nla_nest_end(msg, nl_pat); + } + nla_nest_end(msg, nl_pats); + } + + nla_nest_end(msg, nl_wowlan); + } + + genlmsg_end(msg, hdr); + return genlmsg_reply(msg, info); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + +static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG]; + struct cfg80211_wowlan no_triggers = {}; + struct cfg80211_wowlan new_triggers = {}; + struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan; + int err, i; + + if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) + goto no_triggers; + + err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG, + nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), + nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), + nl80211_wowlan_policy); + if (err) + return err; + + if (tb[NL80211_WOWLAN_TRIG_ANY]) { + if (!(wowlan->flags & WIPHY_WOWLAN_ANY)) + return -EINVAL; + new_triggers.any = true; + } + + if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) { + if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT)) + return -EINVAL; + new_triggers.disconnect = true; + } + + if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) { + if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT)) + return -EINVAL; + new_triggers.magic_pkt = true; + } + + if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { + struct nlattr *pat; + int n_patterns = 0; + int rem, pat_len, mask_len; + struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; + + nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], + rem) + n_patterns++; + if (n_patterns > wowlan->n_patterns) + return -EINVAL; + + new_triggers.patterns = kcalloc(n_patterns, + sizeof(new_triggers.patterns[0]), + GFP_KERNEL); + if (!new_triggers.patterns) + return -ENOMEM; + + new_triggers.n_patterns = n_patterns; + i = 0; + + nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], + rem) { + nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT, + nla_data(pat), nla_len(pat), NULL); + err = -EINVAL; + if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] || + !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]) + goto error; + pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]); + mask_len = DIV_ROUND_UP(pat_len, 8); + if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) != + mask_len) + goto error; + if (pat_len > wowlan->pattern_max_len || + pat_len < wowlan->pattern_min_len) + goto error; + + new_triggers.patterns[i].mask = + kmalloc(mask_len + pat_len, GFP_KERNEL); + if (!new_triggers.patterns[i].mask) { + err = -ENOMEM; + goto error; + } + new_triggers.patterns[i].pattern = + new_triggers.patterns[i].mask + mask_len; + memcpy(new_triggers.patterns[i].mask, + nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]), + mask_len); + new_triggers.patterns[i].pattern_len = pat_len; + memcpy(new_triggers.patterns[i].pattern, + nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]), + pat_len); + i++; + } + } + + if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) { + struct cfg80211_wowlan *ntrig; + ntrig = kmemdup(&new_triggers, sizeof(new_triggers), + GFP_KERNEL); + if (!ntrig) { + err = -ENOMEM; + goto error; + } + cfg80211_rdev_free_wowlan(rdev); + rdev->wowlan = ntrig; + } else { + no_triggers: + cfg80211_rdev_free_wowlan(rdev); + rdev->wowlan = NULL; + } + + return 0; + error: + for (i = 0; i < new_triggers.n_patterns; i++) + kfree(new_triggers.patterns[i].mask); + kfree(new_triggers.patterns); + return err; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -5306,6 +5533,22 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_GET_WOWLAN, + .doit = nl80211_get_wowlan, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_WOWLAN, + .doit = nl80211_set_wowlan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 4294fa22bb2..c6e4ca6a7d2 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -93,7 +93,7 @@ static int wiphy_suspend(struct device *dev, pm_message_t state) if (rdev->ops->suspend) { rtnl_lock(); - ret = rdev->ops->suspend(&rdev->wiphy); + ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); rtnl_unlock(); } -- cgit v1.2.3-70-g09d2 From 6572e91d5fa61bb3f879a00b96d763c566ced6cb Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 25 Apr 2011 15:56:16 +0530 Subject: wireless: Fix warnings due to -Wunused-but-set-variable These warnings are exposed by gcc 4.6. net/wireless/reg.c: In function 'freq_reg_info_regd': net/wireless/reg.c:675:38: warning: variable 'pr' set but not used [-Wunused-but-set-variable] net/wireless/lib80211_crypt_wep.c: In function 'lib80211_wep_build_iv': net/wireless/lib80211_crypt_wep.c:99:12: warning: variable 'len' set but not used [-Wunused-but-set-variable] Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- net/wireless/lib80211_crypt_wep.c | 3 +-- net/wireless/reg.c | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/lib80211_crypt_wep.c b/net/wireless/lib80211_crypt_wep.c index e2e88878ba3..2f265e033ae 100644 --- a/net/wireless/lib80211_crypt_wep.c +++ b/net/wireless/lib80211_crypt_wep.c @@ -96,13 +96,12 @@ static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len, u8 *key, int keylen, void *priv) { struct lib80211_wep_data *wep = priv; - u32 klen, len; + u32 klen; u8 *pos; if (skb_headroom(skb) < 4 || skb->len < hdr_len) return -1; - len = skb->len - hdr_len; pos = skb_push(skb, 4); memmove(pos, pos + 4, hdr_len); pos += hdr_len; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 8982053f996..798cb4cd070 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -672,11 +672,9 @@ static int freq_reg_info_regd(struct wiphy *wiphy, for (i = 0; i < regd->n_reg_rules; i++) { const struct ieee80211_reg_rule *rr; const struct ieee80211_freq_range *fr = NULL; - const struct ieee80211_power_rule *pr = NULL; rr = ®d->reg_rules[i]; fr = &rr->freq_range; - pr = &rr->power_rule; /* * We only need to know if one frequency rule was -- cgit v1.2.3-70-g09d2 From b130e5cec958bae3867cf6ab09a9b24ba8fada01 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Tue, 3 May 2011 16:57:07 -0700 Subject: nl80211: Introduce NL80211_MESH_SETUP_USERSPACE_AMPE Introduce a new configuration option to support AMPE from userspace. Prior to this series we only supported authentication in userspace: an authentication daemon would authenticate peer candidates in userspace and hand them over to the kernel. From that point the mesh stack would take over and establish a peer link (Mesh Peering Management). These patches introduce support for Authenticated Mesh Peering Exchange in userspace. The userspace daemon implements the AMPE protocol and on successfull completion create mesh peers and install encryption keys. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- include/linux/nl80211.h | 10 ++++++++++ include/net/cfg80211.h | 4 +++- net/mac80211/cfg.c | 6 +++++- net/mac80211/ieee80211_i.h | 6 +++++- net/mac80211/mesh.c | 2 +- net/mac80211/mesh_plink.c | 5 +++-- net/wireless/nl80211.c | 4 +++- 7 files changed, 30 insertions(+), 7 deletions(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index a75dea9c416..c53b916036c 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1769,6 +1769,15 @@ enum nl80211_meshconf_params { * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication * daemon will be authenticating mesh candidates. * + * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication + * daemon will be securing peer link frames. AMPE is a secured version of Mesh + * Peering Management (MPM) and is implemented with the assistance of a + * userspace daemon. When this flag is set, the kernel will send peer + * management frames to a userspace daemon that will implement AMPE + * functionality (security capabilities selection, key confirmation, and key + * management). When the flag is unset (default), the kernel can autonomously + * complete (unsecured) mesh peering without the need of a userspace daemon. + * * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use */ @@ -1778,6 +1787,7 @@ enum nl80211_mesh_setup_params { NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC, NL80211_MESH_SETUP_IE, NL80211_MESH_SETUP_USERSPACE_AUTH, + NL80211_MESH_SETUP_USERSPACE_AMPE, /* keep last */ __NL80211_MESH_SETUP_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0920daf3680..10c17d68059 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -695,7 +695,8 @@ struct mesh_config { * @path_metric: which metric to use * @ie: vendor information elements (optional) * @ie_len: length of vendor information elements - * @is_secure: or not + * @is_authenticated: this mesh requires authentication + * @is_secure: this mesh uses security * * These parameters are fixed when the mesh is created. */ @@ -706,6 +707,7 @@ struct mesh_setup { u8 path_metric; const u8 *ie; u8 ie_len; + bool is_authenticated; bool is_secure; }; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 1ebc13383ae..18c2555e04e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1064,7 +1064,11 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len); ifmsh->mesh_pp_id = setup->path_sel_proto; ifmsh->mesh_pm_id = setup->path_metric; - ifmsh->is_secure = setup->is_secure; + ifmsh->security = IEEE80211_MESH_SEC_NONE; + if (setup->is_authenticated) + ifmsh->security |= IEEE80211_MESH_SEC_AUTHED; + if (setup->is_secure) + ifmsh->security |= IEEE80211_MESH_SEC_SECURED; return 0; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e89bc27f8dc..7f4d0dc1d53 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -490,7 +490,11 @@ struct ieee80211_if_mesh { bool accepting_plinks; const u8 *ie; u8 ie_len; - bool is_secure; + enum { + IEEE80211_MESH_SEC_NONE = 0x0, + IEEE80211_MESH_SEC_AUTHED = 0x1, + IEEE80211_MESH_SEC_SECURED = 0x2, + } security; }; #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index c1299e24954..2a59eb34513 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -574,7 +574,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, &elems); /* ignore beacons from secure mesh peers if our security is off */ - if (elems.rsn_len && !sdata->u.mesh.is_secure) + if (elems.rsn_len && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) return; if (elems.ds_params && elems.ds_params_len == 1) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 84e5b056af0..87abf8deb36 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -251,7 +251,7 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, rcu_read_unlock(); /* Userspace handles peer allocation when security is enabled * */ - if (sdata->u.mesh.is_secure) + if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr, elems->ie_start, elems->total_len, GFP_KERNEL); @@ -460,7 +460,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m mpl_dbg("Mesh plink: missing necessary peer link ie\n"); return; } - if (elems.rsn_len && !sdata->u.mesh.is_secure) { + if (elems.rsn_len && + sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { mpl_dbg("Mesh plink: can't establish link with secure peer\n"); return; } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0a199a1ca9b..64efc2d7a7a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2871,6 +2871,7 @@ static const struct nla_policy [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG }, }; static int nl80211_parse_mesh_config(struct genl_info *info, @@ -2980,7 +2981,8 @@ static int nl80211_parse_mesh_setup(struct genl_info *info, setup->ie = nla_data(ieattr); setup->ie_len = nla_len(ieattr); } - setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); + setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); + setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]); return 0; } -- cgit v1.2.3-70-g09d2 From 0778a6a3e56cabdc322755f97ad23ee67efad0f0 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Tue, 3 May 2011 16:57:08 -0700 Subject: mac80211: Let userspace send action frames over mesh interfaces Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/wireless/mlme.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 16881fea4ce..493b939970c 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -963,6 +963,16 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, if (memcmp(mgmt->bssid, dev->dev_addr, ETH_ALEN)) err = -EINVAL; break; + case NL80211_IFTYPE_MESH_POINT: + if (memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN)) { + err = -EINVAL; + break; + } + /* + * check for mesh DA must be done by driver as + * cfg80211 doesn't track the stations + */ + break; default: err = -EOPNOTSUPP; break; -- cgit v1.2.3-70-g09d2 From 0a35d36d6f019bde6c98812456798275b02e5aee Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Wed, 4 May 2011 10:24:56 -0700 Subject: cfg80211: Use capability info to detect mesh beacons. Mesh beacons no longer use all-zeroes BSSID. Beacon frames for MBSS, infrastructure BSS, or IBSS are differentiated by the Capability Information field in the Beacon frame. A mesh STA sets the ESS and IBSS subfields to 0 in transmitted Beacon or Probe Response management frames. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 5 +++++ net/wireless/scan.c | 14 +++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'net/wireless') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index ee1c96a866c..d527fb7bd67 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1002,6 +1002,11 @@ struct ieee80211_ht_info { #define WLAN_CAPABILITY_ESS (1<<0) #define WLAN_CAPABILITY_IBSS (1<<1) + +/* A mesh STA sets the ESS and IBSS capability bits to zero */ +#define WLAN_CAPABILITY_IS_MBSS(cap) \ + (!((cap) & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS))) + #define WLAN_CAPABILITY_CF_POLLABLE (1<<2) #define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3) #define WLAN_CAPABILITY_PRIVACY (1<<4) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index fbf6f33ae4d..62e542a2b19 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -210,7 +210,7 @@ static bool is_mesh(struct cfg80211_bss *a, { const u8 *ie; - if (!is_zero_ether_addr(a->bssid)) + if (!WLAN_CAPABILITY_IS_MBSS(a->capability)) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_ID, @@ -248,11 +248,7 @@ static int cmp_bss(struct cfg80211_bss *a, if (a->channel != b->channel) return b->channel->center_freq - a->channel->center_freq; - r = memcmp(a->bssid, b->bssid, ETH_ALEN); - if (r) - return r; - - if (is_zero_ether_addr(a->bssid)) { + if (WLAN_CAPABILITY_IS_MBSS(a->capability | b->capability)) { r = cmp_ies(WLAN_EID_MESH_ID, a->information_elements, a->len_information_elements, @@ -267,6 +263,10 @@ static int cmp_bss(struct cfg80211_bss *a, b->len_information_elements); } + r = memcmp(a->bssid, b->bssid, ETH_ALEN); + if (r) + return r; + return cmp_ies(WLAN_EID_SSID, a->information_elements, a->len_information_elements, @@ -407,7 +407,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, res->ts = jiffies; - if (is_zero_ether_addr(res->pub.bssid)) { + if (WLAN_CAPABILITY_IS_MBSS(res->pub.capability)) { /* must be mesh, verify */ meshid = cfg80211_find_ie(WLAN_EID_MESH_ID, res->pub.information_elements, -- cgit v1.2.3-70-g09d2 From 9c3990aaec0ad9f686ef6480f6861f2df89b2a7a Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Tue, 3 May 2011 16:57:11 -0700 Subject: nl80211: Let userspace drive the peer link management states. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- include/linux/nl80211.h | 4 ++++ include/net/cfg80211.h | 29 +++++++++++++++++++++++++++++ net/mac80211/cfg.c | 30 +++++++++++++++++++++--------- net/mac80211/sta_info.h | 23 ----------------------- net/wireless/nl80211.c | 6 ++++++ 5 files changed, 60 insertions(+), 32 deletions(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index c53b916036c..de96783954a 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -913,6 +913,9 @@ enum nl80211_commands { * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver * allows auth frames in a mesh to be passed to userspace for processing via * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. + * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link. Used when + * userspace is driving the peer link management state machine. + * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled. * * @NL80211_ATTR_WOWLAN_SUPPORTED: indicates, as part of the wiphy capabilities, * the supported WoWLAN triggers @@ -1109,6 +1112,7 @@ enum nl80211_attrs { NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, NL80211_ATTR_SUPPORT_MESH_AUTH, + NL80211_ATTR_STA_PLINK_STATE, NL80211_ATTR_WOWLAN_TRIGGERS, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 10c17d68059..4b0d035be64 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -371,6 +371,33 @@ enum plink_actions { PLINK_ACTION_BLOCK, }; +/** + * enum plink_states - state of a mesh peer link finite state machine + * + * @PLINK_LISTEN: initial state, considered the implicit state of non + * existant mesh peer links + * @PLINK_OPN_SNT: mesh plink open frame has been sent to this mesh + * peer @PLINK_OPN_RCVD: mesh plink open frame has been received from + * this mesh peer + * @PLINK_CNF_RCVD: mesh plink confirm frame has been received from + * this mesh peer + * @PLINK_ESTAB: mesh peer link is established + * @PLINK_HOLDING: mesh peer link is being closed or cancelled + * @PLINK_BLOCKED: all frames transmitted from this mesh plink are + * discarded + * @PLINK_INVALID: reserved + */ +enum plink_state { + PLINK_LISTEN, + PLINK_OPN_SNT, + PLINK_OPN_RCVD, + PLINK_CNF_RCVD, + PLINK_ESTAB, + PLINK_HOLDING, + PLINK_BLOCKED, + PLINK_INVALID, +}; + /** * struct station_parameters - station parameters * @@ -387,6 +414,7 @@ enum plink_actions { * @listen_interval: listen interval or -1 for no change * @aid: AID or zero for no change * @plink_action: plink action to take + * @plink_state: set the peer link state for a station * @ht_capa: HT capabilities of station */ struct station_parameters { @@ -397,6 +425,7 @@ struct station_parameters { u16 aid; u8 supported_rates_len; u8 plink_action; + u8 plink_state; struct ieee80211_ht_cap *ht_capa; }; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 18c2555e04e..51f775772d9 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -734,15 +734,27 @@ static void sta_apply_parameters(struct ieee80211_local *local, params->ht_capa, &sta->sta.ht_cap); - if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) { - switch (params->plink_action) { - case PLINK_ACTION_OPEN: - mesh_plink_open(sta); - break; - case PLINK_ACTION_BLOCK: - mesh_plink_block(sta); - break; - } + if (ieee80211_vif_is_mesh(&sdata->vif)) { + if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) + switch (params->plink_state) { + case PLINK_LISTEN: + case PLINK_ESTAB: + case PLINK_BLOCKED: + sta->plink_state = params->plink_state; + break; + default: + /* nothing */ + break; + } + else + switch (params->plink_action) { + case PLINK_ACTION_OPEN: + mesh_plink_open(sta); + break; + case PLINK_ACTION_BLOCK: + mesh_plink_block(sta); + break; + } } } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index af1a7f8c867..f00b4dcb49d 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -173,29 +173,6 @@ struct sta_ampdu_mlme { }; -/** - * enum plink_state - state of a mesh peer link finite state machine - * - * @PLINK_LISTEN: initial state, considered the implicit state of non existant - * mesh peer links - * @PLINK_OPN_SNT: mesh plink open frame has been sent to this mesh peer - * @PLINK_OPN_RCVD: mesh plink open frame has been received from this mesh peer - * @PLINK_CNF_RCVD: mesh plink confirm frame has been received from this mesh - * peer - * @PLINK_ESTAB: mesh peer link is established - * @PLINK_HOLDING: mesh peer link is being closed or cancelled - * @PLINK_BLOCKED: all frames transmitted from this mesh plink are discarded - */ -enum plink_state { - PLINK_LISTEN, - PLINK_OPN_SNT, - PLINK_OPN_RCVD, - PLINK_CNF_RCVD, - PLINK_ESTAB, - PLINK_HOLDING, - PLINK_BLOCKED -}; - /** * struct sta_info - STA information * diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 64efc2d7a7a..f698c1d116e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -174,6 +174,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, + [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 }, }; /* policy for the key attributes */ @@ -2247,6 +2248,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); params.listen_interval = -1; + params.plink_state = PLINK_INVALID; if (info->attrs[NL80211_ATTR_STA_AID]) return -EINVAL; @@ -2278,6 +2280,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) params.plink_action = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) + params.plink_state = + nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); + err = get_vlan(info, rdev, ¶ms.vlan); if (err) goto out; -- cgit v1.2.3-70-g09d2 From ff973af74aa6932ca4758266bccec68e8135ddf7 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Tue, 3 May 2011 16:57:12 -0700 Subject: nl80211: allow installing keys for a meshif Signed-off-by: Thomas Pedersen Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 5 ++++- net/wireless/nl80211.c | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 51f775772d9..c416cce5e1e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -136,7 +136,10 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, mutex_lock(&sdata->local->sta_mtx); if (mac_addr) { - sta = sta_info_get_bss(sdata, mac_addr); + if (ieee80211_vif_is_mesh(&sdata->vif)) + sta = sta_info_get(sdata, mac_addr); + else + sta = sta_info_get_bss(sdata, mac_addr); if (!sta) { ieee80211_key_free(sdata->local, key); err = -ENOENT; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f698c1d116e..b454f8960a0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -545,6 +545,7 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_MESH_POINT: break; case NL80211_IFTYPE_ADHOC: if (!wdev->current_bss) -- cgit v1.2.3-70-g09d2 From 8429828ec96b66b6861e9fabebec007e9e132370 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Tue, 3 May 2011 16:57:13 -0700 Subject: nl80211: allow setting MFP flag for a meshif Signed-off-by: Thomas Pedersen Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b454f8960a0..fc3c95d9254 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2328,6 +2328,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) err = -EINVAL; if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_MFP) | BIT(NL80211_STA_FLAG_AUTHORIZED))) err = -EINVAL; break; -- cgit v1.2.3-70-g09d2 From 1833d81a1229adeab98f275b47624d66462561e5 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Tue, 3 May 2011 16:57:18 -0700 Subject: mac80211: allow setting supported rates on mesh peers This is necessary for userspace managed stations. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index fc3c95d9254..95dd5832e71 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2324,8 +2324,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) err = -EINVAL; if (params.listen_interval >= 0) err = -EINVAL; - if (params.supported_rates) - err = -EINVAL; if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | BIT(NL80211_STA_FLAG_MFP) | -- cgit v1.2.3-70-g09d2 From 807f8a8c300435d5483e8d78df9dcdbc27333166 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 11 May 2011 17:09:35 +0300 Subject: cfg80211/nl80211: add support for scheduled scans Implement new functionality for scheduled scan offload. With this feature we can scan automatically at certain intervals. The idea is that the hardware can perform scan automatically and filter on desired results without waking up the host unnecessarily. Add NL80211_CMD_START_SCHED_SCAN and NL80211_CMD_STOP_SCHED_SCAN commands to the nl80211 interface. When results are available they are reported by NL80211_CMD_SCHED_SCAN_RESULTS events. The userspace is informed when the scheduled scan has stopped with a NL80211_CMD_SCHED_SCAN_STOPPED event, which can be triggered either by the driver or by a call to NL80211_CMD_STOP_SCHED_SCAN. Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- include/linux/nl80211.h | 25 +++++ include/net/cfg80211.h | 57 +++++++++++ net/wireless/core.c | 12 ++- net/wireless/core.h | 7 ++ net/wireless/nl80211.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 4 + net/wireless/scan.c | 70 ++++++++++++++ 7 files changed, 424 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index de96783954a..f8b5595ba4a 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -203,6 +203,26 @@ * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, * partial scan results may be available * + * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan. Like with normal + * scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS) are passed, they are used + * in the probe requests. For broadcast, a broadcast SSID must be + * passed (ie. an empty string). If no SSID is passed, no probe + * requests are sent and a passive scan is performed. + * %NL80211_ATTR_SCAN_FREQUENCIES, if passed, define which channels + * should be scanned; if not passed, all channels allowed for the + * current regulatory domain are used. Extra IEs can also be passed + * from the userspace by using the %NL80211_ATTR_IE attribute. + * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan + * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan + * results available. + * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has + * stopped. The driver may issue this event at any time during a + * scheduled scan. One reason for stopping the scan is if the hardware + * does not support starting an association or a normal scan while running + * a scheduled scan. This event is also sent when the + * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface + * is brought down while a scheduled scan was running. + * * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation * or noise level * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to @@ -545,6 +565,11 @@ enum nl80211_commands { NL80211_CMD_GET_WOWLAN, NL80211_CMD_SET_WOWLAN, + NL80211_CMD_START_SCHED_SCAN, + NL80211_CMD_STOP_SCHED_SCAN, + NL80211_CMD_SCHED_SCAN_RESULTS, + NL80211_CMD_SCHED_SCAN_STOPPED, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4b0d035be64..e214c85b74d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -823,6 +823,33 @@ struct cfg80211_scan_request { struct ieee80211_channel *channels[0]; }; +/** + * struct cfg80211_sched_scan_request - scheduled scan request description + * + * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans) + * @n_ssids: number of SSIDs + * @n_channels: total number of channels to scan + * @ie: optional information element(s) to add into Probe Request or %NULL + * @ie_len: length of ie in octets + * @wiphy: the wiphy this was for + * @dev: the interface + * @channels: channels to scan + */ +struct cfg80211_sched_scan_request { + struct cfg80211_ssid *ssids; + int n_ssids; + u32 n_channels; + const u8 *ie; + size_t ie_len; + + /* internal */ + struct wiphy *wiphy; + struct net_device *dev; + + /* keep last */ + struct ieee80211_channel *channels[0]; +}; + /** * enum cfg80211_signal_type - signal type * @@ -1292,6 +1319,10 @@ struct cfg80211_wowlan { * @set_power_mgmt: Configure WLAN power management. A timeout value of -1 * allows the driver to adjust the dynamic ps timeout value. * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold. + * @sched_scan_start: Tell the driver to start a scheduled scan. + * @sched_scan_stop: Tell the driver to stop an ongoing scheduled + * scan. The driver_initiated flag specifies whether the driver + * itself has informed that the scan has stopped. * * @mgmt_frame_register: Notify driver that a management frame type was * registered. Note that this callback may not sleep, and cannot run @@ -1478,6 +1509,12 @@ struct cfg80211_ops { int (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx); void (*get_ringparam)(struct wiphy *wiphy, u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); + + int (*sched_scan_start)(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request); + int (*sched_scan_stop)(struct wiphy *wiphy, struct net_device *dev, + bool driver_initiated); }; /* @@ -1522,6 +1559,7 @@ struct cfg80211_ops { * @WIPHY_FLAG_IBSS_RSN: The device supports IBSS RSN. * @WIPHY_FLAG_MESH_AUTH: The device supports mesh authentication by routing * auth frames to userspace. See @NL80211_MESH_SETUP_USERSPACE_AUTH. + * @WIPHY_FLAG_SCHED_SCAN: The device supports scheduled scans. */ enum wiphy_flags { WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), @@ -1534,6 +1572,7 @@ enum wiphy_flags { WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), WIPHY_FLAG_IBSS_RSN = BIT(8), WIPHY_FLAG_MESH_AUTH = BIT(10), + WIPHY_FLAG_SUPPORTS_SCHED_SCAN = BIT(11), }; struct mac_address { @@ -2354,6 +2393,24 @@ int cfg80211_wext_siwpmksa(struct net_device *dev, */ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted); +/** + * cfg80211_sched_scan_results - notify that new scan results are available + * + * @wiphy: the wiphy which got scheduled scan results + */ +void cfg80211_sched_scan_results(struct wiphy *wiphy); + +/** + * cfg80211_sched_scan_stopped - notify that the scheduled scan has stopped + * + * @wiphy: the wiphy on which the scheduled scan stopped + * + * The driver can call this function to inform cfg80211 that the + * scheduled scan had to be stopped, for whatever reason. The driver + * is then called back via the sched_scan_stop operation when done. + */ +void cfg80211_sched_scan_stopped(struct wiphy *wiphy); + /** * cfg80211_inform_bss_frame - inform cfg80211 of a received BSS frame * diff --git a/net/wireless/core.c b/net/wireless/core.c index bea0d80710c..f924a49b242 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -370,7 +370,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) spin_lock_init(&rdev->bss_lock); INIT_LIST_HEAD(&rdev->bss_list); INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); - + INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); + INIT_WORK(&rdev->sched_scan_stopped_wk, __cfg80211_sched_scan_stopped); #ifdef CONFIG_CFG80211_WEXT rdev->wiphy.wext = &cfg80211_wext_handler; #endif @@ -672,6 +673,11 @@ static void wdev_cleanup_work(struct work_struct *work) ___cfg80211_scan_done(rdev, true); } + if (WARN_ON(rdev->sched_scan_req && + rdev->sched_scan_req->dev == wdev->netdev)) { + __cfg80211_stop_sched_scan(rdev, false); + } + cfg80211_unlock_rdev(rdev); mutex_lock(&rdev->devlist_mtx); @@ -759,6 +765,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: + cfg80211_lock_rdev(rdev); + __cfg80211_stop_sched_scan(rdev, false); + cfg80211_unlock_rdev(rdev); + wdev_lock(wdev); #ifdef CONFIG_CFG80211_WEXT kfree(wdev->wext.ie); diff --git a/net/wireless/core.h b/net/wireless/core.h index 7a18c10a7fb..e3f7b1d995c 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -60,8 +60,11 @@ struct cfg80211_registered_device { struct rb_root bss_tree; u32 bss_generation; struct cfg80211_scan_request *scan_req; /* protected by RTNL */ + struct cfg80211_sched_scan_request *sched_scan_req; unsigned long suspend_at; struct work_struct scan_done_wk; + struct work_struct sched_scan_results_wk; + struct work_struct sched_scan_stopped_wk; #ifdef CONFIG_NL80211_TESTMODE struct genl_info *testmode_info; @@ -411,6 +414,10 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); +void __cfg80211_sched_scan_results(struct work_struct *wk); +int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, + bool driver_initiated); +void __cfg80211_sched_scan_stopped(struct work_struct *wk); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); int cfg80211_change_iface(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype ntype, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 95dd5832e71..4fac370284c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -761,6 +761,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, } CMD(set_channel, SET_CHANNEL); CMD(set_wds_peer, SET_WDS_PEER); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) + CMD(sched_scan_start, START_SCHED_SCAN); #undef CMD @@ -3357,6 +3359,179 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) return err; } +static int nl80211_start_sched_scan(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_sched_scan_request *request; + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct cfg80211_ssid *ssid; + struct ieee80211_channel *channel; + struct nlattr *attr; + struct wiphy *wiphy; + int err, tmp, n_ssids = 0, n_channels, i; + enum ieee80211_band band; + size_t ie_len; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || + !rdev->ops->sched_scan_start) + return -EOPNOTSUPP; + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (rdev->sched_scan_req) + return -EINPROGRESS; + + wiphy = &rdev->wiphy; + + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + n_channels = validate_scan_freqs( + info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); + if (!n_channels) + return -EINVAL; + } else { + n_channels = 0; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + } + + if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], + tmp) + n_ssids++; + + if (n_ssids > wiphy->max_scan_ssids) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_IE]) + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + else + ie_len = 0; + + if (ie_len > wiphy->max_scan_ie_len) + return -EINVAL; + + request = kzalloc(sizeof(*request) + + sizeof(*ssid) * n_ssids + + sizeof(channel) * n_channels + + ie_len, GFP_KERNEL); + if (!request) + return -ENOMEM; + + if (n_ssids) + request->ssids = (void *)&request->channels[n_channels]; + request->n_ssids = n_ssids; + if (ie_len) { + if (request->ssids) + request->ie = (void *)(request->ssids + n_ssids); + else + request->ie = (void *)(request->channels + n_channels); + } + + i = 0; + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + /* user specified, bail out if channel not found */ + nla_for_each_nested(attr, + info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], + tmp) { + struct ieee80211_channel *chan; + + chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); + + if (!chan) { + err = -EINVAL; + goto out_free; + } + + /* ignore disabled channels */ + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + request->channels[i] = chan; + i++; + } + } else { + /* all channels */ + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + if (!wiphy->bands[band]) + continue; + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + struct ieee80211_channel *chan; + + chan = &wiphy->bands[band]->channels[j]; + + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + request->channels[i] = chan; + i++; + } + } + } + + if (!i) { + err = -EINVAL; + goto out_free; + } + + request->n_channels = i; + + i = 0; + if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], + tmp) { + if (request->ssids[i].ssid_len > + IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out_free; + } + memcpy(request->ssids[i].ssid, nla_data(attr), + nla_len(attr)); + request->ssids[i].ssid_len = nla_len(attr); + i++; + } + } + + if (info->attrs[NL80211_ATTR_IE]) { + request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + memcpy((void *)request->ie, + nla_data(info->attrs[NL80211_ATTR_IE]), + request->ie_len); + } + + request->dev = dev; + request->wiphy = &rdev->wiphy; + + err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); + if (!err) { + rdev->sched_scan_req = request; + nl80211_send_sched_scan(rdev, dev, + NL80211_CMD_START_SCHED_SCAN); + goto out; + } + +out_free: + kfree(request); +out: + return err; +} + +static int nl80211_stop_sched_scan(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || + !rdev->ops->sched_scan_stop) + return -EOPNOTSUPP; + + return __cfg80211_stop_sched_scan(rdev, false); +} + static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, @@ -5326,6 +5501,22 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .dumpit = nl80211_dump_scan, }, + { + .cmd = NL80211_CMD_START_SCHED_SCAN, + .doit = nl80211_start_sched_scan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_STOP_SCHED_SCAN, + .doit = nl80211_stop_sched_scan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, { .cmd = NL80211_CMD_AUTHENTICATE, .doit = nl80211_authenticate, @@ -5652,6 +5843,28 @@ static int nl80211_send_scan_msg(struct sk_buff *msg, return -EMSGSIZE; } +static int +nl80211_send_sched_scan_msg(struct sk_buff *msg, + struct cfg80211_registered_device *rdev, + struct net_device *netdev, + u32 pid, u32 seq, int flags, u32 cmd) +{ + void *hdr; + + hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, struct net_device *netdev) { @@ -5709,6 +5922,43 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, nl80211_scan_mcgrp.id, GFP_KERNEL); } +void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_SCHED_SCAN_RESULTS) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); +} + +void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u32 cmd) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); +} + /* * This can happen on global regulatory changes or device specific settings * based on custom world regulatory domains. diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index f2af6955a66..2f1bfb87a65 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -12,6 +12,10 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, struct net_device *netdev); void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, struct net_device *netdev); +void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u32 cmd); +void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, + struct net_device *netdev); void nl80211_send_reg_change_event(struct regulatory_request *request); void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, struct net_device *netdev, diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 62e542a2b19..65dfae3b9d4 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -93,6 +93,76 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) } EXPORT_SYMBOL(cfg80211_scan_done); +void __cfg80211_sched_scan_results(struct work_struct *wk) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(wk, struct cfg80211_registered_device, + sched_scan_results_wk); + + cfg80211_lock_rdev(rdev); + + /* we don't have sched_scan_req anymore if the scan is stopping */ + if (rdev->sched_scan_req) + nl80211_send_sched_scan_results(rdev, + rdev->sched_scan_req->dev); + + cfg80211_unlock_rdev(rdev); +} + +void cfg80211_sched_scan_results(struct wiphy *wiphy) +{ + /* ignore if we're not scanning */ + if (wiphy_to_dev(wiphy)->sched_scan_req) + queue_work(cfg80211_wq, + &wiphy_to_dev(wiphy)->sched_scan_results_wk); +} +EXPORT_SYMBOL(cfg80211_sched_scan_results); + +void __cfg80211_sched_scan_stopped(struct work_struct *wk) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(wk, struct cfg80211_registered_device, + sched_scan_stopped_wk); + + cfg80211_lock_rdev(rdev); + __cfg80211_stop_sched_scan(rdev, true); + cfg80211_unlock_rdev(rdev); +} + +void cfg80211_sched_scan_stopped(struct wiphy *wiphy) +{ + queue_work(cfg80211_wq, &wiphy_to_dev(wiphy)->sched_scan_stopped_wk); +} +EXPORT_SYMBOL(cfg80211_sched_scan_stopped); + +int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, + bool driver_initiated) +{ + int err; + struct net_device *dev; + + ASSERT_RDEV_LOCK(rdev); + + if (!rdev->sched_scan_req) + return 0; + + dev = rdev->sched_scan_req->dev; + + err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev, + driver_initiated); + if (err) + return err; + + nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); + + kfree(rdev->sched_scan_req); + rdev->sched_scan_req = NULL; + + return err; +} + static void bss_release(struct kref *ref) { struct cfg80211_internal_bss *bss; -- cgit v1.2.3-70-g09d2 From bbe6ad6dcb1eb26bd12ec85320f402721c3383ae Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 11 May 2011 17:09:37 +0300 Subject: cfg80211/nl80211: add interval attribute for scheduled scans Introduce NL80211_ATTR_SCHED_SCAN_INTERVAL as a required attribute for NL80211_CMD_START_SCHED_SCAN. This value informs the driver at which intervals the scheduled scan cycles should be executed. Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- include/linux/nl80211.h | 25 ++++++++++++++++--------- include/net/cfg80211.h | 2 ++ net/wireless/nl80211.c | 10 ++++++++++ 3 files changed, 28 insertions(+), 9 deletions(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index f8b5595ba4a..281a2bb6a6e 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -203,15 +203,17 @@ * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, * partial scan results may be available * - * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan. Like with normal - * scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS) are passed, they are used - * in the probe requests. For broadcast, a broadcast SSID must be - * passed (ie. an empty string). If no SSID is passed, no probe - * requests are sent and a passive scan is performed. - * %NL80211_ATTR_SCAN_FREQUENCIES, if passed, define which channels - * should be scanned; if not passed, all channels allowed for the - * current regulatory domain are used. Extra IEs can also be passed - * from the userspace by using the %NL80211_ATTR_IE attribute. + * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain + * intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL. + * Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS) + * are passed, they are used in the probe requests. For + * broadcast, a broadcast SSID must be passed (ie. an empty + * string). If no SSID is passed, no probe requests are sent and + * a passive scan is performed. %NL80211_ATTR_SCAN_FREQUENCIES, + * if passed, define which channels should be scanned; if not + * passed, all channels allowed for the current regulatory domain + * are used. Extra IEs can also be passed from the userspace by + * using the %NL80211_ATTR_IE attribute. * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan * results available. @@ -948,6 +950,9 @@ enum nl80211_commands { * indicate which WoW triggers should be enabled. This is also * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN * triggers. + + * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan + * cycles, in msecs. * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -1142,6 +1147,8 @@ enum nl80211_attrs { NL80211_ATTR_WOWLAN_TRIGGERS, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, + NL80211_ATTR_SCHED_SCAN_INTERVAL, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e214c85b74d..1f1e221b6ce 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -829,6 +829,7 @@ struct cfg80211_scan_request { * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans) * @n_ssids: number of SSIDs * @n_channels: total number of channels to scan + * @interval: interval between each scheduled scan cycle * @ie: optional information element(s) to add into Probe Request or %NULL * @ie_len: length of ie in octets * @wiphy: the wiphy this was for @@ -839,6 +840,7 @@ struct cfg80211_sched_scan_request { struct cfg80211_ssid *ssids; int n_ssids; u32 n_channels; + u32 interval; const u8 *ie; size_t ie_len; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4fac370284c..b5b050b62f2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -175,6 +175,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 }, + [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, }; /* policy for the key attributes */ @@ -3370,6 +3371,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, struct nlattr *attr; struct wiphy *wiphy; int err, tmp, n_ssids = 0, n_channels, i; + u32 interval; enum ieee80211_band band; size_t ie_len; @@ -3383,6 +3385,13 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, if (rdev->sched_scan_req) return -EINPROGRESS; + if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) + return -EINVAL; + + interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]); + if (interval == 0) + return -EINVAL; + wiphy = &rdev->wiphy; if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { @@ -3505,6 +3514,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, request->dev = dev; request->wiphy = &rdev->wiphy; + request->interval = interval; err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); if (!err) { -- cgit v1.2.3-70-g09d2 From 56d1893d94bc06d0b1aa3a53f924ed02f9e207bf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 9 May 2011 18:41:15 +0200 Subject: cfg80211: restrict AP beacon intervals Multiple virtual AP interfaces can currently try to use different beacon intervals, but that just leads to problems since it won't actually be done that way by drivers. Return an error in this case to make sure it won't be done wrong. Also, ignore attempts to change the DTIM period or beacon interval during the lifetime of the BSS. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 4 ++++ net/wireless/core.c | 1 + net/wireless/core.h | 3 +++ net/wireless/nl80211.c | 40 +++++++++++++++++++++++----------------- net/wireless/util.c | 25 +++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 17 deletions(-) (limited to 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1f1e221b6ce..a46adb7a91b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1908,6 +1908,8 @@ struct cfg80211_cached_keys; * @mgmt_registrations_lock: lock for the list * @mtx: mutex used to lock data in this struct * @cleanup_work: work struct used for cleanup that can't be done directly + * @beacon_interval: beacon interval used on this device for transmitting + * beacons, 0 when not valid */ struct wireless_dev { struct wiphy *wiphy; @@ -1948,6 +1950,8 @@ struct wireless_dev { bool ps; int ps_timeout; + int beacon_interval; + #ifdef CONFIG_CFG80211_WEXT /* wext data */ struct { diff --git a/net/wireless/core.c b/net/wireless/core.c index f924a49b242..e2ab65d7c86 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -787,6 +787,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, default: break; } + wdev->beacon_interval = 0; break; case NETDEV_DOWN: dev_hold(dev); diff --git a/net/wireless/core.h b/net/wireless/core.h index e3f7b1d995c..fd9135f9b5b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -433,6 +433,9 @@ int cfg80211_set_freq(struct cfg80211_registered_device *rdev, u16 cfg80211_calculate_bitrate(struct rate_info *rate); +int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, + u32 beacon_int); + #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) #else diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b5b050b62f2..9ef8e287d61 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1876,8 +1876,9 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) struct beacon_parameters *info); struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; struct beacon_parameters params; - int haveinfo = 0; + int haveinfo = 0, err; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL])) return -EINVAL; @@ -1886,6 +1887,8 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; + memset(¶ms, 0, sizeof(params)); + switch (info->genlhdr->cmd) { case NL80211_CMD_NEW_BEACON: /* these are required for NEW_BEACON */ @@ -1894,6 +1897,15 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) !info->attrs[NL80211_ATTR_BEACON_HEAD]) return -EINVAL; + params.interval = + nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); + params.dtim_period = + nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); + + err = cfg80211_validate_beacon_int(rdev, params.interval); + if (err) + return err; + call = rdev->ops->add_beacon; break; case NL80211_CMD_SET_BEACON: @@ -1907,20 +1919,6 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) if (!call) return -EOPNOTSUPP; - memset(¶ms, 0, sizeof(params)); - - if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { - params.interval = - nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); - haveinfo = 1; - } - - if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) { - params.dtim_period = - nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); - haveinfo = 1; - } - if (info->attrs[NL80211_ATTR_BEACON_HEAD]) { params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); params.head_len = @@ -1938,13 +1936,18 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) if (!haveinfo) return -EINVAL; - return call(&rdev->wiphy, dev, ¶ms); + err = call(&rdev->wiphy, dev, ¶ms); + if (!err && params.interval) + wdev->beacon_interval = params.interval; + return err; } static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; if (!rdev->ops->del_beacon) return -EOPNOTSUPP; @@ -1953,7 +1956,10 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; - return rdev->ops->del_beacon(&rdev->wiphy, dev); + err = rdev->ops->del_beacon(&rdev->wiphy, dev); + if (!err) + wdev->beacon_interval = 0; + return err; } static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { diff --git a/net/wireless/util.c b/net/wireless/util.c index 6a750bc6bcf..414c9f604df 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -896,3 +896,28 @@ u16 cfg80211_calculate_bitrate(struct rate_info *rate) /* do NOT round down here */ return (bitrate + 50000) / 100000; } + +int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, + u32 beacon_int) +{ + struct wireless_dev *wdev; + int res = 0; + + if (!beacon_int) + return -EINVAL; + + mutex_lock(&rdev->devlist_mtx); + + list_for_each_entry(wdev, &rdev->netdev_list, list) { + if (!wdev->beacon_interval) + continue; + if (wdev->beacon_interval != beacon_int) { + res = -EINVAL; + break; + } + } + + mutex_unlock(&rdev->devlist_mtx); + + return res; +} -- cgit v1.2.3-70-g09d2 From 85a9994a0a6cba1a6cc6af4bd3ebd85f778be0fe Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 12 May 2011 16:28:29 +0300 Subject: cfg80211/mac80211: avoid bounce back mac->cfg->mac on sched_scan_stopped When sched_scan_stopped was called by the driver, mac80211 calls cfg80211, which in turn was calling mac80211 back with a flag "driver_initiated". This flag was used so that mac80211 would do the necessary cleanup but would not call the driver. This was enough to prevent the bounce back between the driver and mac80211, but not between mac80211 and cfg80211. To fix this, we now do the cleanup in mac80211 before calling cfg80211. To help with locking issues, the workqueue was moved from cfg80211 to mac80211. Reported-by: Johannes Berg Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- include/net/cfg80211.h | 3 +-- net/mac80211/cfg.c | 5 ++--- net/mac80211/ieee80211_i.h | 5 +++-- net/mac80211/main.c | 3 +++ net/mac80211/scan.c | 33 +++++++++++++++++++++++++++------ net/wireless/core.c | 1 - net/wireless/core.h | 2 -- net/wireless/scan.c | 21 +++++++-------------- 8 files changed, 43 insertions(+), 30 deletions(-) (limited to 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a46adb7a91b..e1f1b41f7b1 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1515,8 +1515,7 @@ struct cfg80211_ops { int (*sched_scan_start)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_sched_scan_request *request); - int (*sched_scan_stop)(struct wiphy *wiphy, struct net_device *dev, - bool driver_initiated); + int (*sched_scan_stop)(struct wiphy *wiphy, struct net_device *dev); }; /* diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 303f33fcb84..2d1c1a5f3c5 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1372,15 +1372,14 @@ ieee80211_sched_scan_start(struct wiphy *wiphy, } static int -ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, - bool driver_initiated) +ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (!sdata->local->ops->sched_scan_stop) return -EOPNOTSUPP; - return ieee80211_request_sched_scan_stop(sdata, driver_initiated); + return ieee80211_request_sched_scan_stop(sdata); } static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6f55a789c09..82f90ff8bb1 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -849,6 +849,7 @@ struct ieee80211_local { bool sched_scanning; struct ieee80211_sched_scan_ies sched_scan_ies; + struct work_struct sched_scan_stopped_work; unsigned long leave_oper_channel_time; enum mac80211_scan_state next_scan_state; @@ -1160,8 +1161,8 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local, /* scheduled scan handling */ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req); -int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata, - bool driver_initiated); +int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); +void ieee80211_sched_scan_stopped_work(struct work_struct *work); /* off-channel helpers */ bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 30e6a682a04..7f89011fa22 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -652,6 +652,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, setup_timer(&local->dynamic_ps_timer, ieee80211_dynamic_ps_timer, (unsigned long) local); + INIT_WORK(&local->sched_scan_stopped_work, + ieee80211_sched_scan_stopped_work); + sta_info_init(local); for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index ea44a8e941e..d20046b5d8f 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -902,8 +902,7 @@ out: return ret; } -int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata, - bool driver_initiated) +int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; int ret = 0, i; @@ -919,11 +918,9 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata, for (i = 0; i < IEEE80211_NUM_BANDS; i++) kfree(local->sched_scan_ies.ie[i]); - if (!driver_initiated) - drv_sched_scan_stop(local, sdata); + drv_sched_scan_stop(local, sdata); local->sched_scanning = false; } - out: mutex_unlock(&sdata->local->mtx); @@ -940,12 +937,36 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw) } EXPORT_SYMBOL(ieee80211_sched_scan_results); +void ieee80211_sched_scan_stopped_work(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, + sched_scan_stopped_work); + int i; + + mutex_lock(&local->mtx); + + if (!local->sched_scanning) { + mutex_unlock(&local->mtx); + return; + } + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + kfree(local->sched_scan_ies.ie[i]); + + local->sched_scanning = false; + + mutex_unlock(&local->mtx); + + cfg80211_sched_scan_stopped(local->hw.wiphy); +} + void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); trace_api_sched_scan_stopped(local); - cfg80211_sched_scan_stopped(hw->wiphy); + ieee80211_queue_work(&local->hw, &local->sched_scan_stopped_work); } EXPORT_SYMBOL(ieee80211_sched_scan_stopped); diff --git a/net/wireless/core.c b/net/wireless/core.c index e2ab65d7c86..18b002f1686 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -371,7 +371,6 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) INIT_LIST_HEAD(&rdev->bss_list); INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); - INIT_WORK(&rdev->sched_scan_stopped_wk, __cfg80211_sched_scan_stopped); #ifdef CONFIG_CFG80211_WEXT rdev->wiphy.wext = &cfg80211_wext_handler; #endif diff --git a/net/wireless/core.h b/net/wireless/core.h index fd9135f9b5b..d4b8f4c0bbb 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -64,7 +64,6 @@ struct cfg80211_registered_device { unsigned long suspend_at; struct work_struct scan_done_wk; struct work_struct sched_scan_results_wk; - struct work_struct sched_scan_stopped_wk; #ifdef CONFIG_NL80211_TESTMODE struct genl_info *testmode_info; @@ -417,7 +416,6 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); void __cfg80211_sched_scan_results(struct work_struct *wk); int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, bool driver_initiated); -void __cfg80211_sched_scan_stopped(struct work_struct *wk); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); int cfg80211_change_iface(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype ntype, diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 65dfae3b9d4..73a441d237b 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -119,22 +119,14 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy) } EXPORT_SYMBOL(cfg80211_sched_scan_results); -void __cfg80211_sched_scan_stopped(struct work_struct *wk) +void cfg80211_sched_scan_stopped(struct wiphy *wiphy) { - struct cfg80211_registered_device *rdev; - - rdev = container_of(wk, struct cfg80211_registered_device, - sched_scan_stopped_wk); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); cfg80211_lock_rdev(rdev); __cfg80211_stop_sched_scan(rdev, true); cfg80211_unlock_rdev(rdev); } - -void cfg80211_sched_scan_stopped(struct wiphy *wiphy) -{ - queue_work(cfg80211_wq, &wiphy_to_dev(wiphy)->sched_scan_stopped_wk); -} EXPORT_SYMBOL(cfg80211_sched_scan_stopped); int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, @@ -150,10 +142,11 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, dev = rdev->sched_scan_req->dev; - err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev, - driver_initiated); - if (err) - return err; + if (!driver_initiated) { + err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); + if (err) + return err; + } nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); -- cgit v1.2.3-70-g09d2 From 7527a782e187d1214a5b3dc2897ce441033bb4ef Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 May 2011 10:58:57 +0200 Subject: cfg80211: advertise possible interface combinations Add the ability to advertise interface combinations in nl80211. This allows the driver to indicate what the combinations are that it supports. "Combinations" of just a single interface are implicit, as previously. Note that cfg80211 will enforce that the restrictions are met, but not for all drivers yet (once all drivers are updated, we can remove the flag and enforce for all). When no combinations are actually supported, an empty list will be exported so that userspace can know if the kernel exported this info or not (although it isn't clear to me what tools using the info should do if the kernel didn't export it). Since some interface types are purely virtual/software and don't fit the restrictions, those are exposed in a new list of pure SW types, not subject to restrictions. This mainly exists to handle AP-VLAN and monitor interfaces in mac80211. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 122 +++++++++++++++++++++++++++++++++++++++++++++++- include/net/cfg80211.h | 88 ++++++++++++++++++++++++++++++++++ net/mac80211/main.c | 16 +++++-- net/wireless/core.c | 69 +++++++++++++++++++++++++++ net/wireless/core.h | 11 +++++ net/wireless/nl80211.c | 105 +++++++++++++++++++++++++++++++++++------ net/wireless/util.c | 80 +++++++++++++++++++++++++++++++ 7 files changed, 472 insertions(+), 19 deletions(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 281a2bb6a6e..0ea497cb607 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -76,6 +76,39 @@ * below. */ +/** + * DOC: Virtual interface / concurrency capabilities + * + * Some devices are able to operate with virtual MACs, they can have + * more than one virtual interface. The capability handling for this + * is a bit complex though, as there may be a number of restrictions + * on the types of concurrency that are supported. + * + * To start with, each device supports the interface types listed in + * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the + * types there no concurrency is implied. + * + * Once concurrency is desired, more attributes must be observed: + * To start with, since some interface types are purely managed in + * software, like the AP-VLAN type in mac80211 for example, there's + * an additional list of these, they can be added at any time and + * are only restricted by some semantic restrictions (e.g. AP-VLAN + * cannot be added without a corresponding AP interface). This list + * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute. + * + * Further, the list of supported combinations is exported. This is + * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically, + * it exports a list of "groups", and at any point in time the + * interfaces that are currently active must fall into any one of + * the advertised groups. Within each group, there are restrictions + * on the number of interfaces of different types that are supported + * and also the number of different channels, along with potentially + * some other restrictions. See &enum nl80211_if_combination_attrs. + * + * All together, these attributes define the concurrency of virtual + * interfaces that a given device supports. + */ + /** * enum nl80211_commands - supported nl80211 commands * @@ -954,6 +987,14 @@ enum nl80211_commands { * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan * cycles, in msecs. * + * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported + * interface combinations. In each nested item, it contains attributes + * defined in &enum nl80211_if_combination_attrs. + * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like + * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that + * are managed in software: interfaces of these types aren't subject to + * any restrictions in their number or combinations. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1149,6 +1190,9 @@ enum nl80211_attrs { NL80211_ATTR_SCHED_SCAN_INTERVAL, + NL80211_ATTR_INTERFACE_COMBINATIONS, + NL80211_ATTR_SOFTWARE_IFTYPES, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1201,7 +1245,9 @@ enum nl80211_attrs { * @NL80211_IFTYPE_ADHOC: independent BSS member * @NL80211_IFTYPE_STATION: managed BSS member * @NL80211_IFTYPE_AP: access point - * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points + * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points; VLAN interfaces + * are a bit special in that they must always be tied to a pre-existing + * AP type interface. * @NL80211_IFTYPE_WDS: wireless distribution interface * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames * @NL80211_IFTYPE_MESH_POINT: mesh point @@ -2206,4 +2252,78 @@ enum nl80211_wowlan_triggers { MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1 }; +/** + * enum nl80211_iface_limit_attrs - limit attributes + * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) + * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that + * can be chosen from this set of interface types (u32) + * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a + * flag attribute for each interface type in this set + * @NUM_NL80211_IFACE_LIMIT: number of attributes + * @MAX_NL80211_IFACE_LIMIT: highest attribute number + */ +enum nl80211_iface_limit_attrs { + NL80211_IFACE_LIMIT_UNSPEC, + NL80211_IFACE_LIMIT_MAX, + NL80211_IFACE_LIMIT_TYPES, + + /* keep last */ + NUM_NL80211_IFACE_LIMIT, + MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1 +}; + +/** + * enum nl80211_if_combination_attrs -- interface combination attributes + * + * @NL80211_IFACE_COMB_UNSPEC: (reserved) + * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits + * for given interface types, see &enum nl80211_iface_limit_attrs. + * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of + * interfaces that can be created in this group. This number doesn't + * apply to interfaces purely managed in software, which are listed + * in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE. + * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that + * beacon intervals within this group must be all the same even for + * infrastructure and AP/GO combinations, i.e. the GO(s) must adopt + * the infrastructure network's beacon interval. + * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many + * different channels may be used within this group. + * @NUM_NL80211_IFACE_COMB: number of attributes + * @MAX_NL80211_IFACE_COMB: highest attribute number + * + * Examples: + * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2 + * => allows an AP and a STA that must match BIs + * + * numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8 + * => allows 8 of AP/GO + * + * numbers = [ #{STA} <= 2 ], channels = 2, max = 2 + * => allows two STAs on different channels + * + * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4 + * => allows a STA plus three P2P interfaces + * + * The list of these four possiblities could completely be contained + * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate + * that any of these groups must match. + * + * "Combinations" of just a single interface will not be listed here, + * a single interface of any valid interface type is assumed to always + * be possible by itself. This means that implicitly, for each valid + * interface type, the following group always exists: + * numbers = [ #{} <= 1 ], channels = 1, max = 1 + */ +enum nl80211_if_combination_attrs { + NL80211_IFACE_COMB_UNSPEC, + NL80211_IFACE_COMB_LIMITS, + NL80211_IFACE_COMB_MAXNUM, + NL80211_IFACE_COMB_STA_AP_BI_MATCH, + NL80211_IFACE_COMB_NUM_CHANNELS, + + /* keep last */ + NUM_NL80211_IFACE_COMB, + MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1 +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e1f1b41f7b1..04afcfb9eaf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1547,6 +1547,10 @@ struct cfg80211_ops { * hints read the documenation for regulatory_hint_found_beacon() * @WIPHY_FLAG_NETNS_OK: if not set, do not allow changing the netns of this * wiphy at all + * @WIPHY_FLAG_ENFORCE_COMBINATIONS: Set this flag to enforce interface + * combinations for this device. This flag is used for backward + * compatibility only until all drivers advertise combinations and + * they will always be enforced. * @WIPHY_FLAG_PS_ON_BY_DEFAULT: if set to true, powersave will be enabled * by default -- this flag will be set depending on the kernel's default * on wiphy_new(), but can be changed by the driver if it has a good @@ -1574,6 +1578,81 @@ enum wiphy_flags { WIPHY_FLAG_IBSS_RSN = BIT(8), WIPHY_FLAG_MESH_AUTH = BIT(10), WIPHY_FLAG_SUPPORTS_SCHED_SCAN = BIT(11), + WIPHY_FLAG_ENFORCE_COMBINATIONS = BIT(12), +}; + +/** + * struct ieee80211_iface_limit - limit on certain interface types + * @max: maximum number of interfaces of these types + * @types: interface types (bits) + */ +struct ieee80211_iface_limit { + u16 max; + u16 types; +}; + +/** + * struct ieee80211_iface_combination - possible interface combination + * @limits: limits for the given interface types + * @n_limits: number of limitations + * @num_different_channels: can use up to this many different channels + * @max_interfaces: maximum number of interfaces in total allowed in this + * group + * @beacon_int_infra_match: In this combination, the beacon intervals + * between infrastructure and AP types must match. This is required + * only in special cases. + * + * These examples can be expressed as follows: + * + * Allow #STA <= 1, #AP <= 1, matching BI, channels = 1, 2 total: + * + * struct ieee80211_iface_limit limits1[] = { + * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, + * { .max = 1, .types = BIT(NL80211_IFTYPE_AP}, }, + * }; + * struct ieee80211_iface_combination combination1 = { + * .limits = limits1, + * .n_limits = ARRAY_SIZE(limits1), + * .max_interfaces = 2, + * .beacon_int_infra_match = true, + * }; + * + * + * Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total: + * + * struct ieee80211_iface_limit limits2[] = { + * { .max = 8, .types = BIT(NL80211_IFTYPE_AP) | + * BIT(NL80211_IFTYPE_P2P_GO), }, + * }; + * struct ieee80211_iface_combination combination2 = { + * .limits = limits2, + * .n_limits = ARRAY_SIZE(limits2), + * .max_interfaces = 8, + * .num_different_channels = 1, + * }; + * + * + * Allow #STA <= 1, #{P2P-client,P2P-GO} <= 3 on two channels, 4 total. + * This allows for an infrastructure connection and three P2P connections. + * + * struct ieee80211_iface_limit limits3[] = { + * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, + * { .max = 3, .types = BIT(NL80211_IFTYPE_P2P_GO) | + * BIT(NL80211_IFTYPE_P2P_CLIENT), }, + * }; + * struct ieee80211_iface_combination combination3 = { + * .limits = limits3, + * .n_limits = ARRAY_SIZE(limits3), + * .max_interfaces = 4, + * .num_different_channels = 2, + * }; + */ +struct ieee80211_iface_combination { + const struct ieee80211_iface_limit *limits; + u32 num_different_channels; + u16 max_interfaces; + u8 n_limits; + bool beacon_int_infra_match; }; struct mac_address { @@ -1653,6 +1732,11 @@ struct wiphy_wowlan_support { * @priv: driver private data (sized according to wiphy_new() parameter) * @interface_modes: bitmask of interfaces types valid for this wiphy, * must be set by driver + * @iface_combinations: Valid interface combinations array, should not + * list single interface types. + * @n_iface_combinations: number of entries in @iface_combinations array. + * @software_iftypes: bitmask of software interface types, these are not + * subject to any restrictions since they are purely managed in SW. * @flags: wiphy flags, see &enum wiphy_flags * @bss_priv_size: each BSS struct has private data allocated with it, * this variable determines its size @@ -1697,6 +1781,10 @@ struct wiphy { const struct ieee80211_txrx_stypes *mgmt_stypes; + const struct ieee80211_iface_combination *iface_combinations; + int n_iface_combinations; + u16 software_iftypes; + u16 n_addresses; /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */ diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 7f89011fa22..79a2281678b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -685,7 +685,7 @@ EXPORT_SYMBOL(ieee80211_alloc_hw); int ieee80211_register_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); - int result; + int result, i; enum ieee80211_band band; int channels, max_bitrates; bool supp_ht; @@ -743,11 +743,19 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) return -ENOMEM; /* if low-level driver supports AP, we also support VLAN */ - if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) - local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); + if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); + hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN); + } /* mac80211 always supports monitor */ - local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); + hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); + + /* mac80211 doesn't support more than 1 channel */ + for (i = 0; i < hw->wiphy->n_iface_combinations; i++) + if (hw->wiphy->iface_combinations[i].num_different_channels > 1) + return -EINVAL; #ifndef CONFIG_MAC80211_MESH /* mesh depends on Kconfig, but drivers should set it if they want */ diff --git a/net/wireless/core.c b/net/wireless/core.c index 18b002f1686..c22ef3492ee 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -416,6 +416,67 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) } EXPORT_SYMBOL(wiphy_new); +static int wiphy_verify_combinations(struct wiphy *wiphy) +{ + const struct ieee80211_iface_combination *c; + int i, j; + + /* If we have combinations enforce them */ + if (wiphy->n_iface_combinations) + wiphy->flags |= WIPHY_FLAG_ENFORCE_COMBINATIONS; + + for (i = 0; i < wiphy->n_iface_combinations; i++) { + u32 cnt = 0; + u16 all_iftypes = 0; + + c = &wiphy->iface_combinations[i]; + + /* Combinations with just one interface aren't real */ + if (WARN_ON(c->max_interfaces < 2)) + return -EINVAL; + + /* Need at least one channel */ + if (WARN_ON(!c->num_different_channels)) + return -EINVAL; + + if (WARN_ON(!c->n_limits)) + return -EINVAL; + + for (j = 0; j < c->n_limits; j++) { + u16 types = c->limits[j].types; + + /* + * interface types shouldn't overlap, this is + * used in cfg80211_can_change_interface() + */ + if (WARN_ON(types & all_iftypes)) + return -EINVAL; + all_iftypes |= types; + + if (WARN_ON(!c->limits[j].max)) + return -EINVAL; + + /* Shouldn't list software iftypes in combinations! */ + if (WARN_ON(wiphy->software_iftypes & types)) + return -EINVAL; + + cnt += c->limits[j].max; + /* + * Don't advertise an unsupported type + * in a combination. + */ + if (WARN_ON((wiphy->interface_modes & types) != types)) + return -EINVAL; + } + + /* You can't even choose that many! */ + if (WARN_ON(cnt < c->max_interfaces)) + return -EINVAL; + } + + return 0; +} + int wiphy_register(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); @@ -444,6 +505,10 @@ int wiphy_register(struct wiphy *wiphy) if (WARN_ON(ifmodes != wiphy->interface_modes)) wiphy->interface_modes = ifmodes; + res = wiphy_verify_combinations(wiphy); + if (res) + return res; + /* sanity check supported bands/channels */ for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = wiphy->bands[band]; @@ -698,6 +763,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, struct net_device *dev = ndev; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev; + int ret; if (!wdev) return NOTIFY_DONE; @@ -893,6 +959,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, return notifier_from_errno(-EOPNOTSUPP); if (rfkill_blocked(rdev->rfkill)) return notifier_from_errno(-ERFKILL); + ret = cfg80211_can_add_interface(rdev, wdev->iftype); + if (ret) + return notifier_from_errno(ret); break; } diff --git a/net/wireless/core.h b/net/wireless/core.h index d4b8f4c0bbb..bf0fb40e3c8 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -422,6 +422,17 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, u32 *flags, struct vif_params *params); void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); +int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + enum nl80211_iftype iftype); + +static inline int +cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, + enum nl80211_iftype iftype) +{ + return cfg80211_can_change_interface(rdev, NULL, iftype); +} + struct ieee80211_channel * rdev_freq_to_chan(struct cfg80211_registered_device *rdev, int freq, enum nl80211_channel_type channel_type); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9ef8e287d61..beac296b1fd 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -564,6 +564,88 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) return 0; } +static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes) +{ + struct nlattr *nl_modes = nla_nest_start(msg, attr); + int i; + + if (!nl_modes) + goto nla_put_failure; + + i = 0; + while (ifmodes) { + if (ifmodes & 1) + NLA_PUT_FLAG(msg, i); + ifmodes >>= 1; + i++; + } + + nla_nest_end(msg, nl_modes); + return 0; + +nla_put_failure: + return -ENOBUFS; +} + +static int nl80211_put_iface_combinations(struct wiphy *wiphy, + struct sk_buff *msg) +{ + struct nlattr *nl_combis; + int i, j; + + nl_combis = nla_nest_start(msg, + NL80211_ATTR_INTERFACE_COMBINATIONS); + if (!nl_combis) + goto nla_put_failure; + + for (i = 0; i < wiphy->n_iface_combinations; i++) { + const struct ieee80211_iface_combination *c; + struct nlattr *nl_combi, *nl_limits; + + c = &wiphy->iface_combinations[i]; + + nl_combi = nla_nest_start(msg, i + 1); + if (!nl_combi) + goto nla_put_failure; + + nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS); + if (!nl_limits) + goto nla_put_failure; + + for (j = 0; j < c->n_limits; j++) { + struct nlattr *nl_limit; + + nl_limit = nla_nest_start(msg, j + 1); + if (!nl_limit) + goto nla_put_failure; + NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX, + c->limits[j].max); + if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES, + c->limits[j].types)) + goto nla_put_failure; + nla_nest_end(msg, nl_limit); + } + + nla_nest_end(msg, nl_limits); + + if (c->beacon_int_infra_match) + NLA_PUT_FLAG(msg, + NL80211_IFACE_COMB_STA_AP_BI_MATCH); + NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS, + c->num_different_channels); + NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM, + c->max_interfaces); + + nla_nest_end(msg, nl_combi); + } + + nla_nest_end(msg, nl_combis); + + return 0; +nla_put_failure: + return -ENOBUFS; +} + static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct cfg80211_registered_device *dev) { @@ -571,13 +653,11 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct nlattr *nl_bands, *nl_band; struct nlattr *nl_freqs, *nl_freq; struct nlattr *nl_rates, *nl_rate; - struct nlattr *nl_modes; struct nlattr *nl_cmds; enum ieee80211_band band; struct ieee80211_channel *chan; struct ieee80211_rate *rate; int i; - u16 ifmodes = dev->wiphy.interface_modes; const struct ieee80211_txrx_stypes *mgmt_stypes = dev->wiphy.mgmt_stypes; @@ -637,20 +717,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, } } - nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); - if (!nl_modes) + if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, + dev->wiphy.interface_modes)) goto nla_put_failure; - i = 0; - while (ifmodes) { - if (ifmodes & 1) - NLA_PUT_FLAG(msg, i); - ifmodes >>= 1; - i++; - } - - nla_nest_end(msg, nl_modes); - nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); if (!nl_bands) goto nla_put_failure; @@ -865,6 +935,13 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, nla_nest_end(msg, nl_wowlan); } + if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, + dev->wiphy.software_iftypes)) + goto nla_put_failure; + + if (nl80211_put_iface_combinations(&dev->wiphy, msg)) + goto nla_put_failure; + return genlmsg_end(msg, hdr); nla_put_failure: diff --git a/net/wireless/util.c b/net/wireless/util.c index 414c9f604df..95e4e254da0 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -803,6 +803,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, return -EBUSY; if (ntype != otype) { + err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, + ntype); + if (err) + return err; + dev->ieee80211_ptr->use_4addr = false; dev->ieee80211_ptr->mesh_id_up_len = 0; @@ -921,3 +926,78 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, return res; } + +int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + enum nl80211_iftype iftype) +{ + struct wireless_dev *wdev_iter; + int num[NUM_NL80211_IFTYPES]; + int total = 1; + int i, j; + + ASSERT_RTNL(); + + /* Always allow software iftypes */ + if (rdev->wiphy.software_iftypes & BIT(iftype)) + return 0; + + /* + * Drivers will gradually all set this flag, until all + * have it we only enforce for those that set it. + */ + if (!(rdev->wiphy.flags & WIPHY_FLAG_ENFORCE_COMBINATIONS)) + return 0; + + memset(num, 0, sizeof(num)); + + num[iftype] = 1; + + mutex_lock(&rdev->devlist_mtx); + list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { + if (wdev_iter == wdev) + continue; + if (!netif_running(wdev_iter->netdev)) + continue; + + if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) + continue; + + num[wdev_iter->iftype]++; + total++; + } + mutex_unlock(&rdev->devlist_mtx); + + for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { + const struct ieee80211_iface_combination *c; + struct ieee80211_iface_limit *limits; + + c = &rdev->wiphy.iface_combinations[i]; + + limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, + GFP_KERNEL); + if (!limits) + return -ENOMEM; + if (total > c->max_interfaces) + goto cont; + + for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { + if (rdev->wiphy.software_iftypes & BIT(iftype)) + continue; + for (j = 0; j < c->n_limits; j++) { + if (!(limits[j].types & iftype)) + continue; + if (limits[j].max < num[iftype]) + goto cont; + limits[j].max -= num[iftype]; + } + } + /* yay, it fits */ + kfree(limits); + return 0; + cont: + kfree(limits); + } + + return -EBUSY; +} -- cgit v1.2.3-70-g09d2 From 57cf8043a64b56a10b9f194572548a3dfb62e596 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Fri, 13 May 2011 10:45:43 -0700 Subject: nl80211: Move peer link state definition to nl80211 These definitions need to be exposed now that we can set the peer link states via NL80211_ATTR_STA_PLINK_STATE. They were already being (opaquely) reported by NL80211_STA_INFO_PLINK_STATE. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- include/linux/nl80211.h | 41 +++++++++++++++++++++++++-- include/net/cfg80211.h | 27 ------------------ net/mac80211/cfg.c | 6 ++-- net/mac80211/mesh_plink.c | 72 +++++++++++++++++++++++------------------------ net/mac80211/rx.c | 2 +- net/mac80211/sta_info.c | 2 +- net/mac80211/sta_info.h | 6 ++-- net/wireless/nl80211.c | 2 +- 8 files changed, 83 insertions(+), 75 deletions(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 0ea497cb607..fc3a94821b1 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -973,9 +973,10 @@ enum nl80211_commands { * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver * allows auth frames in a mesh to be passed to userspace for processing via * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. - * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link. Used when - * userspace is driving the peer link management state machine. - * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled. + * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as + * defined in &enum nl80211_plink_state. Used when userspace is + * driving the peer link management state machine. + * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled. * * @NL80211_ATTR_WOWLAN_SUPPORTED: indicates, as part of the wiphy capabilities, * the supported WoWLAN triggers @@ -1396,6 +1397,7 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_LLID: the station's mesh LLID * @NL80211_STA_INFO_PLID: the station's mesh PLID * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station + * (see %enum nl80211_plink_state) * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested * attribute, like NL80211_STA_INFO_TX_BITRATE. * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute @@ -2326,4 +2328,37 @@ enum nl80211_if_combination_attrs { MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1 }; + +/** + * enum nl80211_plink_state - state of a mesh peer link finite state machine + * + * @NL80211_PLINK_LISTEN: initial state, considered the implicit + * state of non existant mesh peer links + * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to + * this mesh peer + * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received + * from this mesh peer + * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been + * received from this mesh peer + * @NL80211_PLINK_ESTAB: mesh peer link is established + * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled + * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh + * plink are discarded + * @NUM_NL80211_PLINK_STATES: number of peer link states + * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states + */ +enum nl80211_plink_state { + NL80211_PLINK_LISTEN, + NL80211_PLINK_OPN_SNT, + NL80211_PLINK_OPN_RCVD, + NL80211_PLINK_CNF_RCVD, + NL80211_PLINK_ESTAB, + NL80211_PLINK_HOLDING, + NL80211_PLINK_BLOCKED, + + /* keep last */ + NUM_NL80211_PLINK_STATES, + MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 04afcfb9eaf..0a2d795d35d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -371,33 +371,6 @@ enum plink_actions { PLINK_ACTION_BLOCK, }; -/** - * enum plink_states - state of a mesh peer link finite state machine - * - * @PLINK_LISTEN: initial state, considered the implicit state of non - * existant mesh peer links - * @PLINK_OPN_SNT: mesh plink open frame has been sent to this mesh - * peer @PLINK_OPN_RCVD: mesh plink open frame has been received from - * this mesh peer - * @PLINK_CNF_RCVD: mesh plink confirm frame has been received from - * this mesh peer - * @PLINK_ESTAB: mesh peer link is established - * @PLINK_HOLDING: mesh peer link is being closed or cancelled - * @PLINK_BLOCKED: all frames transmitted from this mesh plink are - * discarded - * @PLINK_INVALID: reserved - */ -enum plink_state { - PLINK_LISTEN, - PLINK_OPN_SNT, - PLINK_OPN_RCVD, - PLINK_CNF_RCVD, - PLINK_ESTAB, - PLINK_HOLDING, - PLINK_BLOCKED, - PLINK_INVALID, -}; - /** * struct station_parameters - station parameters * diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 6ecd5862735..be70c70d3f5 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -730,9 +730,9 @@ static void sta_apply_parameters(struct ieee80211_local *local, #ifdef CONFIG_MAC80211_MESH if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) switch (params->plink_state) { - case PLINK_LISTEN: - case PLINK_ESTAB: - case PLINK_BLOCKED: + case NL80211_PLINK_LISTEN: + case NL80211_PLINK_ESTAB: + case NL80211_PLINK_BLOCKED: sta->plink_state = params->plink_state; break; default: diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 2c37bee3095..f4adc091788 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -83,7 +83,7 @@ void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) */ static inline void mesh_plink_fsm_restart(struct sta_info *sta) { - sta->plink_state = PLINK_LISTEN; + sta->plink_state = NL80211_PLINK_LISTEN; sta->llid = sta->plid = sta->reason = 0; sta->plink_retries = 0; } @@ -126,11 +126,11 @@ static bool __mesh_plink_deactivate(struct sta_info *sta) struct ieee80211_sub_if_data *sdata = sta->sdata; bool deactivated = false; - if (sta->plink_state == PLINK_ESTAB) { + if (sta->plink_state == NL80211_PLINK_ESTAB) { mesh_plink_dec_estab_count(sdata); deactivated = true; } - sta->plink_state = PLINK_BLOCKED; + sta->plink_state = NL80211_PLINK_BLOCKED; mesh_path_flush_by_nexthop(sta); return deactivated; @@ -268,7 +268,7 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, sta->last_rx = jiffies; sta->sta.supp_rates[local->hw.conf.channel->band] = rates; if (mesh_peer_accepts_plinks(elems) && - sta->plink_state == PLINK_LISTEN && + sta->plink_state == NL80211_PLINK_LISTEN && sdata->u.mesh.accepting_plinks && sdata->u.mesh.mshcfg.auto_open_plinks) mesh_plink_open(sta); @@ -308,8 +308,8 @@ static void mesh_plink_timer(unsigned long data) sdata = sta->sdata; switch (sta->plink_state) { - case PLINK_OPN_RCVD: - case PLINK_OPN_SNT: + case NL80211_PLINK_OPN_RCVD: + case NL80211_PLINK_OPN_SNT: /* retry timer */ if (sta->plink_retries < dot11MeshMaxRetries(sdata)) { u32 rand; @@ -328,17 +328,17 @@ static void mesh_plink_timer(unsigned long data) } reason = cpu_to_le16(MESH_MAX_RETRIES); /* fall through on else */ - case PLINK_CNF_RCVD: + case NL80211_PLINK_CNF_RCVD: /* confirm timer */ if (!reason) reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT); - sta->plink_state = PLINK_HOLDING; + sta->plink_state = NL80211_PLINK_HOLDING; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid, reason); break; - case PLINK_HOLDING: + case NL80211_PLINK_HOLDING: /* holding timer */ del_timer(&sta->plink_timer); mesh_plink_fsm_restart(sta); @@ -386,11 +386,11 @@ int mesh_plink_open(struct sta_info *sta) spin_lock_bh(&sta->lock); get_random_bytes(&llid, 2); sta->llid = llid; - if (sta->plink_state != PLINK_LISTEN) { + if (sta->plink_state != NL80211_PLINK_LISTEN) { spin_unlock_bh(&sta->lock); return -EBUSY; } - sta->plink_state = PLINK_OPN_SNT; + sta->plink_state = NL80211_PLINK_OPN_SNT; mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); spin_unlock_bh(&sta->lock); mpl_dbg("Mesh plink: starting establishment with %pM\n", @@ -407,7 +407,7 @@ void mesh_plink_block(struct sta_info *sta) spin_lock_bh(&sta->lock); deactivated = __mesh_plink_deactivate(sta); - sta->plink_state = PLINK_BLOCKED; + sta->plink_state = NL80211_PLINK_BLOCKED; spin_unlock_bh(&sta->lock); if (deactivated) @@ -430,13 +430,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m __le16 plid, llid, reason; #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG static const char *mplstates[] = { - [PLINK_LISTEN] = "LISTEN", - [PLINK_OPN_SNT] = "OPN-SNT", - [PLINK_OPN_RCVD] = "OPN-RCVD", - [PLINK_CNF_RCVD] = "CNF_RCVD", - [PLINK_ESTAB] = "ESTAB", - [PLINK_HOLDING] = "HOLDING", - [PLINK_BLOCKED] = "BLOCKED" + [NL80211_PLINK_LISTEN] = "LISTEN", + [NL80211_PLINK_OPN_SNT] = "OPN-SNT", + [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD", + [NL80211_PLINK_CNF_RCVD] = "CNF_RCVD", + [NL80211_PLINK_ESTAB] = "ESTAB", + [NL80211_PLINK_HOLDING] = "HOLDING", + [NL80211_PLINK_BLOCKED] = "BLOCKED" }; #endif @@ -502,7 +502,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m return; } - if (sta && sta->plink_state == PLINK_BLOCKED) { + if (sta && sta->plink_state == NL80211_PLINK_BLOCKED) { rcu_read_unlock(); return; } @@ -572,7 +572,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m event = CNF_ACPT; break; case PLINK_CLOSE: - if (sta->plink_state == PLINK_ESTAB) + if (sta->plink_state == NL80211_PLINK_ESTAB) /* Do not check for llid or plid. This does not * follow the standard but since multiple plinks * per sta are not supported, it is necessary in @@ -607,14 +607,14 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m reason = 0; switch (sta->plink_state) { /* spin_unlock as soon as state is updated at each case */ - case PLINK_LISTEN: + case NL80211_PLINK_LISTEN: switch (event) { case CLS_ACPT: mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->lock); break; case OPN_ACPT: - sta->plink_state = PLINK_OPN_RCVD; + sta->plink_state = NL80211_PLINK_OPN_RCVD; sta->plid = plid; get_random_bytes(&llid, 2); sta->llid = llid; @@ -631,7 +631,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } break; - case PLINK_OPN_SNT: + case NL80211_PLINK_OPN_SNT: switch (event) { case OPN_RJCT: case CNF_RJCT: @@ -640,7 +640,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m if (!reason) reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; - sta->plink_state = PLINK_HOLDING; + sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; @@ -652,7 +652,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m break; case OPN_ACPT: /* retry timer is left untouched */ - sta->plink_state = PLINK_OPN_RCVD; + sta->plink_state = NL80211_PLINK_OPN_RCVD; sta->plid = plid; llid = sta->llid; spin_unlock_bh(&sta->lock); @@ -660,7 +660,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m plid, 0); break; case CNF_ACPT: - sta->plink_state = PLINK_CNF_RCVD; + sta->plink_state = NL80211_PLINK_CNF_RCVD; if (!mod_plink_timer(sta, dot11MeshConfirmTimeout(sdata))) sta->ignore_plink_timer = true; @@ -673,7 +673,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } break; - case PLINK_OPN_RCVD: + case NL80211_PLINK_OPN_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: @@ -682,7 +682,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m if (!reason) reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; - sta->plink_state = PLINK_HOLDING; + sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; @@ -700,7 +700,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m break; case CNF_ACPT: del_timer(&sta->plink_timer); - sta->plink_state = PLINK_ESTAB; + sta->plink_state = NL80211_PLINK_ESTAB; spin_unlock_bh(&sta->lock); mesh_plink_inc_estab_count(sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); @@ -713,7 +713,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } break; - case PLINK_CNF_RCVD: + case NL80211_PLINK_CNF_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: @@ -722,7 +722,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m if (!reason) reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; - sta->plink_state = PLINK_HOLDING; + sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; @@ -734,7 +734,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m break; case OPN_ACPT: del_timer(&sta->plink_timer); - sta->plink_state = PLINK_ESTAB; + sta->plink_state = NL80211_PLINK_ESTAB; spin_unlock_bh(&sta->lock); mesh_plink_inc_estab_count(sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); @@ -749,13 +749,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } break; - case PLINK_ESTAB: + case NL80211_PLINK_ESTAB: switch (event) { case CLS_ACPT: reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; deactivated = __mesh_plink_deactivate(sta); - sta->plink_state = PLINK_HOLDING; + sta->plink_state = NL80211_PLINK_HOLDING; llid = sta->llid; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->lock); @@ -775,7 +775,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m break; } break; - case PLINK_HOLDING: + case NL80211_PLINK_HOLDING: switch (event) { case CLS_ACPT: if (del_timer(&sta->plink_timer)) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 1b9413fb383..3a9515cb7ce 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -490,7 +490,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) * establisment frame, beacon or probe, drop the frame. */ - if (!rx->sta || sta_plink_state(rx->sta) != PLINK_ESTAB) { + if (!rx->sta || sta_plink_state(rx->sta) != NL80211_PLINK_ESTAB) { struct ieee80211_mgmt *mgmt; if (!ieee80211_is_mgmt(hdr->frame_control)) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 82ab6b4643f..4a15f960356 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -277,7 +277,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ #ifdef CONFIG_MAC80211_MESH - sta->plink_state = PLINK_LISTEN; + sta->plink_state = NL80211_PLINK_LISTEN; init_timer(&sta->plink_timer); #endif diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index d6b566076f0..c6ae8718bd5 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -318,7 +318,7 @@ struct sta_info { u8 plink_retries; bool ignore_plink_timer; bool plink_timer_was_running; - enum plink_state plink_state; + enum nl80211_plink_state plink_state; u32 plink_timeout; struct timer_list plink_timer; #endif @@ -336,12 +336,12 @@ struct sta_info { struct ieee80211_sta sta; }; -static inline enum plink_state sta_plink_state(struct sta_info *sta) +static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta) { #ifdef CONFIG_MAC80211_MESH return sta->plink_state; #endif - return PLINK_LISTEN; + return NL80211_PLINK_LISTEN; } static inline void set_sta_flags(struct sta_info *sta, const u32 flags) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index beac296b1fd..2222ce08ee9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2335,7 +2335,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); params.listen_interval = -1; - params.plink_state = PLINK_INVALID; + params.plink_state = -1; if (info->attrs[NL80211_ATTR_STA_AID]) return -EINVAL; -- cgit v1.2.3-70-g09d2 From 8b3becadc82de3b87a5c196239db3fef6caa9c82 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Fri, 13 May 2011 11:22:31 -0700 Subject: cfg80211: make stripping of 802.11 header optional from AMSDU Currently the devices that have already stripped IEEE 802.11 header from the AMSDU SKB can not use ieee80211_amsdu_to_8023s routine. This patch enhances ieee80211_amsdu_to_8023s() API by changing mandatory removing of IEEE 802.11 header from AMSDU to optional. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/iwmc3200wifi/rx.c | 3 ++- include/net/cfg80211.h | 4 +++- net/mac80211/rx.c | 2 +- net/wireless/util.c | 21 +++++++++++++-------- 4 files changed, 19 insertions(+), 11 deletions(-) (limited to 'net/wireless') diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c index 9a57cf6a488..5665a1a9b99 100644 --- a/drivers/net/wireless/iwmc3200wifi/rx.c +++ b/drivers/net/wireless/iwmc3200wifi/rx.c @@ -1576,7 +1576,8 @@ static void iwm_rx_process_amsdu(struct iwm_priv *iwm, struct sk_buff *skb) IWM_HEXDUMP(iwm, DBG, RX, "A-MSDU: ", skb->data, skb->len); __skb_queue_head_init(&list); - ieee80211_amsdu_to_8023s(skb, &list, ndev->dev_addr, wdev->iftype, 0); + ieee80211_amsdu_to_8023s(skb, &list, ndev->dev_addr, wdev->iftype, 0, + true); while ((frame = __skb_dequeue(&list))) { ndev->stats.rx_packets++; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0a2d795d35d..bfd6557946b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2237,10 +2237,12 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, * @addr: The device MAC address. * @iftype: The device interface type. * @extra_headroom: The hardware extra headroom for SKBs in the @list. + * @has_80211_header: Set it true if SKB is with IEEE 802.11 header. */ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, const u8 *addr, enum nl80211_iftype iftype, - const unsigned int extra_headroom); + const unsigned int extra_headroom, + bool has_80211_header); /** * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 3a9515cb7ce..78c72a41d86 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1783,7 +1783,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr, rx->sdata->vif.type, - rx->local->hw.extra_tx_headroom); + rx->local->hw.extra_tx_headroom, true); while (!skb_queue_empty(&frame_list)) { rx->skb = __skb_dequeue(&frame_list); diff --git a/net/wireless/util.c b/net/wireless/util.c index 95e4e254da0..f0536d44d43 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -544,7 +544,8 @@ EXPORT_SYMBOL(ieee80211_data_from_8023); void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, const u8 *addr, enum nl80211_iftype iftype, - const unsigned int extra_headroom) + const unsigned int extra_headroom, + bool has_80211_header) { struct sk_buff *frame = NULL; u16 ethertype; @@ -553,14 +554,18 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, int remaining, err; u8 dst[ETH_ALEN], src[ETH_ALEN]; - err = ieee80211_data_to_8023(skb, addr, iftype); - if (err) - goto out; + if (has_80211_header) { + err = ieee80211_data_to_8023(skb, addr, iftype); + if (err) + goto out; - /* skip the wrapping header */ - eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); - if (!eth) - goto out; + /* skip the wrapping header */ + eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); + if (!eth) + goto out; + } else { + eth = (struct ethhdr *) skb->data; + } while (skb != frame) { u8 padding; -- cgit v1.2.3-70-g09d2